/*
Theme Name: Vallem
Theme URI: https://vallem.com
Author: Ettic
Author URI: https://ettic.nl
Description: Block theme for vallem.com.
Version: 0.1.127
Requires at least: 7.0
Tested up to: 7.0.4
Requires PHP: 8.2
License: GPL-2.0-or-later
License URI: https://www.gnu.org/licenses/gpl-2.0.html
Text Domain: vallem
*/

/* Design tokens live in theme.json. Only add CSS here for what theme.json cannot express. */

/* Inter v4 carries an opsz axis (14-32). Browsers default to auto, this is belt and braces. */
body {
	font-optical-sizing: auto;
}

/* ---- The heading ladder --------------------------------------------------
 *
 * theme.json can only style a heading by level, and the patterns pick by size.
 * section-lead-capture puts an h2 at 20px, section-pillars puts an h3 at 36px,
 * so level and size disagree across the theme and a level rule cannot say what
 * display type looks like. These can, because they key on the size preset: a
 * heading is treated for the size it is set at, whatever its level.
 *
 * Weight tops out at 500 and never reaches 600. That is the services bento's
 * own rule, `h1, h2, h3 { font-weight: 500 }` in the prototype the Stripe port
 * was signed off from, and it is what the four heroes already do by hand. The
 * 600 headings inherited until now arrived with the first typography commit,
 * 0f7c7ca on 2026-08-14, and was a default nobody revisited rather than a
 * decision. Nol on 2026-09-07: 600 is too much, cap it at 500.
 *
 * Tracking at 30px and below is Inter's Dynamic Metrics formula, the same
 * numbers theme.json carries per level. Above that the formula flattens at
 * -0.0223em because Inter expects the opsz axis to take over, and v4's axis
 * stops at 32. Nothing compensates between 36 and 72px, which is why every
 * display heading in the theme was hand-tightened. The display steps here are
 * those hand values made into a ladder: -0.028em at 48, which is the bento's
 * section title, and -0.035em at 72, which is the homepage hero.
 *
 * :is() weighs its heaviest branch, a type selector, so each rule is (0,1,1).
 * That clears theme.json's `:root :where(h2)` at (0,1,0) with no !important.
 * An inline style still beats it, which is why the patterns no longer write
 * weight, tracking or leading of their own onto a heading.
 * ------------------------------------------------------------------------- */

:is(h1, h2, h3, h4, h5, h6).has-display-hero-font-size {
	font-weight: 500;
	letter-spacing: -0.035em;
	line-height: 1.05;
}

:is(h1, h2, h3, h4, h5, h6).has-display-large-font-size {
	font-weight: 500;
	letter-spacing: -0.032em;
	line-height: 1.05;
}

:is(h1, h2, h3, h4, h5, h6).has-display-medium-font-size {
	font-weight: 500;
	letter-spacing: -0.028em;
	line-height: 1.14;
}

:is(h1, h2, h3, h4, h5, h6).has-display-small-font-size {
	font-weight: 500;
	letter-spacing: -0.025em;
	line-height: 1.15;
}

:is(h1, h2, h3, h4, h5, h6).has-xxx-large-font-size {
	font-weight: 500;
	letter-spacing: -0.0213em;
	line-height: 1.2;
}

:is(h1, h2, h3, h4, h5, h6).has-xx-large-font-size {
	font-weight: 500;
	letter-spacing: -0.0195em;
	line-height: 1.25;
}

:is(h1, h2, h3, h4, h5, h6).has-x-large-font-size {
	font-weight: 500;
	letter-spacing: -0.0167em;
	line-height: 1.3;
}

:is(h1, h2, h3, h4, h5, h6).has-large-font-size {
	font-weight: 500;
	letter-spacing: -0.0143em;
	line-height: 1.35;
}

/* WordPress puts a block gap between every root-level child. Sections here own
   their own vertical space, so the first one should meet the nav directly, and
   the last one should run straight into the footer rather than ending on a
   band of page background. Both cases are the same gap, so they are one rule. */
.wp-site-blocks > main,
.wp-site-blocks > footer {
	margin-block-start: 0;
}

/* The same gap again, one level down, between the sections themselves. It is
   core's own theme.json that ships it: `styles.spacing.blockGap: "24px"` becomes
   `:root :where(.is-layout-constrained) > * { margin-block: 24px 0 }`, which is
   a margin-block SHORTHAND, so grepping for margin-block-start never finds it.
   Sections butt up, so it goes.

   Scoped to the full-width children of the post content. Spacing between
   paragraphs, list items and buttons inside a section is untouched, because of
   the child combinator, and an ordinary block dropped between two sections
   keeps its gap because it is not alignfull. That also keeps the rule off blog
   post paragraphs the day a single.html arrives, since those are direct
   children of post-content too. Three classes beat core's (0,1,0) outright
   rather than depending on which stylesheet the editor prints last. */
.wp-block-post-content.is-layout-constrained > .alignfull {
	margin-block-start: 0;
}

/* Colouring part of a heading uses core's Highlight format, which is a <mark>.
   The browser default for <mark> is a yellow plate, and core suppresses it with
   an inline `background-color: transparent` rather than with a rule. That inline
   style is the one thing kses strips from a <mark>, so the moment content is
   saved by anyone without unfiltered_html, or inserted programmatically, the
   yellow comes back and a two-tone quote turns into a highlighter pen.

   Caught by rendering the quote in a real install, not by reading the markup,
   which was correct. Only marks that carry a text colour are neutralised; a
   genuine highlight sets its own background inline and still wins over this. */
.wp-site-blocks mark.has-inline-color {
	background-color: transparent;
}

/* ---- The `border` slug collides with core's border flag -------------------
 *
 * The palette has a colour whose slug is `border`, so theme.json generates its
 * text-colour class as
 *
 *     .has-border-color { color: var(--wp--preset--color--border) !important }
 *
 * and core's border support writes that *same class name* onto every block that
 * carries a border colour, purely as a flag that one is set. See `qF()` in
 * wp-includes/js/dist/block-editor.min.js: `{"has-border-color": borderColor ||
 * style?.border?.color}`, alongside the real one, `has-$slug-border-color`.
 *
 * So any block with a hairline repaints its own text, and everything inside it
 * that has not set a colour of its own, to #D4D4D8. Silently, at !important,
 * with nothing wrong in the markup. It was already happening on the comparison
 * section's right-hand card, whose list was reading grey-lilac on navy instead
 * of white, and it is what turned every price and tick list on the pricing
 * cards to pale grey the first time they rendered.
 *
 * Told apart by `has-text-color`. Core writes that alongside every text-colour
 * preset and never alongside the border flag, so a lone `has-border-color` is
 * always the flag. `!important` because core's rule carries one: with two, the
 * higher specificity decides, and (0,2,0) beats (0,1,0).
 *
 * The second rule is the case the first cannot reach. A block that sets a text
 * colour AND a border colour carries both classes, so `has-text-color` no
 * longer separates them, and the winner is whichever preset theme.json prints
 * later. Everything declared after `border` in the palette already wins on
 * source order; the six declared before it lose. `base` on a bordered card in a
 * dark section is a real combination, so those six are re-asserted here.
 *
 * All of this is a stopgap. The fix is to rename the slug, which is a decision
 * about the palette rather than about CSS: a slug is stored in the markup of
 * every block that uses it, so renaming is not free and is Nol's call.
 * ------------------------------------------------------------------------- */

.has-border-color:not(.has-text-color) {
	color: inherit !important;
}

.has-border-color.has-base-color { color: var(--wp--preset--color--base) !important; }
.has-border-color.has-base-muted-color { color: var(--wp--preset--color--base-muted) !important; }
.has-border-color.has-canvas-color { color: var(--wp--preset--color--canvas) !important; }
.has-border-color.has-surface-color { color: var(--wp--preset--color--surface) !important; }
.has-border-color.has-surface-alt-color { color: var(--wp--preset--color--surface-alt) !important; }
.has-border-color.has-surface-dark-color { color: var(--wp--preset--color--surface-dark) !important; }

/* The tick. One green and one glyph, shared by the comparison list and the
   intro section's points, because they are the same mark making the same point
   and two copies drift the moment either is retuned. Point both at a palette
   slug once the greens are settled; see docs/pattern-types.md. */
:root {
	--vallem-tick-color: #89ed75;
	--vallem-tick-glyph: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'><path fill='%23FFFFFF' fill-rule='evenodd' clip-rule='evenodd' d='M21.4056 8.21413C22.0123 8.6108 22.1825 9.42421 21.7859 10.0309L14.3485 21.4057C14.1225 21.7514 13.747 21.9705 13.3349 21.9972C12.9228 22.024 12.5222 21.8552 12.2534 21.5417L8.31599 16.948C7.84426 16.3976 7.908 15.569 8.45835 15.0973C9.00871 14.6256 9.83728 14.6893 10.309 15.2397L13.1084 18.5057L19.5888 8.59438C19.9855 7.98769 20.7989 7.81744 21.4056 8.21413Z'/></svg>");

	/* The arrow, for the same reason as the tick: one glyph, more than one
	   place. Drawn rather than typed because U+2192 is outside the unicode-range
	   of both Inter subsets we ship, so a text arrow would come from the OS
	   fallback and change shape per platform. It is always used as a mask, so
	   the stroke colour inside it never shows and the colour stays a property of
	   whatever is using it. Used by the mega menu's panel titles and by the
	   arrow-lead button variation. */
	--vallem-arrow: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M2.5 8h11m-4.5-4.5L13.5 8 9 12.5' fill='none' stroke='%23000' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");

	/* The stars, hoisted here the day a second section needed them. One data URI
	   holding all five, on a 100x24 viewBox at a 20-unit pitch, so `contain`
	   sizes a row off its height alone and no box needs a width. Always used as
	   a mask, so the fill inside it never shows and the colour belongs to
	   whatever is drawing them. Height stays local to each row, because the demo
	   CTA sets them beside a 14px score and the CTA plate sets them alone above
	   a 36px heading. Five is the count in both places: five outlines beside a
	   varying score is the convention, it is not a number to correct. Used by
	   the demo CTA's rating row and by the CTA plate with the logo band. */
	--vallem-stars-glyph: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 24'><path fill='%23000' d='M10.0 3.4 11.9 9.3 18.2 9.3 13.1 13.0 15.1 19.0 10.0 15.3 4.9 19.0 6.9 13.0 1.8 9.3 8.1 9.3ZM30.0 3.4 31.9 9.3 38.2 9.3 33.1 13.0 35.1 19.0 30.0 15.3 24.9 19.0 26.9 13.0 21.8 9.3 28.1 9.3ZM50.0 3.4 51.9 9.3 58.2 9.3 53.1 13.0 55.1 19.0 50.0 15.3 44.9 19.0 46.9 13.0 41.8 9.3 48.1 9.3ZM70.0 3.4 71.9 9.3 78.2 9.3 73.1 13.0 75.1 19.0 70.0 15.3 64.9 19.0 66.9 13.0 61.8 9.3 68.1 9.3ZM90.0 3.4 91.9 9.3 98.2 9.3 93.1 13.0 95.1 19.0 90.0 15.3 84.9 19.0 86.9 13.0 81.8 9.3 88.1 9.3Z'/></svg>");

	/* The dot field, hoisted here the day a second plate drew one, same rule as
	   the tick, the arrow and the stars: one thing, more than one place, so it
	   moves to :root. Used by the CTA plate with the logo band and by the quote
	   plate.

	   Mixed out of currentcolor rather than out of a palette slug, so it
	   recolours with whatever plate is drawing it and needs no light and dark
	   variant. --vallem-dot-ink is how much of that colour reaches the dot; 9%
	   is the value both plates use and the one to turn if a third plate needs a
	   fainter field.

	   It is always a plate's own background-image and never a ::before, and
	   that is what makes the Achtergrond-paneel work: WordPress writes a chosen
	   image as an inline background-image with a background-size beside it
	   (block-supports/background.php), inline beats the stylesheet, and the
	   dots are replaced outright. A ::before would sit on top of the upload.
	   The pitch stays local to each plate, because that is the one number the
	   two sections set differently. */
	--vallem-dot-ink: 9%;
	--vallem-dot-field: radial-gradient(
		color-mix(in srgb, currentcolor var(--vallem-dot-ink), transparent) 1px,
		transparent 1.1px
	);
}

/* ------------------------------------------------------------------------- *
 * Buttons
 *
 * Four decisions, kept apart on purpose so they never fight for the same
 * control:
 *
 *   look    a block style variation, styles/blocks/button-*.json
 *   shape   the border radius control
 *   size    the font size control, because every dimension is in em
 *   motion  here, and automatic, so no pattern has to opt in
 *
 * Size is the one worth spelling out. theme.json sets the padding and the corner
 * in em, so picking Klein or Groot in the editor moves the padding, the corner
 * and the roll together. There is no size variation to register and nothing
 * that can be set to a size the type does not match.
 * ------------------------------------------------------------------------- */

.wp-block-button__link {
	/* The roll travels exactly one line box, so the second copy of a character
	   lands flush outside the clip whatever line height the button ends up
	   with. The em value is the fallback for browsers without lh (before
	   Chrome 109, Safari 16.4, Firefox 120) and matches theme.json's 1.35. */
	--vallem-roll: 1.35em;

	/* The logo row rolls on this curve. One motion, used twice. */
	--vallem-roll-time: 600ms;
	--vallem-roll-ease: cubic-bezier(0.625, 0.05, 0, 1);

	/* How far the button gives on hover. Small on purpose: it should read as
	   the button taking the press, not as an animation. */
	--vallem-press: 0.98;

	transition:
		transform var(--vallem-roll-time) var(--vallem-roll-ease),
		background-color 200ms ease,
		color 200ms ease;
}

@supports (height: 1lh) {
	.wp-block-button__link {
		--vallem-roll: 1lh;
	}
}

/* Outline. Owned outright rather than adjusted, because core's own rule is
   three classes deep and its colours are guarded by :not() selectors, so half
   of it lands and half of it does not depending on what the editor set. One
   hairline at a fraction of the label's own colour, which means it reads on
   any background without anybody choosing a border colour. Anything set in the
   editor is an inline style and still wins over this. */
.wp-site-blocks .wp-block-button.is-style-outline > .wp-block-button__link,
.wp-site-blocks .wp-block-button > .wp-block-button__link.is-style-outline {
	border: 1px solid color-mix(in srgb, currentcolor 28%, transparent);
	padding:
		var(--wp--custom--button-padding-block)
		var(--wp--custom--button-padding-inline);
	background-color: transparent;
}

/* Subtle. Outline without the hairline: a tertiary action that reads as copy
   until the pointer arrives. Its colours are here for the same reason as
   outline's, and taking the label from currentcolor is what lets one variation
   serve a light section and a dark one. */
.wp-site-blocks .wp-block-button.is-style-subtle > .wp-block-button__link {
	color: currentcolor;
	background-color: transparent;
}

/* The button gives a little under the pointer. A transform, because it is the
   only way to change a button's size that the page cannot see: it happens after
   layout, so nothing around it can move no matter what.

   This started as a border that grew inward with the padding giving back
   exactly what it took, which keeps the label pin sharp and matches the
   reference. It cannot be made to hold. Border widths are snapped to whole
   device pixels and padding is not, so the two only balance when the frame
   lands on a whole pixel: an em frame drifted at 14, 18 and 20px, and a 2px
   frame drifted again at 125% display scaling, which is a normal Windows
   setting. Measured 16 of 18 buttons moving their neighbours. Sharper text is
   not worth a layout that is right on some machines. */
.wp-block-button__link:focus-visible {
	transform: scale(var(--vallem-press));
}

@media (hover: hover) and (pointer: fine) {
	.wp-block-button__link:hover {
		transform: scale(var(--vallem-press));
	}
}

/* WordPress emits pseudo states for elements but not for block style
   variations, so a variation's hover colour cannot live in its own file with
   the rest of its look. Verified against the generated stylesheet, not
   assumed. Four classes clears both the variation and theme.json's own button
   hover; a colour set in the editor still wins over all of it. */
.wp-block-button.is-style-primary > .wp-block-button__link:hover {
	background-color: var(--wp--preset--color--primary-dark);
}

.wp-block-button.is-style-inverted > .wp-block-button__link:hover {
	background-color: var(--wp--preset--color--surface);
	color: var(--wp--preset--color--contrast);
}

.wp-site-blocks .wp-block-button.is-style-outline > .wp-block-button__link:hover {
	background-color: color-mix(in srgb, currentcolor 8%, transparent);
	border-color: color-mix(in srgb, currentcolor 55%, transparent);
}

.wp-site-blocks .wp-block-button.is-style-subtle > .wp-block-button__link:hover {
	background-color: color-mix(in srgb, currentcolor 8%, transparent);
}

/* The roll. Each character hides a second copy of itself one line below, drawn
   as a shadow rather than as more markup, and hover slides the pair up so the
   copy lands where the original was. assets/js/text-roll.js does the splitting;
   without it the label is an ordinary label and the button is unharmed. */
.vallem-roll__w {
	display: inline-block;
	white-space: nowrap;
}

/* Inline boxes cannot be transformed, so each character needs its own block.
   This one is the clip and never moves, the one inside it is what rolls; a
   single element cannot be both. No will-change anywhere: it rasterises the
   span to the size of its own box, which shears the overhang off an f and cuts
   the descender off a j. */
.vallem-roll__c {
	display: inline-block;
	overflow: hidden;
	vertical-align: bottom;
}

/* The hair of rotation is what keeps the two copies rasterising identically
   mid-transition. */
.vallem-roll__r {
	display: inline-block;
	text-shadow: 0 var(--vallem-roll) currentcolor;
	transform: translateY(0) rotate(0.001deg);
	transition: transform var(--vallem-roll-time) var(--vallem-roll-ease);
}

.wp-block-button__link:focus-visible .vallem-roll__r {
	transform: translateY(calc(-1 * var(--vallem-roll))) rotate(0.001deg);
}

@media (hover: hover) and (pointer: fine) {
	.wp-block-button__link:hover .vallem-roll__r {
		transform: translateY(calc(-1 * var(--vallem-roll))) rotate(0.001deg);
	}
}

/* Reduced motion takes the movement away rather than making it instant. A
   character that snaps a line upward is the same event compressed, not a
   quieter one. The colour change theme.json puts on :hover stays, so the button
   still answers the pointer. */
@media (prefers-reduced-motion: reduce) {
	.wp-block-button__link {
		--vallem-press: 1;

		transition:
			background-color 200ms ease,
			color 200ms ease;
	}

	.vallem-roll__r {
		transition: none;
	}

	.wp-block-button__link:hover .vallem-roll__r,
	.wp-block-button__link:focus-visible .vallem-roll__r {
		transform: none;
	}
}

/* Header. Top bar and nav are ordinary sections in the flow. Once the nav has
   scrolled past, the bar detaches into a floating pill. Two layouts, picked in
   the editor: default sits above the next section, the "Over de sectie" block
   style sits on top of it. Behaviour in assets/js/header.js */

:root {
	--vallem-topbar-height: 36px;
	--vallem-nav-height: 64px;
	--vallem-nav-height-compact: 56px;
	--vallem-nav-width: 1376px;
	--vallem-nav-radius: 12px;

	/* Where the page's content starts, and the line every section is set to.
	   It is the nav bar's 1376px shell less the spacing-20 the bar pads its own
	   contents by, so a section's heading begins on the same vertical as the
	   logo. The bar itself is transparent until the nav sticks, so this line,
	   not the shell, is the width of the navigation as anyone actually sees it.

	   Written out rather than calculated from the two: the eight patterns carry
	   the same number as a literal in their layout.contentSize attribute, which
	   cannot take a var(), and a calc here would let the two drift apart in
	   silence. Change this and change those. */
	--vallem-content-line: 1344px;
	--vallem-ease: cubic-bezier(0.22, 1, 0.36, 1);

	/* The light plate. A hairline frame drawn per side so the joint between the
	   bar and an open panel can be left open, and a lift that carries both. */
	--vallem-nav-hairline: rgb(24 24 27 / 8%);
	--vallem-nav-lift: 0 1px 2px rgb(24 24 27 / 4%), 0 12px 32px rgb(24 24 27 / 8%);
}

.vallem-topbar {
	position: relative;
	z-index: 1;
	min-height: var(--vallem-topbar-height);
	display: flex;
	align-items: center;
}

/* The constrained layout centres its child with auto margins, which in a flex
   parent leaves the row hugging its contents. Stretch it so the phone number
   sits hard left and the links hard right. */
.vallem-topbar > * {
	flex: 1;
	max-width: var(--vallem-nav-width);

	/* Matches the nav bar's inner padding so both rows start on the same line. */
	padding-inline: var(--wp--preset--spacing--20);
}

.vallem-topbar a {
	color: inherit;
	text-decoration: none;
	opacity: 0.75;
	transition: opacity 150ms ease;
}

.vallem-topbar a:hover,
.vallem-topbar a:focus-visible {
	opacity: 1;
}

.vallem-topbar p {
	margin: 0;
	white-space: nowrap;
}

/* Default: the nav is an ordinary section in the flow. It reserves its own
   height so nothing jumps when the bar detaches on scroll. */
.vallem-nav {
	position: relative;
	z-index: 100;
	min-height: var(--vallem-nav-height);
	padding-inline: var(--wp--preset--spacing--50);
}

/* The shell holds the bar and the panel stack together so the mega menu
   travels with the bar when it detaches. Everything that used to position the
   bar positions the shell now. */
/* The scrim is inserted as the nav's first child, which makes the shell a
   second child and hands it the flow layout's block gap. That pushed the whole
   bar down by 24px. Neither of these two is in the flow in any real sense. */
.vallem-nav .vallem-nav__shell,
.vallem-nav .vallem-nav__backdrop {
	margin-block: 0;
}

.vallem-nav__shell {
	position: relative;
	z-index: 1;
	max-width: var(--vallem-nav-width);
	margin-inline: auto;
}

.vallem-nav__bar {
	min-height: var(--vallem-nav-height);
	padding-inline: var(--wp--preset--spacing--20);
	border-radius: var(--vallem-nav-radius);
	color: var(--wp--preset--color--contrast);
	background-color: transparent;
	transition:
		background-color 300ms var(--vallem-ease),
		box-shadow 300ms var(--vallem-ease),
		min-height 300ms var(--vallem-ease),
		color 300ms var(--vallem-ease);
}

/* Overlay: the nav sits on top of whatever section follows it instead of
   above it. Applied per template via the "Over de sectie" block style. */
.vallem-nav.is-style-overlay {
	position: absolute;
	inset-inline: 0;
	min-height: 0;
}

.vallem-nav.is-style-overlay + * {
	--vallem-overlay-clearance: var(--vallem-nav-height);
}

/* Light contents wherever the bar is transparent over something dark. */
.vallem-nav.is-on-dark .vallem-nav__bar {
	color: var(--wp--preset--color--base);
}

/* Detached: the shell leaves the flow and floats. It slides with `top` rather
   than a transform, which keeps the shell out of the way as a containing block.
   Desktop only. The phone has its own arrangement further down, where the nav
   is sticky and the pill never happens. */
.vallem-nav.is-stuck .vallem-nav__shell {
	position: fixed;
	inset-inline: var(--wp--preset--spacing--50);
	top: calc(var(--vallem-admin-offset, 0px) + 8px);
	transition: top 450ms var(--vallem-ease);
}

.vallem-nav.is-stuck.is-hidden .vallem-nav__shell {
	top: -140px;
}

/* The pill is a light plate, so it has to draw its own frame and lift instead
   of borrowing contrast from whatever is behind it. After the on-dark rule
   because both states can be true at once and the plate is what the eye reads.  */
.vallem-nav.is-stuck .vallem-nav__bar {
	min-height: var(--vallem-nav-height-compact);
	color: var(--wp--preset--color--contrast);
	background-color: var(--wp--preset--color--base);
	box-shadow:
		inset 0 0 0 1px var(--vallem-nav-hairline),
		var(--vallem-nav-lift);
}

/* Suppress the slide for the frame the bar detaches in, so it does not
   animate in from wherever it happened to be sitting. */
.vallem-nav.no-transition .vallem-nav__shell,
.vallem-nav.no-transition .vallem-nav__bar {
	transition: none;
}

/* The admin bar shifts the document but not fixed elements. */
.admin-bar {
	--vallem-admin-offset: 32px;
}

@media (width <= 782px) {
	.admin-bar {
		--vallem-admin-offset: 46px;
	}
}

.vallem-nav__logo {
	display: flex;
	align-items: center;
	flex-shrink: 0;
	color: inherit;
}

.vallem-nav__logo svg {
	display: block;
	width: auto;
	height: 22px;
}

/* Nav links inherit the bar colour so they flip with it. */
.vallem-nav__menu .wp-block-navigation-item__content {
	padding: 6px 10px;
	border-radius: 6px;
	color: inherit;
	font-weight: 500;
	text-decoration: none;
	transition: background-color 150ms ease;
}

.vallem-nav__menu .wp-block-navigation-item__content:hover,
.vallem-nav__menu .wp-block-navigation-item__content:focus-visible {
	background-color: currentcolor;
}

/* Tint only the background, not the text, on hover. */
.vallem-nav__menu .wp-block-navigation-item__content:hover {
	background-color: color-mix(in srgb, currentcolor 10%, transparent);
}

/* The bar's button is the ordinary button, scaled to the bar rather than to
   the type. Overriding the token rather than the padding is what keeps it in
   the system: theme.json sets the padding from this variable, so the bar sets
   a size without owning a second copy of how a button is built. */
.vallem-nav__cta {
	--wp--custom--button-padding-block: 0.45em;
	--wp--custom--button-padding-inline: 1.15em;
}

.vallem-nav__cta .wp-block-button__link {
	white-space: nowrap;
}

/* The button inverts the bar, not the page: over a dark section the bar is
   transparent, so the ordinary dark plate would disappear into it.

   Colour has to come from here because it follows a state, the same reason the
   logo row's scheme does. Everything else about it is the ordinary button. */
.vallem-nav.is-on-dark .vallem-nav__cta .wp-block-button__link {
	color: var(--wp--preset--color--contrast);
	background-color: var(--wp--preset--color--base);
}

.vallem-nav.is-on-dark .vallem-nav__cta .wp-block-button__link:hover {
	color: var(--wp--preset--color--contrast);
	background-color: var(--wp--preset--color--surface);
}

/* The pill and an open panel both bring their own light plate, so the ordinary
   dark button is right again even while the pill floats over a dark section.
   Both of these have to sit after the on-dark rule to win the tie. */
.vallem-nav.is-stuck .vallem-nav__cta .wp-block-button__link,
.vallem-nav.is-menu-open .vallem-nav__cta .wp-block-button__link {
	color: var(--wp--preset--color--base);
	background-color: var(--wp--preset--color--contrast);
}

.vallem-nav.is-stuck .vallem-nav__cta .wp-block-button__link:hover,
.vallem-nav.is-menu-open .vallem-nav__cta .wp-block-button__link:hover {
	color: var(--wp--preset--color--base);
	background-color: var(--wp--preset--color--primary);
}

@media (width <= 781px) {
	.vallem-topbar__lead {
		overflow: hidden;
		text-overflow: ellipsis;
	}

	/* Contact stays reachable on mobile, just tighter. Through the token
	   again, for the same reason. */
	.vallem-nav__cta {
		--wp--custom--button-padding-inline: 0.85em;
	}

	.vallem-nav__logo svg {
		height: 19px;
	}

	.vallem-nav__bar {
		padding-inline: var(--wp--preset--spacing--10);
	}
}

/* Respect a reduced-motion preference: keep the states, drop the sliding. */
@media (prefers-reduced-motion: reduce) {
	.vallem-nav {
		transition: none;
	}
}

/* Mega menu. A nav item opens a panel when it carries a class of the form
   `opens-<name>` and a group in the panel stack carries the matching anchor
   `panel-<name>`. Both are ordinary editor fields, so the wiring is visible in
   the Site Editor and nothing in the JS knows the site's menu.

   Panels are plain blocks, so everything inside stays editable. Behaviour in
   assets/js/nav-mega.js. Desktop only: below 600px these same panels are read
   by assets/js/nav-drawer.js and rebuilt as the drawer's second level.

   Selectors carry the `.vallem-nav` prefix wherever they override a core layout
   default, because core's layout CSS is a single class and may print after
   this file. */

/* Open: the bar takes the same plate as the panel so the two read as one card,
   whatever was behind them. Its bottom edge is left unframed, and the panel's
   top edge likewise, so the joint between them does not show. */
.vallem-nav.is-menu-open .vallem-nav__bar {
	color: var(--wp--preset--color--contrast);
	background-color: var(--wp--preset--color--base);
	border-end-start-radius: 0;
	border-end-end-radius: 0;
	box-shadow:
		inset 0 1px 0 var(--vallem-nav-hairline),
		inset 1px 0 0 var(--vallem-nav-hairline),
		inset -1px 0 0 var(--vallem-nav-hairline);
}

/* The clip box. Height is animated from 0 to the measured panel height, which
   is what produces the morph between panels of different sizes. It overlaps the
   bar by a pixel so no hairline seam shows at fractional device pixels. */
.vallem-nav .vallem-nav__panels {
	position: absolute;
	z-index: 1;
	inset-inline: 0;
	top: calc(100% - 1px);
	height: 0;
	margin: 0;
	overflow: hidden;
	pointer-events: none;
	background-color: var(--wp--preset--color--base);
	border-end-start-radius: var(--vallem-nav-radius);
	border-end-end-radius: var(--vallem-nav-radius);
	box-shadow:
		inset 1px 0 0 var(--vallem-nav-hairline),
		inset -1px 0 0 var(--vallem-nav-hairline),
		inset 0 -1px 0 var(--vallem-nav-hairline),
		var(--vallem-nav-lift);
}

/* Panels stack on top of each other so the clip box measures whichever one is
   being asked for, not the tallest.

   Grid rather than the flex layout the block declares, because the columns pad
   themselves and flex only shares out what is left after that padding, which
   leaves a column two tracks wide 48px short of two tracks. Equal tracks are
   what grid is for. Auto flow keeps it count-agnostic, so a column added in the
   editor still lands on a track of its own. */
.vallem-nav .vallem-nav__panel {
	position: absolute;
	inset: 0 0 auto;
	margin: 0;
	display: grid;
	grid-auto-flow: column;
	grid-auto-columns: 1fr;
	gap: 0;
	align-items: stretch;
	visibility: hidden;
	opacity: 0;
}

/* No dividing rules between columns. The rule under each header groups the list
   below it, which is what the columns were being asked to do. min-width so a
   long word cannot push a track wider than its share. */
.vallem-nav .vallem-nav__panel-col {
	min-width: 0;
	margin: 0;
	padding: 40px 24px;
}

.vallem-nav .vallem-nav__panel-col > * {
	margin-block: 0;
}

/* Selected through the column so this outweighs the item's own `margin: 0`,
   which would otherwise win on source order. 20px, the same as the gap between
   two items, so the split column below can hand both jobs to one row gap and
   still line its first link up with the plain column beside it. */
.vallem-nav .vallem-nav__panel-col > .vallem-nav__panel-label + * {
	margin-block-start: 20px;
}

/* Producten runs its items two up over two of the panel's tracks, so the three
   tracks the panel was drawn on all keep the width they had. The label is left a
   direct child and spanned, so its rule still runs the whole column rather than
   stopping at the first track. */
.vallem-nav .vallem-nav__panel-col.is--split {
	grid-column: span 2;
	display: grid;
	grid-template-columns: repeat(2, minmax(0, 1fr));
	gap: 20px 48px;
	align-content: start;
}

.vallem-nav .vallem-nav__panel-col.is--split > .vallem-nav__panel-label {
	grid-column: 1 / -1;
}

/* The row gap already spaces these. Left in place the margins would add to it,
   and only on the items that happen to start a row, which staggers the two
   tracks against each other. */
.vallem-nav .vallem-nav__panel-col.is--split > .vallem-nav__panel-label + *,
.vallem-nav .vallem-nav__panel-col.is--split > .vallem-nav__panel-item + .vallem-nav__panel-item {
	margin-block-start: 0;
}

/* No preset font size on the label, so the editor's own choice wins the moment
   anyone makes one. Preset size classes carry !important.

   The padding is the room the rule sits in. */
.vallem-nav__panel-label {
	position: relative;
	padding-block-end: 12px;
	color: var(--wp--preset--color--contrast);
	font-size: 15px;
	font-weight: 500;
	line-height: 1.35;
}

/* The resting rule, and the accent that wipes over it while the column is
   hovered. Both are pseudo elements because GSAP owns the label's own transform
   and would fight anything set here. */
.vallem-nav__panel-label::before,
.vallem-nav__panel-label::after {
	content: "";
	position: absolute;
	inset-inline: 0;
	inset-block-end: 0;
}

.vallem-nav__panel-label::before {
	height: 1px;
	background-color: var(--wp--preset--color--border);
}

.vallem-nav__panel-label::after {
	height: 2px;
	background-image: linear-gradient(
		90deg,
		var(--wp--preset--color--primary),
		var(--wp--preset--color--violet)
	);
	transform: scaleX(0);
	transform-origin: left;
	transition: transform 400ms cubic-bezier(0.22, 1, 0.36, 1);
}

.vallem-nav .vallem-nav__panel-col:hover .vallem-nav__panel-label::after,
.vallem-nav .vallem-nav__panel-col:focus-within .vallem-nav__panel-label::after {
	transform: scaleX(1);
}

/* The card's label is an eyebrow over a heading, not a column header, so it
   takes neither the rule nor the accent. */
.vallem-nav .vallem-nav__panel-col.is--card .vallem-nav__panel-label::before,
.vallem-nav .vallem-nav__panel-col.is--card .vallem-nav__panel-label::after {
	content: none;
}

/* The card column uses the same class for an eyebrow above a heading, and that
   one still wants to be the quieter of the two. */
.vallem-nav .vallem-nav__panel-col.is--card .vallem-nav__panel-label {
	padding-block-end: 0;
	color: var(--wp--preset--color--contrast-muted);
	font-size: 14px;
	font-weight: 500;
}

.vallem-nav .vallem-nav__panel-col.is--card > .vallem-nav__panel-label + * {
	margin-block-start: 8px;
}

.vallem-nav .vallem-nav__panel-item {
	position: relative;
	margin: 0;
}

.vallem-nav .vallem-nav__panel-item + .vallem-nav__panel-item {
	margin-block-start: 20px;
}

.vallem-nav .vallem-nav__panel-item > * {
	margin-block: 0;
}

.vallem-nav__panel-title {
	font-size: 15px;
	font-weight: 500;
	line-height: 1.35;
}

/* Blue is the hover state, not the resting one. A column of ten blue links was
   more colour than the panel could carry. */
.vallem-nav__panel-title a {
	color: var(--wp--preset--color--contrast);
	text-decoration: none;
	transition: color 150ms ease;
}

/* The whole item is the hit area, not just the words. Out of the inline flow,
   which is why it is ::before and leaves the arrow to ::after. */
.vallem-nav__panel-title a::before {
	content: "";
	position: absolute;
	inset: 0;
}

/* The arrow is --vallem-arrow, declared once on :root. Its space is held
   whether or not it shows, so nothing reflows under the pointer. */
.vallem-nav__panel-title a::after {
	content: "";
	display: inline-block;
	width: 0.7em;
	height: 0.7em;
	margin-inline-start: 0.3em;
	vertical-align: -0.02em;
	background-color: currentColor;
	-webkit-mask: var(--vallem-arrow) center / contain no-repeat;
	mask: var(--vallem-arrow) center / contain no-repeat;
	opacity: 0;
	transform: translateX(-0.25em);
	transition: opacity 150ms ease, transform 150ms ease;
}

/* Hooked on the item, not the link, so the sibling description follows too. */
.vallem-nav .vallem-nav__panel-item:hover .vallem-nav__panel-title a,
.vallem-nav__panel-title a:focus-visible {
	color: var(--wp--preset--color--primary);
}

.vallem-nav .vallem-nav__panel-item:hover .vallem-nav__panel-title a::after,
.vallem-nav__panel-title a:focus-visible::after {
	opacity: 1;
	transform: translateX(0);
}

.vallem-nav__panel-desc {
	color: var(--wp--preset--color--contrast-muted);
	line-height: 1.4;
	transition: color 150ms ease;
}

.vallem-nav .vallem-nav__panel-item:hover .vallem-nav__panel-desc {
	color: var(--wp--preset--color--contrast);
}

/* There is no plate for a focus ring to sit on now, so it goes on the hit area,
   which is the whole item. */
.vallem-nav__panel-title a:focus-visible {
	outline: none;
}

.vallem-nav__panel-title a:focus-visible::before {
	border-radius: 4px;
	outline: 2px solid var(--wp--preset--color--primary);
	outline-offset: 4px;
}

.vallem-nav .vallem-nav__panel-col.is--card .vallem-nav__panel-cta {
	margin-block-start: 20px;
}

/* The toggle replaces the item's link at runtime and keeps core's content
   class, so it inherits the nav link's padding, radius and hover plate.

   Three classes because core sets `display: block` on the item content at two,
   which left the chevron inline and sitting on the text baseline.

   Longhands rather than `font: inherit`: the shorthand also resets the weight,
   which outranked the content class and left the three toggles at 400 while
   the one plain link stayed at 500. */
.vallem-nav .vallem-nav__menu .vallem-nav__toggle {
	appearance: none;
	border: 0;
	background: none;
	font-family: inherit;
	font-size: inherit;
	font-style: inherit;
	line-height: inherit;
	letter-spacing: inherit;
	color: inherit;
	cursor: pointer;
	display: flex;
	align-items: center;
}

/* Sized and spaced like the reference: no gap, the glyph's own padding inside
   the viewBox is the whole of the space between it and the label. */
.vallem-nav__chevron {
	width: 1.25em;
	height: 1.25em;
	flex: none;
}

.vallem-nav__backdrop {
	position: fixed;
	z-index: 0;
	inset: 0;
	visibility: hidden;
	opacity: 0;
	background-color: rgb(24 24 27 / 28%);
}

/* Below the drawer breakpoint a panel has nowhere to hang, so none of them are
   drawn. They stay in the document: the drawer reads its second level straight
   out of them, and display:none hides a subtree without emptying it. */
@media (width < 600px) {
	.vallem-nav .vallem-nav__panels {
		display: none;
	}
}

@media (prefers-reduced-motion: reduce) {
	.vallem-nav__chevron {
		transition: none;
	}

	/* The arrow still arrives, it just does not travel to get there. */
	.vallem-nav__panel-title a::after {
		transform: none;
		transition: opacity 150ms ease;
	}

	/* Same for the header accent: it appears, it does not wipe. */
	.vallem-nav__panel-label::after {
		transition: none;
	}
}

/* ------------------------------------------------------------------------- *
 * Mobile drawer.
 *
 * Below 600px the bar goes full bleed and fixed, transparent over whatever it
 * sits on until the page scrolls, then a white plate. The menu becomes a dark
 * panel hanging off the bar's bottom edge with two levels and a pinned footer.
 * Built by assets/js/nav-drawer.js from the nav block and the mega menu panels.
 *
 * The panel is not a full-screen overlay. It is anchored under the bar, and the
 * bar is pushed down by --vallem-nav-top, which counts down from the top line's
 * height to zero over the first 36px of scroll. That one number places the bar,
 * places the panel and sizes it, and it is why the top line stays visible above
 * an open panel at the top of the page and is gone once you have scrolled.
 *
 * Everything that would strand a visitor without JavaScript hangs off
 * .is-drawer-ready, which nav-drawer.js sets once it has built the panel. Without
 * it the bar stays in the flow and the menu stays an ordinary list.
 * ------------------------------------------------------------------------- */

:root {
	--vallem-drawer-line: rgb(255 255 255 / 20%); /* the dashed rules */
	--vallem-drawer-hair: rgb(255 255 255 / 8%); /* section and footer edges */
	--vallem-drawer-fill: rgb(255 255 255 / 4%); /* the card plate */

	/* A step above the card it sits on, so a pressed row reads as lifted rather
	   than as the plate it is already on. */
	--vallem-drawer-press: rgb(255 255 255 / 10%);

	/* Not contrast-muted. That token is 3.65:1 on the dark panel, which fails,
	   and a company selling compliance does not ship failing body text. White at
	   60% over contrast is 6.6:1. */
	--vallem-drawer-muted: rgb(255 255 255 / 60%);

	--vallem-drawer-radius: 12px;
}

/* Two classes, not one. Both of these are block wrappers, so core's layout CSS
   reaches them as `.wp-block-buttons.is-layout-flex` and sets display:flex at the
   same weight a single class carries. A one-class hide loses that tie and the
   buttons turn up under the header on a desktop.

   All three are the drawer's, and the drawer only exists below its breakpoint.
   The panel is built once and then left in the document, so this is what keeps
   it out of the header on the way back up: turning a phone to landscape crosses
   600px, and without this the panel becomes an ordinary block in the flow and
   spills a bulleted copy of the menu under the logo.

   The CTA is off here because the JS moves it into the panel's footer, and the
   burger because the JS builds it. */
.vallem-nav__drawer,
.vallem-nav .vallem-nav__drawer-cta,
.vallem-nav__burger {
	display: none;
}

/* The editor canvas, where all of this has to stay reachable: nothing moves the
   buttons into a footer there and nothing carries the top line's links into a
   panel, so hidden would mean uneditable. Written twice for the same reason as
   the about hero's swap, and inert on the front end. */
body.editor-styles-wrapper .vallem-nav .vallem-nav__drawer-cta,
.editor-styles-wrapper .vallem-nav .vallem-nav__drawer-cta {
	display: flex;
}

body.editor-styles-wrapper .vallem-topbar .vallem-topbar__links,
.editor-styles-wrapper .vallem-topbar .vallem-topbar__links {
	display: flex;
}

@media (width < 600px) {
	/* The top line carries one thing on a phone. Academy is not dropped,
	   nav-drawer.js carries it into the panel. */
	.vallem-topbar .vallem-topbar__links {
		display: none;
	}

	.vallem-topbar__lead::after {
		content: "";
		display: inline-block;
		width: 0.75em;
		height: 0.75em;
		margin-inline-start: 0.4em;
		vertical-align: -0.05em;
		background-color: currentcolor;
		-webkit-mask: var(--vallem-arrow) center / contain no-repeat;
		mask: var(--vallem-arrow) center / contain no-repeat;
		opacity: 0.6;
	}

	/* Ungated, both of these. The bar is this tall on a phone whether or not the
	   drawer ever builds, so nothing shifts and nothing animates its height when
	   the class arrives. */
	.vallem-nav {
		min-height: var(--vallem-nav-height-compact);
		padding-inline: 0;
	}

	.vallem-nav .vallem-nav__bar {
		min-height: var(--vallem-nav-height-compact);
		padding-inline: var(--wp--preset--spacing--20);
		border-radius: 0;
	}

	.vallem-nav .vallem-nav__shell {
		max-width: none;
	}

	/* Sticky rather than fixed. The bar starts in the flow under the top line and
	   pins itself the moment that line has scrolled away, which is the whole
	   behaviour, done by the compositor. A fixed bar offset by a scroll handler
	   lags behind the line it is meant to sit under during a fast flick on iOS,
	   which opens and closes a gap at exactly the moment the two have to look
	   joined.

	   On the template part, not on the nav. Sticky travels only inside its own
	   parent, and the nav's parent is this wrapper, which is exactly as tall as
	   the header: a sticky nav has nowhere to go and scrolls away with it. Sticking
	   the wrapper at minus the top line's height parks that line off screen and
	   lands the bar on zero, which is the same result somewhere it can happen.

	   No admin bar offset. Below 600px core sets #wpadminbar to position:absolute,
	   wp-includes/css/admin-bar.css, so it scrolls away like anything else.
	   Reserving 46px for it left the bar floating 46px down the screen. */
	header.wp-block-template-part:has(> .vallem-nav.is-drawer-ready) {
		position: sticky;
		z-index: 100;
		top: calc(-1 * var(--vallem-topbar-h, var(--vallem-topbar-height)));
	}

	/* The pill belongs to the desktop header. header.js drops these classes on
	   this side of the breakpoint, so this only covers the frame in between. */
	.vallem-nav.is-drawer-ready.is-stuck .vallem-nav__shell,
	.vallem-nav.is-drawer-ready.is-stuck.is-hidden .vallem-nav__shell {
		position: relative;
		inset-inline: auto;
		top: auto;
	}

	/* A white bar on a near white canvas needs the hairline to have an edge at
	   all. The reference gets away without one because its page is grey. */
	.vallem-nav.is-drawer-ready.is-scrolled .vallem-nav__bar {
		color: var(--wp--preset--color--contrast);
		background-color: var(--wp--preset--color--base);
		box-shadow: inset 0 -1px 0 var(--vallem-nav-hairline);
	}

	/* Open, the bar is the panel's top edge, so it takes the panel's plate. */
	.vallem-nav.is-drawer-ready.is-drawer-open .vallem-nav__bar {
		color: var(--wp--preset--color--base);
		background-color: var(--wp--preset--color--contrast);
		box-shadow: none;
	}

	.vallem-nav.is-drawer-ready.is-stuck .vallem-nav__bar {
		border-radius: 0;
		box-shadow: inset 0 -1px 0 var(--vallem-nav-hairline);
	}

	/* Not gated on the drawer being ready. Core used to hide this list itself
	   below 600px and show its own burger instead, and dropping its overlay took
	   that with it. Left until the JS runs, five links wrap into five rows in a
	   bar built for one, so the header lands on top of the page. Without JS there
	   is no menu on a phone, which is what core's overlay also came to. */
	.vallem-nav .vallem-nav__menu,
	.vallem-nav .vallem-nav__cta {
		display: none;
	}

	/* Belt and braces. The nav block ships with Overlay Menu set to "Never", so
	   none of this is rendered, but flipping that back in the editor would put
	   core's burger next to ours and its overlay behind our drawer. */
	.vallem-nav .wp-block-navigation__responsive-container-open,
	.vallem-nav .wp-block-navigation__responsive-container-close {
		display: none;
	}

	.vallem-nav.is-drawer-ready .vallem-nav__burger {
		display: flex;
		align-items: center;
		justify-content: center;
		width: 44px;
		height: 44px;
		margin-inline-end: -10px;
		padding: 0;
		border: 0;
		background: none;
		color: inherit;
		cursor: pointer;
	}

	.vallem-nav__burger svg {
		width: 20px;
		height: 20px;
	}

	/* --- The panel ---------------------------------------------------------- */

	.vallem-nav__drawer {
		position: absolute;
		z-index: 2;
		inset-inline: 0;
		top: 100%;

		/* The shell is a flow group, so core hands every child after the first a
		   block gap. On an absolutely positioned box that is not spacing, it is a
		   24px drop, and the page showed through the gap under the bar. */
		margin: 0;
		display: flex;
		flex-direction: column;
		/* Spelled out, like everywhere else in this file: there is no global
		   reset, and the height below has to include the top border or the panel
		   overhangs the bottom of the screen by it. */
		box-sizing: border-box;

		/* --vallem-nav-top is how far down the screen the bar currently sits,
		   measured in header.js. The panel starts under the bar and ends at the
		   bottom of the viewport, so its height is whatever is left. dvh, not vh:
		   the bar is anchored to the visible viewport, and only dvh agrees with
		   it while a mobile browser's toolbars come and go. */
		height: calc(
			100dvh - var(--vallem-nav-height-compact) - var(--vallem-nav-top, 0px)
		);
		overflow: hidden;
		color: var(--wp--preset--color--base);
		background-color: var(--wp--preset--color--contrast);
		border-top: 1px solid var(--vallem-drawer-hair);
		border-end-start-radius: var(--vallem-drawer-radius);
		border-end-end-radius: var(--vallem-drawer-radius);
		box-shadow: 0 10px 15px -3px rgb(0 0 0 / 10%), 0 4px 6px -4px rgb(0 0 0 / 10%);
		opacity: 0;
		translate: 0 -12px;
		pointer-events: none;

		/* visibility carries the closed panel's links out of the tab order. The
		   JS also marks the panel inert, which is the better tool, but this is
		   the one that has always worked everywhere. It is in the transition so
		   it holds until the fade has finished rather than cutting it off. */
		visibility: hidden;
		transition:
			opacity 300ms var(--vallem-ease),
			translate 300ms var(--vallem-ease),
			visibility 300ms;
	}

	.vallem-nav__drawer:focus {
		outline: none;
	}

	.vallem-nav__drawer.is-open {
		opacity: 1;
		translate: 0 0;
		pointer-events: auto;
		visibility: visible;
	}

	/* Both levels sit side by side on one track twice the panel's width, so the
	   drill-down is one transform and neither level has to be measured. */
	.vallem-nav__drawer-track {
		display: flex;
		flex: 1;
		min-height: 0;
		width: 200%;
		transition: transform 300ms ease-out;
	}

	.vallem-nav__drawer.is-drilled .vallem-nav__drawer-track {
		transform: translateX(-50%);
	}

	/* Closing resets the drill, and a 300ms slide underneath a 300ms fade shows
	   as a ghost of the first level sweeping back in: the fade is on an ease that
	   is most of the way done by the halfway mark, the slide is not. Nothing is
	   being watched while the panel is shut, so the reset is instant. */
	.vallem-nav__drawer:not(.is-open) .vallem-nav__drawer-track {
		transition: none;
	}

	.vallem-nav__drawer-level {
		display: flex;
		flex: none;
		flex-direction: column;
		width: 50%;
		min-height: 0;
	}

	/* The footer is pinned over this, so the padding is what keeps the last row
	   reachable rather than hidden underneath it. The footer is 88px plus
	   whatever the safe area adds, so the reserve tracks the same inset with a
	   few pixels in hand for a label that wraps to two lines. */
	.vallem-nav__drawer-scroll {
		flex: 1;
		min-height: 0;
		overflow-y: auto;
		overscroll-behavior: contain;
		padding: 4px var(--wp--preset--spacing--20)
			calc(112px + env(safe-area-inset-bottom));
	}

	/* --- First level -------------------------------------------------------- */

	.vallem-nav__drawer-list,
	.vallem-nav__drawer-aside,
	.vallem-nav__drawer-card,
	.vallem-nav__drawer-links {
		margin: 0;
		padding: 0;
		list-style: none;
	}

	.vallem-nav__drawer-item {
		display: flex;
		align-items: center;
		justify-content: space-between;
		gap: 12px;
		width: 100%;

		/* Both, explicitly. A button is border-box by user agent default and an
		   anchor is content-box, so this one min-height was measuring the border
		   box on the rows with a chevron and the content box on the rest: 69px
		   against 105px for the same declaration. */
		box-sizing: border-box;
		min-height: 64px;
		padding: 20px 0;
		border: 0;
		border-block-end: 1px dashed var(--vallem-drawer-line);
		border-radius: 0;
		background: none;
		color: inherit;
		font-family: inherit;
		font-size: 20px;
		font-weight: 500;
		line-height: 1.4;
		letter-spacing: inherit;
		text-align: start;
		text-decoration: none;
		cursor: pointer;
	}

	.vallem-nav__drawer-item svg {
		flex: none;
		width: 24px;
		height: 24px;
		opacity: 0.55;
	}

	.vallem-nav__drawer-item:active {
		opacity: 0.7;
	}

	/* The top line's other links, kept quieter than the menu proper. */
	.vallem-nav__drawer-aside {
		display: flex;
		flex-direction: column;
		gap: 16px;
		margin-block-start: 28px;
	}

	/* --- Second level ------------------------------------------------------- */

	.vallem-nav__drawer-back {
		display: flex;
		flex: none;
		align-items: center;
		gap: 8px;
		padding: 20px var(--wp--preset--spacing--20);
		border: 0;
		background: none;
		color: inherit;
		font-family: inherit;
		font-size: 12px;
		font-weight: 500;
		line-height: 1.1;
		letter-spacing: 0.72px;
		text-transform: uppercase;
		cursor: pointer;
	}

	.vallem-nav__drawer-back svg {
		flex: none;
		width: 24px;
		height: 24px;
	}

	/* The browser's own ring is a blue that belongs to no part of this panel, and
	   the rows sit on a dark plate where it barely reads. Keyboard only: focus
	   moves on its own when you drill in, and a thumb should not be handed a ring
	   for it. */
	.vallem-nav__drawer-item:focus-visible,
	.vallem-nav__drawer-back:focus-visible,
	.vallem-nav__drawer-row:focus-visible,
	.vallem-nav__drawer-link:focus-visible {
		outline: 2px solid var(--wp--preset--color--base);
		outline-offset: -2px;
		border-radius: 4px;
	}

	.vallem-nav__drawer-sub {
		display: flex;
		flex-direction: column;
		gap: 24px;
		padding-block-start: 4px;
	}

	.vallem-nav__drawer-section {
		display: flex;
		flex-direction: column;
		gap: 20px;
		padding-block-start: 24px;
		border-block-start: 1px solid var(--vallem-drawer-hair);
	}

	.vallem-nav__drawer-section:first-child {
		padding-block-start: 0;
		border-block-start: 0;
	}

	.vallem-nav__drawer-eyebrow {
		margin: 0;
		color: var(--vallem-drawer-muted);
		font-size: 12px;
		font-weight: 500;
		line-height: 1.1;
		letter-spacing: 0.72px;
		text-transform: uppercase;
	}

	/* A column whose items carry descriptions becomes a card, one whose items are
	   bare titles becomes a plain list. The shape follows the content, so a column
	   written in the editor lands right without anyone tagging it. */
	.vallem-nav__drawer-card {
		display: flex;
		flex-direction: column;
		gap: 4px;
		padding: 8px;
		background-color: var(--vallem-drawer-fill);
		border-radius: 8px;
	}

	.vallem-nav__drawer-row {
		display: flex;
		flex-direction: column;
		gap: 2px;
		padding: 10px;
		border-radius: 6px;
		color: inherit;
		text-decoration: none;
	}

	.vallem-nav__drawer-row:active {
		background-color: var(--vallem-drawer-press);
	}

	.vallem-nav__drawer-row-title {
		font-size: 16px;
		font-weight: 500;
		line-height: 1.5;
	}

	.vallem-nav__drawer-row-desc {
		color: var(--vallem-drawer-muted);
		font-size: 14px;
		line-height: 1.5;
	}

	.vallem-nav__drawer-links {
		display: flex;
		flex-direction: column;
		gap: 16px;
	}

	.vallem-nav__drawer-link {
		color: inherit;
		font-size: 15px;
		font-weight: 400;
		line-height: 1.5;
		text-decoration: none;
	}

	.vallem-nav__drawer-link:active {
		opacity: 0.7;
	}

	/* --- Footer ------------------------------------------------------------- */

	.vallem-nav__drawer-foot {
		position: absolute;
		z-index: 1;
		inset-inline: 0;
		bottom: 0;
		padding:
			16px
			var(--wp--preset--spacing--20)
			max(20px, env(safe-area-inset-bottom));
		border-block-start: 1px solid var(--vallem-drawer-hair);
		background-color: var(--wp--preset--color--contrast);
		border-end-start-radius: var(--vallem-drawer-radius);
		border-end-end-radius: var(--vallem-drawer-radius);
	}

	.vallem-nav .vallem-nav__drawer-foot .vallem-nav__drawer-cta {
		display: flex;
		gap: 12px;
		margin: 0;

		/* Two buttons side by side on a 390px screen leave about 125px of label
		   each at the ordinary padding, and "Contact opnemen" is wider than that,
		   so it wrapped. Through the token, the way the bar's button is sized,
		   rather than by overriding the padding itself. */
		--wp--custom--button-padding-inline: 0.75em;
	}

	.vallem-nav__drawer-foot .wp-block-button {
		flex: 1;
	}

	.vallem-nav__drawer-foot .wp-block-button__link {
		display: flex;
		align-items: center;
		justify-content: center;
		box-sizing: border-box;
		width: 100%;
		min-height: 51px;
		padding-block: 0;
		border-radius: 8px;
	}

	/* theme.json's button is a dark plate with light text, which is invisible on
	   this panel. Same inversion the bar's button makes over a dark section.

	   The hover has to be written out for the same reason it is written out for
	   the bar's button: overriding the resting colour here outranks theme.json's
	   own :hover, so without this the button would have no hover at all. Outline
	   needs none, its hover is built out of currentcolor and follows the flip. */
	.vallem-nav__drawer-foot .wp-block-button:not(.is-style-outline) .wp-block-button__link {
		color: var(--wp--preset--color--contrast);
		background-color: var(--wp--preset--color--base);
	}

	.vallem-nav__drawer-foot .wp-block-button:not(.is-style-outline) .wp-block-button__link:hover {
		color: var(--wp--preset--color--contrast);
		background-color: var(--wp--preset--color--surface);
	}

	.vallem-nav__drawer-foot .wp-block-button.is-style-outline > .wp-block-button__link {
		color: var(--wp--preset--color--base);
		border-color: rgb(255 255 255 / 40%);
	}
}

@media (width < 600px) and (prefers-reduced-motion: reduce) {
	.vallem-nav__drawer,
	.vallem-nav__drawer-track {
		transition: none;
	}
}

/* ------------------------------------------------------------------------- *
 * Section rhythm.
 *
 * The top and bottom padding of every full-width section, kept here rather than
 * as spacing attributes in the pattern markup. Patterns are unsynced, so a value
 * written into a block is copied into the page and a later edit only reaches new
 * inserts. Here one change reaches every page that already exists.
 *
 * Single class each, deliberately. A heavier selector outranks core's
 * `.has-global-padding > .alignfull` rule and cancels the negative margins that
 * make a full-width section full width. See the note above .vallem-academy__band.
 *
 * The editor's Dimensions panel reads empty for these sections. Setting a value
 * there still wins, because block spacing serialises to an inline style.
 *
 * Three steps are in use, 60, 70 and 80. That is inherited, not designed.
 * ------------------------------------------------------------------------- */

.vallem-academy {
	padding-block: var(--wp--preset--spacing--60);
	padding-inline: var(--wp--preset--spacing--50);
}

.vallem-compare {
	padding-block: var(--wp--preset--spacing--80);
}

.vallem-faq {
	padding-block: var(--wp--preset--spacing--70);
}

.vallem-feature-band {
	padding-block: var(--wp--preset--spacing--70);
}

/* The inline padding is the page gutter, held here because the outer group is
   .alignfull and has had core's root padding cancelled by the negative margins
   that make it full width. Below the content line that gutter is the only thing
   keeping the plate off the edge of the screen. Same as .vallem-academy. */
.vallem-feature-box {
	padding-block: var(--wp--preset--spacing--70);
	padding-inline: var(--wp--preset--spacing--50);
}

.vallem-feature-duo {
	padding-block: var(--wp--preset--spacing--70);
}

.vallem-feature-tabs {
	padding-block: var(--wp--preset--spacing--70);
}

/* The media's negative block margin in the hero-split block names this same
   step by hand, because it has to swallow the bottom padding before it can
   bleed past the plate. Change the step here and change it there. */
.vallem-hero-split {
	padding-block: var(--wp--preset--spacing--70);
}

.vallem-howto {
	padding-block: var(--wp--preset--spacing--70);
}

.vallem-intro-split {
	padding-block: var(--wp--preset--spacing--70);
}

.vallem-logos {
	padding-block: var(--wp--preset--spacing--60);
}

/* The inline padding is the page gutter, for the same reason as
   .vallem-feature-box: the group is .alignfull, so core's root padding has been
   cancelled by the negative margins that make it full width, and below the
   content line this gutter is the only thing keeping the cards off the edge. */
.vallem-partner-cards {
	padding-block: var(--wp--preset--spacing--70);
	padding-inline: var(--wp--preset--spacing--50);
}

.vallem-process {
	padding-block: var(--wp--preset--spacing--80);
}

.vallem-proof {
	padding-block: var(--wp--preset--spacing--80);
}

.vallem-reassurance {
	padding-top: var(--wp--preset--spacing--70);
	padding-bottom: var(--wp--preset--spacing--80);
}

.vallem-reviews {
	padding-block: var(--wp--preset--spacing--70);
}

.vallem-signup {
	padding-block: var(--wp--preset--spacing--70);
}

.vallem-statement {
	padding-block: var(--wp--preset--spacing--70);
}

/* Hero, home. One-off section, see patterns/hero-home.php */

/* On the content line. Capped here rather than through wideSize, because
   wideSize also caps fluid typography and moving it would be a global type
   change smuggled in as a layout fix. Three classes are needed to outrank
   core's `.has-global-padding > .alignfull` negative margins, which would
   otherwise pin this to the left edge.

   No inline padding. The plate edge is the line, so the gradient starts where
   every section's text starts. The padding this replaces was 32px below 1440
   and zero above it, which made the plate jump 64px wider across that one
   pixel of viewport. */
.wp-block-group.vallem-hero.alignfull {
	max-width: var(--vallem-content-line);
	margin-inline: auto;
}

.vallem-hero__pill-divider {
	border-left: 1px solid rgb(255 255 255 / 20%);
	padding-left: 12px;
}

.vallem-hero__pill-divider a {
	color: inherit;
	text-decoration: none;
}

.vallem-hero__media {
	position: relative;
	overflow: hidden;
}

.vallem-hero__photo,
.vallem-hero__card {
	margin: 0;
}

.vallem-hero__photo img {
	display: block;
	width: 100%;
}

/* Overlaps the photo and is clipped by the media group, as in the design.
   The asset is a transparent composition of three plates that carry their own
   shadows and rounded corners, so there is no frame here: a border and radius
   would draw a box around the empty space between the plates. The bottom plate
   runs past the photo and the media group's overflow cuts it. */
.vallem-hero__card {
	position: absolute;
	top: 22%;
	left: 3%;
	width: 44%;
}

.vallem-hero__card img {
	display: block;
	width: 100%;
	height: auto;
}

/* Mobile. At 390px the root gutter plus the plate's own 40px padding left a
   262px column, so the photo rendered 242 by 77. The hero becomes the page
   here: full bleed, 24px gutter, photo to both edges. Copy goes to 342px, the
   photo to 370 by 168. The !importants all answer inline styles on the blocks
   or core's layout rules, neither of which a stylesheet can reach otherwise. */
@media (width <= 781px) {

	/* Three classes to outrank core's `.has-global-padding > .alignfull`, as
	   above. The radius goes too, it only reads as a card away from the edge. */
	.wp-block-group.vallem-hero.alignfull {
		max-width: none;
		margin-inline: calc(-1 * var(--wp--style--root--padding-left))
			calc(-1 * var(--wp--style--root--padding-right));
	}

	.vallem-hero .vallem-dark-section {
		border-radius: 0 !important;
		padding-inline: var(--wp--preset--spacing--30) !important;
	}

	/* The pill wraps to four lines under 640px. Published pages have no class on
	   it, so the second selector leans on it being the plate's first child. */
	.vallem-hero__pill,
	.vallem-hero .vallem-dark-section > .wp-block-group:first-child {
		display: none;
	}

	/* A hidden child still counts, so core's block gap would sit above the
	   heading that is now first. */
	.vallem-hero .vallem-dark-section > .wp-block-group:first-child + * {
		margin-block-start: 0;
	}

	/* Cancels the padding set above, so the two have to move together. */
	.wp-block-group.vallem-hero__media.alignwide {
		max-width: none;
		margin-inline: calc(-1 * var(--wp--preset--spacing--30)) !important;
	}

	/* 3.15 is a desktop crop, 117px tall once the photo is full width, which
	   leaves the card nothing to sit on. 1.5 gives it the room. */
	.vallem-hero__photo img {
		aspect-ratio: 1.5 !important;
	}

	/* Anchored to the bottom instead of 22% down, so the clip at the bottom edge
	   stays the same few pixels whatever the photo height. The clamp is what
	   keeps it honest across the range: 78% is right on a phone and far too much
	   by 781px, where 26rem lands it near the desktop proportion. */
	.vallem-hero__card {
		top: auto;
		bottom: -3%;
		left: 4%;
		width: clamp(15rem, 78%, 26rem);
	}
}

/* ------------------------------------------------------------------------- *
 * Form confirmation. Theme wide.
 *
 * After a form goes through, Jetpack replaces it with a summary of what was
 * submitted. That summary is a sibling of the form inside
 * .jetpack-contact-form-container rather than a child of it, which is what the
 * scoping here is for.
 *
 * Only the rhythm is touched. Jetpack ships 24px of padding on every row and
 * 8px between a label and its value, which reads as a list of separate cards
 * rather than as one receipt. Its own rules sit at two classes, so these carry
 * three instead of relying on which stylesheet happens to load last.
 *
 * The heading above this is set in functions.php, not here: it is an attribute,
 * not a style.
 * ------------------------------------------------------------------------- */

.jetpack-contact-form-container .contact-form-submission .jetpack_forms_contact-form-success-summary {
	padding-block: 12px;
}

.jetpack-contact-form-container .contact-form-submission .field-name-wrapper {
	margin-block-end: 2px;
}

/* ------------------------------------------------------------------------- *
 * Hero, partners. See patterns/hero-partners.php
 *
 * Three children on one grid cell row: the plate, the photo, and the card over
 * the seam between them. Overlap is done with grid areas rather than absolute
 * positioning, so the tallest of the three sets the height of the section and
 * nothing has to be measured.
 *
 * 62/38 is a ratio, not the content line, because snapping the seam to the
 * 1344px line would leave the photo only as wide as the page gutter.
 * ------------------------------------------------------------------------- */

.vallem-partner-hero {
	/* Where the copy starts and the card ends: the content line, or the page
	   gutter once the viewport is narrower than it. Measured against 100vw
	   because the plate is 62% of the viewport, not the full width of it, so a
	   percentage here would resolve against the wrong box. */
	--vallem-partner-hero-inset: max(
		var(--wp--preset--spacing--50),
		calc((100vw - var(--vallem-content-line)) / 2)
	);

	display: grid;
	grid-template-columns: 62fr 38fr;
	align-items: stretch;

	/* Holds its shape before anyone has filled it in, scaled to the window. svh
	   so browser chrome sliding away does not resize it. Dimensions in the
	   editor still wins over this, because block spacing serialises inline. */
	min-block-size: 80vh;
	min-block-size: 80svh;
}

.vallem-partner-hero__panel {
	grid-area: 1 / 1 / 2 / 2;
	display: flex;
	align-items: center;
	padding-block: var(--wp--preset--spacing--80);
	padding-inline: var(--vallem-partner-hero-inset) var(--wp--preset--spacing--60);
}

/* Short enough to hold the heading to three lines at the top of the scale. */
.vallem-partner-hero__copy {
	max-width: 34rem;
}

/* Tinted so the column still reads as a place for a photo while it is empty,
   rather than as a gap the plate stops short of. */
.vallem-partner-hero__media {
	grid-area: 1 / 2 / 2 / 3;
	align-self: stretch;
	margin: 0;
	overflow: hidden;
	background-color: var(--wp--preset--color--surface);
}

/* Out of flow, so the photo fills the column instead of sizing it. A percentage
   height cannot resolve while the row is still being measured, so an in-flow img
   falls back to its own aspect ratio and a portrait shot makes the section as
   tall as itself. Absolute takes it out of that measurement: the section decides
   the height, then cover crops to fit. */
.vallem-partner-hero__media {
	position: relative;
	display: block;
}

.vallem-partner-hero__media img {
	position: absolute;
	inset: 0;
	display: block;
	inline-size: 100%;
	block-size: 100%;
	object-fit: cover;
}

/* Spans both columns so it can sit over the seam, then is pinned to the content
   line on the right. The block margin is what keeps air above and below it once
   the form is taller than the copy. */
.vallem-partner-hero__card {
	grid-area: 1 / 1 / 2 / 3;
	align-self: center;
	justify-self: end;
	inline-size: min(40rem, calc(100vw - 2 * var(--wp--preset--spacing--50)));
	margin-block: var(--wp--preset--spacing--60);
	margin-inline-end: var(--vallem-partner-hero-inset);
	box-shadow: 0 1px 2px rgb(7 26 58 / 8%), 0 24px 48px rgb(7 26 58 / 16%);
}

/* ---- The form ----------------------------------------------------------- *
 *
 * Shared with the event lead form and with the demo section, which is why every
 * selector below is scoped to all three section classes rather than to the hero
 * alone. See the lead capture section further down for the pieces that are only
 * that form's, and patterns/section-lead-capture.php and
 * patterns/section-demo-cta.php for what they are. :is() takes the highest
 * specificity of its arguments and all three are single classes, so adding a
 * scope changes no weights.
 *
 * Styled through Jetpack's own custom properties rather than by out-specifying
 * its rules. Its field CSS is written with :where(), so it carries no
 * specificity and there is nothing there to fight. What it does instead is
 * compute the whole property set from the form block's attributes and write it
 * as an inline style on a .jetpack-contact-form-container div that it wraps the
 * form in. An inline style beats any stylesheet rule, so these have to be
 * declared on an element inside that container. Declared on the card they are
 * inherited past and silently ignored.
 *
 * The alternative is to set the matching attributes on the form block in the
 * pattern, borderRadius and borderColor and fieldBackgroundColor and the rest,
 * and let Jetpack write them into that inline style itself. Those attributes
 * take literal numbers and hex, so none of the theme's presets could be
 * referenced. That is why the look lives here instead.
 *
 * The written rules are keyed on .grunion-field, which Jetpack adds to every
 * control unconditionally (class-contact-form-field.php, render_field): the
 * text inputs, the select's wrapper div and the phone field's wrapper alike.
 * wp-block-jetpack-input looks like the better hook on its own and is not, it
 * is only added once a field has migrated to its inner blocks, so it is missing
 * from a freshly inserted pattern. It is matched alongside because the editor
 * canvas is React, not Jetpack's PHP: grunion-field does not exist there, and
 * add_editor_style() puts this file in front of both.
 *
 * Do not try to frame the <select> itself. Jetpack zeroes its border, its
 * border width and its background with !important, deliberately, because the
 * frame belongs on the wrapper.
 * ------------------------------------------------------------------------- */

:is(.vallem-partner-hero, .vallem-lead-capture, .vallem-demo-cta, .vallem-contact) .wp-block-jetpack-contact-form {
	/* Tighter than the theme's step scale. A form is the one place on the site
	   where the rhythm has to give: seven controls in a hero card, twenty in a
	   lead form, and the step scale would run either of them off the screen. */
	--vallem-form-field-gap: 0.875rem;

	/* One box, used by every control, so heights match by construction. */
	--vallem-form-field-padding: 9px 12px;
	--vallem-form-field-radius: 6px;

	--jetpack--contact-form--border: 1px solid var(--wp--preset--color--border);
	--jetpack--contact-form--border-color: var(--wp--preset--color--border);
	--jetpack--contact-form--border-radius: var(--vallem-form-field-radius);
	--jetpack--contact-form--border-size: 1px;
	--jetpack--contact-form--border-style: solid;
	--jetpack--contact-form--font-family: inherit;
	--jetpack--contact-form--font-size: var(--wp--preset--font-size--medium);
	--jetpack--contact-form--input-background: var(--wp--preset--color--base);
	--jetpack--contact-form--input-height: auto;
	--jetpack--contact-form--input-padding: var(--vallem-form-field-padding);
	--jetpack--contact-form--input-padding-left: 12px;
	--jetpack--contact-form--input-padding-top: 9px;
	--jetpack--contact-form--line-height: 1.5;
	--jetpack--contact-form--text-color: var(--wp--preset--color--contrast);

	/* Core's generated flex layout for this block sits at one class, so the gap
	   needs two to win. */
	display: flex;
	flex-wrap: wrap;
	gap: var(--vallem-form-field-gap);
}

:is(.vallem-partner-hero, .vallem-lead-capture, .vallem-demo-cta, .vallem-contact) .wp-block-jetpack-contact-form > * {
	min-inline-size: 0;
	margin-block: 0;
}

/* Full width by default. A field set to half width in the editor is skipped
   here so it can take its basis from Jetpack's own width-*-wrap rules. */
:is(.vallem-partner-hero, .vallem-lead-capture, .vallem-demo-cta, .vallem-contact) .wp-block-jetpack-contact-form > :not([class*="grunion-field-width-"]) {
	flex: 1 1 100%;
}

/* Each field is wrapped in a div of its own carrying a block margin, which
   would otherwise stack on top of the flex gap. */
:is(.vallem-partner-hero, .vallem-lead-capture, .vallem-demo-cta, .vallem-contact) .wp-block-jetpack-contact-form div {
	margin-block: 0;
}

/* The box. Checkboxes and radios opt out, they carry grunion-field too. */
:is(.vallem-partner-hero, .vallem-lead-capture, .vallem-demo-cta, .vallem-contact) :is(.grunion-field, .wp-block-jetpack-input):not([type="checkbox"], [type="radio"]) {
	box-sizing: border-box;
	inline-size: 100%;
	margin: 0;
	border: 1px solid var(--wp--preset--color--border);
	border-radius: var(--vallem-form-field-radius);
	background-color: var(--wp--preset--color--base);
	color: var(--wp--preset--color--contrast);
	font-family: inherit;
	font-size: var(--wp--preset--font-size--medium);
	line-height: 1.5;
}

/* Padding goes on whatever holds the text, not on every framed box. The select
   sits inside its wrapper and Jetpack pads it from the input-padding property
   above, so padding the wrapper as well would count it twice. */
:is(.vallem-partner-hero, .vallem-lead-capture, .vallem-demo-cta, .vallem-contact) :is(input, textarea):is(.grunion-field, .wp-block-jetpack-input) {
	padding: var(--vallem-form-field-padding);
}

:is(.vallem-partner-hero, .vallem-lead-capture, .vallem-demo-cta, .vallem-contact) .wp-block-jetpack-contact-form .contact-form__select-wrapper {
	padding: 0;
}

/* Focus lands on whatever is framed: the control itself, or its wrapper when
   the control sits inside one. */
:is(.vallem-partner-hero, .vallem-lead-capture, .vallem-demo-cta, .vallem-contact) :is(.grunion-field, .wp-block-jetpack-input):not([type="checkbox"], [type="radio"]):focus-visible,
:is(.vallem-partner-hero, .vallem-lead-capture, .vallem-demo-cta, .vallem-contact) :is(.grunion-field, .wp-block-jetpack-input):has(:focus-visible) {
	border-color: var(--wp--preset--color--primary);
	outline: 2px solid rgb(24 60 221 / 20%);
	outline-offset: 1px;
}

/* Labels. The pattern carries no styling of its own, so this is the one place
   a form label is described.

   Black, not the muted grey this was. Nol on 2026-09-07, looking at the demo
   form. It is the shared rule, so this moves the partner hero and the lead
   capture form with it: one label colour on the site rather than a form that
   disagrees with the form next to it. Scope it back to .vallem-demo-cta if the
   other two are meant to stay grey. */
:is(.vallem-partner-hero, .vallem-lead-capture, .vallem-demo-cta, .vallem-contact) :is(.grunion-field-label, .wp-block-jetpack-label) {
	display: block;
	margin-block-end: 6px;
	color: var(--wp--preset--color--contrast);
	font-size: var(--wp--preset--font-size--small);
	font-weight: 500;
}

/* Consent is one row of small print, not a field with a label over it. */
:is(.vallem-partner-hero, .vallem-lead-capture, .vallem-demo-cta, .vallem-contact) .grunion-field-label.consent {
	display: flex;
	gap: 8px;
	align-items: flex-start;
	margin-block-end: 0;
	font-weight: 400;
	line-height: 1.45;
}

/* Jetpack sizes the submit from its own input height and vertical padding,
   which leaves it a different shape from every other button on the site. Put
   it back on the theme's own button metrics, full width as on the contact
   page. */
:is(.vallem-partner-hero__card, .vallem-lead-capture__card, .vallem-demo-cta__card, .vallem-contact__card) .wp-block-jetpack-contact-form :is(.wp-block-button, .wp-block-jetpack-button) .wp-block-button__link {
	inline-size: 100%;
	min-block-size: 0;
	padding-block: var(--wp--custom--button-padding-block);
}

/* Stacked. The photo leads, the plate carries the copy, and the card lifts over
   the bottom edge of the copy so the two still read as one object. */
@media (width < 900px) {
	.vallem-partner-hero {
		grid-template-columns: 1fr;
		min-block-size: 0;
	}

	.vallem-partner-hero__panel {
		grid-area: auto;
		padding-block: var(--wp--preset--spacing--70);
		padding-inline: var(--wp--preset--spacing--50);
	}

	.vallem-partner-hero__media {
		grid-area: auto;
		order: -1;
		block-size: 13rem;
	}

	.vallem-partner-hero__card {
		grid-area: auto;
		justify-self: stretch;
		inline-size: auto;
		margin-block: calc(-1 * var(--wp--preset--spacing--60)) var(--wp--preset--spacing--60);
		margin-inline: var(--wp--preset--spacing--50);
	}
}

/* ------------------------------------------------------------------------- *
 * Sectie, leadformulier. See patterns/section-lead-capture.php
 *
 * The partner form's private twin: one card, centred, on a light section, for
 * a colleague to fill in on a phone at a stand. Everything about the fields
 * themselves is inherited from the shared block above; only the plate and the
 * controls the partner form does not have are described here.
 *
 * That is the whole reason this is a separate section rather than a second
 * pattern hung off the hero's CSS. The hero is a three-cell grid with a photo
 * and an overlap. This has none of that, and copying it to get the same fields
 * would have meant maintaining the field look in two places.
 * ------------------------------------------------------------------------- */

.vallem-lead-capture {
	padding-block: var(--wp--preset--spacing--70);
	padding-inline: var(--wp--preset--spacing--50);
}

/* The same plate and the same corner as the partner card, and the same shadow:
   the form is meant to be recognisably the one thing wearing two hats. There is
   no seam to float over here, so it is the only thing on the section and it
   carries a hairline as well, which a shadow alone does not give on canvas. */
.vallem-lead-capture__card {
	border: 1px solid var(--wp--preset--color--border);
	box-shadow: 0 1px 2px rgb(7 26 58 / 8%), 0 24px 48px rgb(7 26 58 / 16%);
}

/* ---- The grouped fields ------------------------------------------------- *
 *
 * Radios and checkbox groups, which the partner form has none of: its only
 * checkbox is the consent line, styled with the fields above. The demo form
 * uses one, and it is what draws the reference's row of interest chips.
 * Jetpack renders each group as
 *
 *   fieldset.jetpack-field-multiple__fieldset
 *     legend.grunion-field-label
 *     div.grunion-radio-options | div.grunion-checkbox-multiple-options
 *       label.contact-form-field > input + span.grunion-*-label
 *
 * so the legend is already carrying the same class the field labels do and
 * needs nothing here.
 *
 * Each option is a box on the same border, corner and padding as a text field,
 * which is what makes a group of them read as part of the same form rather than
 * as a bare list, and gives a thumb something to hit. The whole box is inside
 * the <label>, so the hit area is the box and not the 13px control.
 *
 * Everything here is scoped through .wp-block-jetpack-contact-form for one
 * reason: unlike the field rules above, Jetpack does not write this part with
 * :where(). grunion.scss stacks the options with a real two-class rule
 *
 *   .contact-form .grunion-radio-options { display: flex; flex-direction: column }
 *
 * so a two-class rule here only ties with it and loses on load order, which is
 * how these shipped as a vertical list. Three classes wins outright, whichever
 * stylesheet the page happens to load last.
 *
 * What is deliberately not fought: the control itself. Jetpack draws its own
 * radio and checkbox with appearance:none at three classes and a type, spaces
 * it from the text with a margin, and nudges it onto the text baseline. It is
 * a good control and it already follows currentColor, so there is no gap and
 * no align-items here. Adding either would double the spacing it already sets.
 * ------------------------------------------------------------------------- */

:is(.vallem-lead-capture, .vallem-demo-cta) .wp-block-jetpack-contact-form .jetpack-field-multiple__fieldset {
	margin: 0;
	border: 0;
	padding: 0;

	/* A fieldset ignores width: 100% from its parent without this. */
	min-inline-size: 0;
}

/* A row that wraps, not a column. flex-direction is named rather than left to
   the initial value because it is Jetpack's `column` being overridden. */
:is(.vallem-lead-capture, .vallem-demo-cta) .wp-block-jetpack-contact-form :is(.grunion-radio-options, .grunion-checkbox-multiple-options) {
	display: flex;
	flex-direction: row;
	flex-wrap: wrap;

	/* Boxes on one line match height when a long option wraps to two lines. */
	align-items: stretch;
	gap: 6px;
}

:is(.vallem-lead-capture, .vallem-demo-cta) .wp-block-jetpack-contact-form :is(.grunion-radio-options, .grunion-checkbox-multiple-options) > label {
	border: 1px solid var(--wp--preset--color--border);
	border-radius: var(--vallem-form-field-radius);
	padding: var(--vallem-form-field-padding);
	background-color: var(--wp--preset--color--base);
	font-size: var(--wp--preset--font-size--small);
	line-height: 1.4;
	cursor: pointer;
}

/* The checked state is on the box, not just on the control, so a filled-in
   group can be read at arm's length. :has() rather than a sibling selector
   because the input is inside the label, not before it. */
:is(.vallem-lead-capture, .vallem-demo-cta) .wp-block-jetpack-contact-form :is(.grunion-radio-options, .grunion-checkbox-multiple-options) > label:has(:checked) {
	border-color: var(--wp--preset--color--primary);
	background-color: var(--wp--preset--color--accent-surface);
}

/* Focus has to land on the box too: the control inside it is what takes the
   keyboard, and its own ring would be hidden behind the box's own border. */
:is(.vallem-lead-capture, .vallem-demo-cta) .wp-block-jetpack-contact-form :is(.grunion-radio-options, .grunion-checkbox-multiple-options) > label:has(:focus-visible) {
	outline: 2px solid rgb(24 60 221 / 20%);
	outline-offset: 1px;
}

/* The control keeps its own size whatever the box does. */
:is(.vallem-lead-capture, .vallem-demo-cta) .wp-block-jetpack-contact-form :is(.grunion-radio-options, .grunion-checkbox-multiple-options) input {
	flex: none;
}

/* ---- The row ------------------------------------------------------------ *
 *
 * The shared block near the top of this file makes the form a wrapping row at
 * two classes, which is enough for the partner hero because every field there
 * is full width anyway. This form pairs fields, so it has to survive both of
 * the layouts Jetpack can end up in, and neither of them is kind about it:
 *
 *   .wp-block-jetpack-contact-form:not(.is-layout-flex) > *  { flex: 0 0 100% }
 *
 * is Jetpack's fallback for a form core did not stamp a layout class onto. It
 * sits at three classes, so it beats Jetpack's own width-50 rule at two, and a
 * paired field ends up with a 100% basis capped by a 50% max-width: half as
 * wide as it should be and alone on its row. The same rule set the gap to 24px
 * over the 14px the shared block asks for.
 *
 * The other branch is core's. Where core does stamp the layout on, it generates
 * `flex-direction: column` from the block's own vertical layout attribute, at
 * one class. The shared block never names a direction, so that column would win
 * by default and stack the pairs just the same, for an unrelated reason.
 *
 * So: name the direction, and out-specify the fallback. Three classes for the
 * container and four for the children, because the theme stylesheet is enqueued
 * before Jetpack's and loses every tie.
 *
 * Scoped to this form on purpose. The partner hero is single column, where all
 * of this is invisible, and its markup is already copied into whatever pages
 * were built from it.
 * ------------------------------------------------------------------------- */

.vallem-lead-capture .contact-form .wp-block-jetpack-contact-form {
	display: flex;
	flex-direction: row;
	flex-wrap: wrap;
	gap: var(--vallem-form-field-gap);
}

/* `auto` rather than a width: the basis falls back to the width Jetpack already
   computed for whichever grunion-field-width-* class the field carries, so the
   25/33/50/75 table stays Jetpack's business and is not copied here. */
.vallem-lead-capture .contact-form .wp-block-jetpack-contact-form > [class*="grunion-field-width-"] {
	flex: 1 1 auto;
}

.vallem-lead-capture .contact-form .wp-block-jetpack-contact-form > :not([class*="grunion-field-width-"]) {
	flex: 1 1 100%;
}

/* ---- The rest ----------------------------------------------------------- */

/* Group headings. core/heading is on Jetpack's own allowed list inside a form
   block and survives to the front end untouched: the form's content is run
   through do_blocks() and then do_shortcode(), and only the field shortcodes
   are rewritten. A rule above the words rather than a size or a weight, so
   four of them down a long form divide it without shouting. */
.vallem-lead-form__legend {
	margin-block: var(--wp--preset--spacing--20) 0;
	border-top: 1px solid var(--wp--preset--color--border);
	padding-block-start: var(--wp--preset--spacing--20);
	color: var(--wp--preset--color--contrast-muted);
	font-weight: 600;
}

/* Help text. Jetpack emits it as spans in a div rather than as paragraphs,
   deliberately, so nothing of the theme's is already sizing it. Three classes
   because the rule in the shared block above zeroes the block margin on every
   div inside the form, and two classes plus a type would lose to it. */
:is(.vallem-lead-capture, .vallem-demo-cta) .wp-block-jetpack-contact-form .contact-form__field-hints {
	margin-block-start: 4px;
	color: var(--wp--preset--color--contrast-muted);
	font-size: var(--wp--preset--font-size--small);
	line-height: 1.4;
}

/* Room to write. Jetpack's default is short enough to hide a two-line note.
   Shared with the contact form, which has a message box and the same problem. */
:is(.vallem-lead-capture, .vallem-contact) textarea:is(.grunion-field, .wp-block-jetpack-input) {
	min-block-size: 7rem;
}

/* One column. Two 50% fields in a 320px card leave nothing to type into, and
   the sub-second saving of a pair per row is not worth it. Jetpack's width
   rules sit at two classes, so this needs three. :is() takes the specificity of
   its most specific argument, one class, so adding the contact form to it costs
   nothing.

   Shared with the contact form, whose voornaam/achternaam pair is the same
   arrangement. */
@media (width < 600px) {
	:is(.vallem-lead-capture, .vallem-contact) .wp-block-jetpack-contact-form > [class*="grunion-field-width-"] {
		flex-basis: 100%;
		inline-size: 100%;
		max-inline-size: 100%;
	}
}

/* ------------------------------------------------------------------------- *
 * WarGames band.
 *
 * The film's closing line, blue on near-black, in a dot-matrix terminal face.
 * A phosphor bloom, faint scanlines, and a one-pixel red/cyan split. The split
 * is the part that reads as tape rather than as screen, and one pixel is where
 * it stops looking like a rendering fault. Nothing here moves.
 *
 * The scanlines go over the whole band rather than over the type, so the ground
 * is textured too and it reads as a screen instead of as a font effect. They sit
 * in ::after with no help needed to paint on top: the text is in normal flow and
 * unpositioned, so a positioned pseudo-element is already above it.
 * ------------------------------------------------------------------------- */

.vallem-wargames {
	position: relative;
	overflow: hidden;

	/* Half of the 80 step, which is the 60 step: same 2.5rem and 4rem ends,
	   only the fluid slope differs. Taking the preset rather than halving the
	   clamp by hand keeps the band on the theme's scale. */
	padding-block: var(--wp--preset--spacing--60);
}

.vallem-wargames::after {
	content: "";
	position: absolute;
	inset: 0;
	pointer-events: none;
	background: repeating-linear-gradient(
		to bottom,
		rgb(0 0 0 / 0) 0,
		rgb(0 0 0 / 0) 2px,
		rgb(0 0 0 / 22%) 3px,
		rgb(0 0 0 / 22%) 4px
	);
}

.vallem-wargames .vallem-wargames__verdict,
.vallem-wargames .vallem-wargames__line {
	margin-block: 0;

	/* One size for both. The statement used to be the larger of the two and it
	   overpowered the form above it; at the verdict's size the section reads as
	   a readout rather than as a billboard. */
	font-size: clamp(1.25rem, 3.4vw, 2.75rem);

	font-weight: 400;
	line-height: 1.28;
	letter-spacing: 0.1em;

	/* Uppercased here rather than in the pattern, so the text stays readable in
	   the editor and nobody has to shout in the field. */
	text-transform: uppercase;

	text-shadow:
		-1px 0 0 rgb(255 40 90 / 34%),
		1px 0 0 rgb(60 255 240 / 26%),
		0 0 26px rgb(93 204 255 / 34%);
}

/* Left, not centred. Core's constrained layout sets margin-left and
   margin-right to auto with !important on every child of the group
   (.is-layout-constrained > :where(:not(.alignleft):not(.alignright):not(.alignfull))),
   which turns the narrow max-width below into centring. Nothing but !important
   beats !important, so this is one of the few places it is the right answer
   rather than a shortcut.

   Zeroing the start margin is not enough either. Core sizes and centres every
   child independently, so there is no shared content box to sit against: a
   zeroed margin puts this line at the section's padding edge while the verdict
   above it still starts at the content line. The inset is therefore computed,
   and max() collapses it to zero once the viewport is narrower than the content
   line. Both measured against the rendered page, not reasoned about.

   The max-width breaks the line after MOVE at every width, as it does in the
   film, without a hard <br> that would strand a word on a phone. Both halves
   measure 10.95em in this face at this tracking, measured rather than guessed,
   so the box is set just above that. In em and not ch: ch is the advance of "0"
   and knows nothing about the 0.1em of tracking added on top of every
   character, which is what makes a 20ch box break a 21-character line early. */
.vallem-wargames .vallem-wargames__line {
	max-width: 11.4em;
	margin-inline: max(0px, calc((100% - var(--vallem-content-line)) / 2)) auto !important;
	margin-block-start: clamp(1.5rem, 3vw, 2.5rem);
}

/* The pivot. Core's own inline colour format supplies the colour, from a
   palette slug rather than a hex, so it stays editable from the toolbar. The
   rule under it is drawn here because it is not part of the glyphs: in the film
   the underline runs continuously beneath the text, including under the spaces,
   and that is what makes it read as a readout rather than as set type. */
.vallem-wargames .vallem-wargames__line mark {
	background: none;
	text-decoration: underline;
	text-decoration-thickness: 0.05em;
	text-underline-offset: 0.16em;
}

/* ------------------------------------------------------------------------- *
 * Logo row.
 *
 * Logo heights are written by assets/js/logo-row.js, which normalises them
 * optically rather than to a common height. See that file for the maths.
 *
 * Selectors carry two classes throughout. Core's generated layout class sits at
 * one class, so a single-class rule loses the grid and gap fight.
 * ------------------------------------------------------------------------- */

.vallem-logos {
	--vallem-logo-rest: 1;              /* rest dimming, per scheme */
	/* How far a character travels to swap places with its own second copy. The
	   line-height, the shadow offset and the hover transform all read this, so
	   they cannot drift apart. */
	--vallem-logo-roll: 1.35em;
}

/* Registered in functions.php so the scheme is chosen in the Styles panel
   instead of being baked into the markup. */
.vallem-logos.is-style-logos-dark {
	background-color: var(--wp--preset--color--contrast);
	color: #fff;
	--vallem-logo-rest: 0.76;           /* pure white on near-black is harsh */
}

.vallem-logos .vallem-logos__inner {
	position: relative;
}

.vallem-logos .vallem-logos__row {
	align-items: center;
}

/* Nothing animates on the figure. It stays an unfiltered, untransformed flex
   container so the row composites as one layer per logo, not two. */
.vallem-logos .vallem-logos__item {
	display: flex;
	align-items: center;
	justify-content: center;
	min-width: 0;
	margin: 0;
}

.vallem-logos .vallem-logos__item img {
	--vallem-logo-blur: 0px;
	--vallem-logo-fade: 1;

	display: block;
	width: auto;
	max-width: 100%;
	height: auto;

	/* JS sets width and height together. Without this, max-width clamping a box
	   that has an explicit height squashes the logo instead of scaling it. */
	object-fit: contain;

	/* Both halves of the hover animate on this one element, the fade here and
	   the blur in the chain below. Splitting them across the figure and the
	   image composites two layers and draws a visible edge around every logo.
	   The fade is a multiplier rather than a value JS writes outright, so the
	   scheme keeps ownership of how far down the logos sit at rest. */
	opacity: calc(var(--vallem-logo-rest) * var(--vallem-logo-fade));

	/* One ink for the whole row, so it reads as a single material and dark mode
	   needs no second set of files. Knockouts must be transparent holes: a white
	   shape flattens to black here and the mark becomes a solid block.
	   Add the class vallem-logos--colour in the block's Advanced panel to keep
	   the logos in their own colours.

	   The hover blur is part of this same chain rather than a filter put on for
	   the duration of the animation. Adding a filter promotes the element to its
	   own layer and removing it drops it back, so the logos snapped as the blur
	   finished. The chain here is always present, and only the radius animates. */
	filter: grayscale(1) brightness(0) blur(var(--vallem-logo-blur));
}

.vallem-logos.is-style-logos-dark .vallem-logos__item img {
	filter: grayscale(1) brightness(0) invert(1) blur(var(--vallem-logo-blur));
}

.vallem-logos.vallem-logos--colour .vallem-logos__item img {
	filter: blur(var(--vallem-logo-blur));
}

/* --- The hover copy ------------------------------------------------------ */

.vallem-logos .vallem-logos__caption {
	margin: clamp(1.75rem, 4vw, 2.5rem) 0 0;
}

.vallem-logos .vallem-logos__caption a {
	color: inherit;
	text-decoration: none;
	font-weight: 500;
	letter-spacing: -0.014em;
	line-height: var(--vallem-logo-roll);

	/* A 20px reach around the copy, so it is comfortable to hit rather than a
	   one-line-tall target. Also what makes it a usable tap target on touch,
	   where the line is permanently visible. */
	padding: 20px;

	/* No halo behind the glyphs any more. The roll needs each character to clip
	   its own second copy, and that clip would slice a 26px glow into a visible
	   rectangle around every letter. The logos sit at 0.36 opacity behind blur,
	   so the copy still reads without one. */
}

.vallem-logos .vallem-logos__w {
	display: inline-block;
	white-space: nowrap;
}

/* Inline boxes cannot be transformed, so each character needs its own block.
   No will-change anywhere: it rasterises the span into a tile the size of its own
   box, which shears the overhang off letters like the f and cuts the descenders
   off the j and the g.

   The clip here is what hides the second copy of each character until it rolls
   up. It is the one thing allowed to constrain a glyph, and only vertically:
   the box is exactly one roll tall, which is enough for Inter's ascender and
   descender together with a little to spare. */
.vallem-logos .vallem-logos__c {
	display: inline-block;
	overflow: hidden;
	vertical-align: bottom;
}

/* The roll. A second copy of the character sits one line below, drawn as a
   shadow rather than as more markup, and hover slides the pair up so the copy
   lands where the original was. The hair of rotation is what keeps the two
   copies rasterising identically mid-transition. */
.vallem-logos .vallem-logos__cr {
	display: inline-block;
	transform: translateY(0) rotate(0.001deg);
	transition: transform 600ms cubic-bezier(0.625, 0.05, 0, 1);
	text-shadow: 0 var(--vallem-logo-roll) currentColor;
}

.vallem-logos .vallem-logos__caption a:hover .vallem-logos__cr,
.vallem-logos .vallem-logos__caption a:focus-visible .vallem-logos__cr {
	transform: translateY(calc(-1 * var(--vallem-logo-roll))) rotate(0.001deg);
}

@media (prefers-reduced-motion: reduce) {
	.vallem-logos .vallem-logos__cr {
		transition: none;
	}
}

/* The line only lifts out of the flow where there is a real pointer and once JS
   has taken over. Without either it stays under the row, which is the honest
   fallback: a hover-only message does not exist on a phone. The class lands
   before the logos load, or the caption visibly jumps from under the row into
   the middle on every cold load. */
@media (hover: hover) and (pointer: fine) {
	/* Give the hover a run-up. The trigger reaches past the logos vertically so
	   the animation is already moving by the time the pointer arrives, instead of
	   firing the instant it touches a logo. Absolutely positioned, so it costs
	   nothing in layout, and hit-testing a pseudo-element targets the element it
	   belongs to, so this still fires __inner's pointerenter.

	   It paints before __inner's children, so it can never sit over the link.
	   It does overshoot the section's own padding by roughly 16px at desktop;
	   drop the reach to 64px if that matters. */
	.vallem-logos {
		--vallem-logo-reach: 80px;
	}

	.vallem-logos.is-enhanced .vallem-logos__inner::before {
		content: "";
		position: absolute;
		inset: calc(-1 * var(--vallem-logo-reach)) 0;
	}

	.vallem-logos.is-enhanced .vallem-logos__caption {
		position: absolute;
		inset: 0;
		display: grid;

		/* Both, and they do different jobs. The link is taller than the logo row
		   it is laid over, its own padding sees to that, so the single row track
		   this grid creates does not fit the box. A track that does not fit is
		   laid out from the top and overflows the bottom, and place-items only
		   centres the link inside that track, which is why the copy used to sit
		   under the logos instead of across them. align-content centres the
		   track itself. */
		align-content: center;
		place-items: center;

		margin: 0;
		pointer-events: none;
	}

	.vallem-logos.is-enhanced .vallem-logos__caption a {
		pointer-events: auto;
		max-width: min(640px, 100%);
	}
}

/* Below one row, wrap and centre. A fixed column count strands the remainder
   against the left edge, and with seven logos there is always a remainder. */
@media (width <= 900px) {
	.vallem-logos .vallem-logos__row {
		display: flex;
		flex-wrap: wrap;
		justify-content: center;
		column-gap: clamp(1.75rem, 6vw, 3rem);
		row-gap: clamp(1.75rem, 5vw, 2.5rem);
	}

	.vallem-logos .vallem-logos__item {
		flex: 0 0 auto;
	}
}

/* ------------------------------------------------------------------------- *
 * Academy band. Herbruikbare sectie, zie patterns/section-academy.php
 *
 * Nothing in here sets a colour. The band paints itself from preset attributes
 * on the block, so the editor's colour panel owns the background and the text
 * and a preset class would beat anything written here anyway. What is left is
 * the cap, the column split and the corner on the picture.
 * ------------------------------------------------------------------------- */

/* A stripe across the page, not a plate on it: the colour runs edge to edge
   with no corner, which is what the current site does.

   Nothing is needed here to get that. It used to say `margin-inline: 0`, which
   at (0,3,0) beat core's (0,2,0) `.has-global-padding > .alignfull` and
   cancelled the very negative margins that pull an alignfull element out of the
   root gutter, quietly turning the stripe into an inset plate: 1376px wide at a
   1440 viewport, which looks deliberate, and an 1856px slab with 32px of page
   showing down each side at 1920, which does not. `max-width: none` was a no-op
   too; core already exempts .alignfull from the constrained cap.

   The cap is on the band below, not on the stripe. That is the only difference
   between a stripe and a plate, and keeping it in one place is what stops the
   two being confused. */

.vallem-academy .vallem-academy__band {
	max-width: var(--vallem-content-line);
	margin-inline: auto;
}

/* Stacked first, side by side later. Written this way round because the
   stacked case is the one that has to be right without a media query if
   anything about the band changes. */
.vallem-academy .vallem-academy__copy {
	flex: 1 1 100%;

	/* A flex item will not shrink below its longest word without this, which is
	   how a single long Dutch compound pushes the picture out of the band. */
	min-width: 0;
}

/* Two classes: core's own `.wp-block-image` carries a bottom margin at one
   class, which would drop the picture below the centre line of the row. */
.vallem-academy .vallem-academy__media {
	flex: 0 1 100%;
	min-width: 0;
	margin: 0;
}

.vallem-academy .vallem-academy__media img {
	display: block;
	width: 100%;

	/* A default, not a lock. The border control on the image block lands as an
	   inline style and still wins. */
	border-radius: 8px;
}

/* The copy needs roughly its own line before two columns are worth having, and
   that lands near where the old site flipped as well. The basis pair is the
   old 1000/600 split scaled to this band: the copy grows into the slack, the
   picture keeps its width until there is not enough room for both. */
@media (width >= 1024px) {
	.vallem-academy .vallem-academy__copy {
		flex: 1 1 26rem;
	}

	.vallem-academy .vallem-academy__media {
		flex: 0 1 34rem;
	}
}

/* ------------------------------------------------------------------------- *
 * Process. Section of steps, see patterns/section-process.php
 *
 * The row of cards descends: each one reaches further down than the one before
 * it. Everything here is written so that nothing knows how many cards there
 * are, because the pattern ships three and the whole point is that someone can
 * duplicate one to make a fourth.
 * ------------------------------------------------------------------------- */

/* The heading wraps well before the section does. 46% of the row, which is what
   the old site used, is a percentage of a width that changes; this is the
   measure the type actually wants. */
.vallem-process__title {
	max-width: 620px;
}

/* The lead takes the site's own prose measure rather than a second opinion
   about one. */
.vallem-process__lead {
	max-width: var(--wp--style--global--content-size);
}

/* The cards butt against each other, so each one takes an equal share of the
   row whatever the count. Basis zero rather than a percentage: with min-width
   cleared they can shrink past their own content and the row never wraps by
   accident. */
.vallem-process__steps > * {
	--vallem-process-drop: 0;

	flex: 1 1 0;
	min-width: 0;

	/* The staircase. Each rule matches every card from its position onward, and
	   the last one that matches wins, so a card ends up holding its own index
	   without anything writing that index down. Card four gets 3 because it
	   matches n+2, n+3 and n+4 and the last of those is what sticks.

	   Five deep is enough for any row that still fits across a screen; past
	   that the drop simply stops growing, which is the right way to run out. */
	min-height: calc(14rem + var(--vallem-process-drop) * 4.5rem);
}

.vallem-process__steps > :nth-child(n + 2) {
	--vallem-process-drop: 1;
}

.vallem-process__steps > :nth-child(n + 3) {
	--vallem-process-drop: 2;
}

.vallem-process__steps > :nth-child(n + 4) {
	--vallem-process-drop: 3;
}

.vallem-process__steps > :nth-child(n + 5) {
	--vallem-process-drop: 4;
}

/* Positioned so the emblem has something to hang off, and clipped so it can
   bleed past the card edge. */
.vallem-process__steps > .vallem-process__step {
	position: relative;
	overflow: hidden;
}

/* The heading sits at the top of the card and the body copy drops to the foot,
   so the gap between them is whatever the card's height leaves over. That gap
   is the design: it is what makes the row read as a descent rather than as
   three blocks of text. An auto margin on the last child rather than
   space-between, because a card with three things in it should still put two of
   them together at the top. */
.vallem-process__step > * {
	position: relative;
	z-index: 1;
}

.vallem-process__step > :last-child {
	margin-block-start: auto;
}

/* The brand mark, bled off the top right of the card and clipped. Sized and
   placed as the old site placed it: 110% of the card, starting a little under
   halfway across. It is decoration, so it takes no pointer and no alt text.

   Opacity here rather than a second, paler copy of the file, so one asset
   serves any card colour it is dropped onto. */
.vallem-process__step > .vallem-process__emblem {
	position: absolute;
	z-index: 0;
	inset-block-start: 0;
	inset-inline-start: 44%;
	width: 110%;
	margin: 0;
	opacity: 0.23;
	pointer-events: none;
}

.vallem-process__emblem img {
	display: block;
	width: 100%;
	height: auto;
}

/* Stacked, the staircase has nothing to descend past, so it goes away and the
   cards take their own height. They still meet edge to edge, which is what the
   old site did too and what keeps the colours reading as one band. */
@media (width <= 781px) {
	.vallem-process__steps > * {
		flex-basis: 100%;
		min-height: 0;
	}
}

/* ------------------------------------------------------------------------- *
 * Intro naast portretfoto. Reusable section, patterns/section-intro-split.php
 *
 * Almost nothing about this section lives here. The dark band, the column
 * split, the gaps and every colour are block attributes, so the whole thing is
 * recolourable and rewritable in the editor. What is left is the two things a
 * block cannot say: an image block's own bottom margin, and how big an inline
 * SVG that has no size of its own should be.
 * ------------------------------------------------------------------------- */

/* Core gives every image block a bottom margin. The group around the photo
   already owns the gap down to the caption, so that margin lands on top of it
   and the caption drifts. Same reason the hero zeroes it. */
.vallem-intro-split__photo {
	margin: 0;
}

/* The photo ships empty, so there is no intrinsic width for the aspect ratio to
   measure against and the placeholder collapses to nothing. This gives it the
   column to work from, which is what makes the empty box hold the layout. */
.vallem-intro-split__photo img {
	display: block;
	width: 100%;
}

/* The points are an ordinary list with the shared tick drawn on each item, so
   the mark is one definition used in two sections rather than an SVG pasted
   into the markup. See --vallem-tick-* at the top of this file. */
.vallem-tick-list {
	--vallem-tick-size: 1.375rem;

	margin: 0;
	padding-inline-start: 0;
	list-style: none;
}

.vallem-tick-list li {
	display: flex;
	align-items: flex-start;
	gap: var(--wp--preset--spacing--20);
}

.vallem-tick-list li + li {
	margin-block-start: var(--wp--preset--spacing--20);
}

/* Painted disc with the glyph on top, the same two tones and the same two
   properties the comparison list uses. */
.vallem-tick-list li::before {
	content: "";
	flex: none;
	inline-size: var(--vallem-tick-size);
	block-size: var(--vallem-tick-size);
	border-radius: 50%;
	background: var(--vallem-tick-color) var(--vallem-tick-glyph) center / contain no-repeat;

	/* Centred on the first line rather than on the whole item, so a point that
	   wraps keeps its mark beside the words it belongs to. The em value is the
	   fallback for browsers without lh. */
	margin-block-start: 0.19em;
}

@supports (height: 1lh) {
	.vallem-tick-list li::before {
		margin-block-start: calc((1lh - var(--vallem-tick-size)) / 2);
	}
}

/* Once the columns stack, the portrait is as wide as the page and runs to most
   of a screen and a half. The old site capped its height and let the crop take
   the difference. A max-height rather than a second aspect ratio, because the
   ratio is an inline style on the block: overriding it needs an !important,
   and that would beat whatever the editor set as well. */
@media (width <= 781px) {
	.vallem-intro-split__photo img {
		max-height: 24rem;
	}
}

/* ------------------------------------------------------------------------- *
 * Sectie, tekst met foto en vinkje. See patterns/section-signup.php
 *
 * This block REPLACES the earlier "Sectie, tekst met foto en vinkje" block in
 * style.css. Two things changed under it: the mark is the old site's Lottie
 * animation now rather than a flat shield, and the media column has to hold a
 * shape before anybody has uploaded a photo.
 * ------------------------------------------------------------------------- */

/* The positioning context for the badge. Deliberately not clipped, unlike the
   hero's media group: the hero's card sits inside its photo, this one hangs off
   the corner and has to be allowed out. The group carries no padding and no
   block gap, and the badge is out of flow, so the group is exactly as tall as
   the figure below and anchoring to it anchors to the photo. */
.vallem-signup__media {
	position: relative;
}

/* The ratio has to be on the figure as well as on the image, and that is the
   whole of the bug the badge appeared to have.

   The photo ships empty so the editor shows its upload placeholder. An <img>
   with no src and an empty alt "represents nothing", and the HTML rendering
   spec then has it treated as an empty inline element: not a replaced box, so
   the aspect ratio the block wrote onto it never applies and there is nothing
   for width to size. The figure collapsed to zero on the front end, the group
   collapsed with it, and the badge, anchored to that group, sat alone in the
   middle of an empty column looking broken. It was not the mark that was wrong.

   The ratio is a floor rather than a clamp: a box with a preferred aspect ratio
   takes its automatic minimum size from its content, so a photo cropped taller
   than 6/5 still pushes the figure open and the badge stays on its corner. The
   wash is what makes the empty state read as a media slot instead of a hole.

   The :has() line is for the opposite case, a photo cropped shorter than 6/5,
   where the figure would otherwise leave a band of wash under it. Nothing
   depends on it landing; without it the empty state is still right. */
.vallem-signup__photo {
	margin: 0;
	aspect-ratio: 6 / 5;
	background-color: var(--wp--preset--color--surface);

	/* On the figure as well as on the img below, because this is the box that
	   carries the wash while the slot is empty. Without it the empty state is a
	   square grey panel that the uploaded photo then rounds the corners off. */
	border-radius: 8px;
}

.vallem-signup__photo:has( img[src] ) {
	aspect-ratio: auto;
	background-color: transparent;
}

/* The same 8px the academy picture uses, and for the same reason: a large
   photograph sitting straight on a section background with no plate around it.
   The 6px corner is the one for pictures a card encloses. A default, not a
   lock: the border control on the image block lands as an inline style on the
   img and still wins. */
.vallem-signup__photo img {
	display: block;
	width: 100%;
	border-radius: 8px;
}

/* Absolute, not a negative margin, for the same reason as the hero's card: the
   two columns are vertically centred, so a margin would move the photo relative
   to the copy every time either one changed length. This way the badge is
   anchored to the corner and nothing else in the section can see it.

   The 20px overhang is the design's. It fits in the column gap at every width
   the two columns are side by side, which is why nothing has to clip. */
.vallem-signup__badge {
	/* Measured off the live section rather than guessed: the badge is 100px
	   square, the ring's outer diameter 122px, and the animation's own artwork
	   fills 77% of its 300px frame. 122 / 0.77 / 100 puts the mark at 79% of the
	   badge, which is where the old site had it. The still frame is drawn from
	   the same end-frame numbers as the animation, so both take this one value
	   and neither needs a nudge of its own. */
	--vallem-signup-mark: 79%;

	position: absolute;
	z-index: 1;
	bottom: -20px;
	left: -20px;
	width: clamp(84px, 7vw, 100px);
	aspect-ratio: 1;
	display: grid;
	place-items: center;
}

/* One cell, not two rows. Both the still frame and the animation are in the
   DOM together until the player reports it has the JSON parsed, and a grid that
   gave them a row each would halve the badge in the meantime. */
.vallem-signup__mark,
.vallem-signup__anim {
	grid-area: 1 / 1;
	margin: 0;
	width: var(--vallem-signup-mark);
	aspect-ratio: 1;
}

.vallem-signup__mark img {
	display: block;
	width: 100%;
	height: auto;
}

/* The player writes an <svg> into the mount the moment it is constructed and
   fills it a tick later, so the mount stays invisible until the script says it
   has something drawn. Nothing here ever hides the still frame on its own: no
   JS, a blocked vendor script and a 404 on the JSON all end with the still
   frame on screen, which is the only reason the badge cannot come out empty. */
.vallem-signup__anim {
	visibility: hidden;
}

.vallem-signup__anim svg {
	display: block;
}

.vallem-signup__badge.is-animated .vallem-signup__anim {
	visibility: visible;
}

.vallem-signup__badge.is-animated .vallem-signup__mark {
	display: none;
}

/* Below the breakpoint the columns stack and the photo runs the full width of
   the page, so a 20px overhang would put the badge in the page gutter. It moves
   into the corner instead of hiding: it is the one fixed thing in this pattern
   and the section is weaker without it. The old site does the same. */
@media (width <= 781px) {
	.vallem-signup__badge {
		bottom: 0;
		left: 0;
	}
}

/* ------------------------------------------------------------------------- *
 * Review and key figures. One-off section, see patterns/social-proof-band.php
 *
 * Three things live here and nowhere else: the wash, the hairline between the
 * reviewer and the customer logo, and where the card row gives up on three
 * columns. Everything else the section needs is a block attribute, so it is
 * visible and changeable in the editor.
 * ------------------------------------------------------------------------- */

/* The old site laid a photographic blur under the foot of this section: a
   1418px image loaded for what is two soft blobs of colour. Sampled the
   reference at nine points across its bottom edge plus two columns up its
   height, then fitted two radial gradients to those numbers. They land within
   about one value per channel of the original, which is closer than the JPEG
   was to itself.

   Both blobs are mixed from the palette rather than written as flat hex, so the
   wash is the brand's own blue and violet and follows either of them if it ever
   moves. The percentages are of this box, not of the viewport, so the wash
   keeps its proportions whatever the copy does to the section's height.

   background-image paints above the block's background colour and below the
   content, which is what keeps the colour editable: pick a different one in the
   editor and the wash still reads over the top of it. */
.vallem-proof {
	--vallem-proof-blue: var(--wp--preset--color--accent);
	--vallem-proof-violet: color-mix(in srgb, var(--wp--preset--color--accent) 73%, var(--wp--preset--color--violet));

	background-image:
		radial-gradient(28% 91% at 72% 125%, color-mix(in srgb, var(--vallem-proof-blue) 49%, transparent), transparent),
		radial-gradient(44% 118% at 44% 124%, color-mix(in srgb, var(--vallem-proof-violet) 38%, transparent), transparent 96%);
}

/* The hairline between the reviewer and the customer logo. Here rather than on
   the block because it has to leave when the row wraps, and a border set in the
   editor is an inline style that no media query can reach. This way round a
   border added in the editor still wins, which is the right way round.

   60px so the rule stands a little proud of the 56px portrait beside it, which
   is what the reference does and what stops it reading as an underline for the
   logo. */
.wp-block-group.vallem-proof__customer {
	min-height: 60px;
	padding-inline-start: var(--wp--preset--spacing--20);
	border-inline-start: 1px solid var(--wp--preset--color--border);
}

@media (width <= 781px) {
	/* Once the row wraps, the rule is a line hanging under the name. */
	.wp-block-group.vallem-proof__customer {
		min-height: 0;
		padding-inline-start: 0;
		border-inline-start: 0;
	}
}

/* ------------------------------------------------------------------------- *
 * De reviewslider. Zelfde sectie, zie assets/js/proof-slider.js
 *
 * Alles hier hangt achter `is-slider`, en die klasse zet de JS er pas op als de
 * knoppen staan en de beginstand klopt. Zonder JS blijft de sectie dus een
 * gewone stapel van vier leesbare reviews, en dat is ook precies wat de editor
 * toont. Dat is geen bijvangst maar de reden dat het zo staat: het thema laadt
 * style.css ook in het canvas, dus had het stapelen in de CSS gezeten, dan had
 * de klant in de editor drie van de vier reviews niet meer kunnen vinden.
 *
 * Zelfde afspraak als `is-enhanced` op de reviews-rail.
 * ------------------------------------------------------------------------- */

/* Alle vier de reviews in hetzelfde vak. De sectie is daarmee zo hoog als de
   langste review, wat de reden is dat de citaten in het patroon op ongeveer
   dezelfde lengte gehouden zijn: een kort citaat in een doos die op het langste
   gemaakt is, hangt in een gat. */
.vallem-proof.is-slider .vallem-proof__list {
	display: grid;
}

.vallem-proof.is-slider .vallem-proof__item {
	grid-area: 1 / 1;
}

/* De blockGap van het lijstblok is in flow layout een bovenmarge op elk kind na
   het eerste. In een enkel rastervak zou die marge de tweede tot en met vierde
   review een stuk omlaag duwen. */
.vallem-proof.is-slider .vallem-proof__list > * + * {
	margin-block-start: 0;
}

/* De haarlijn tussen de reviewer en het klantlogo hoort bij de review die aan
   de beurt is. Alles in een item zit tijdens de overgang achter een masker of
   achter een clip-path, behalve deze rand: die zit op een groep en heeft niets
   dat hem afdekt. Zonder deze regel staan er tijdens elke overgang twee lijnen
   tegelijk in beeld, op twee verschillende hoogtes omdat de namen verschillend
   lang zijn, en dat leest als een streep die naar beneden uitschiet.

   Omwisselen op een klasse was niet genoeg. De reviews zijn niet even hoog van
   tekst, dus de twee lijnen staan tijdens de overgang ook nog eens op twee
   hoogtes, en dan is er geen moment waarop er precies een klopt. Hij moet dus
   met zijn eigen review mee vervagen en niet omklappen.

   De kleur blijft hier staan, de JS draait alleen aan de sterkte. Zo hoeft geen
   enkele kleur in een JS-bestand te staan en volgt de lijn de tokens. */
.vallem-proof.is-slider .wp-block-group.vallem-proof__customer {
	--vallem-rule: 0;

	border-inline-start-color: color-mix(
		in srgb,
		var(--wp--preset--color--border) calc(var(--vallem-rule) * 100%),
		transparent
	);
}

/* SplitText zet een masker om elke regel met `overflow: clip`. Dat knipt precies
   op de basislijn af, dus de staarten van de p, de g en de j verdwijnen. De
   padding maakt het masker een fractie hoger en de negatieve marge haalt die
   hoogte weer uit de regelval, zodat de regelafstand niet verspringt. In em, dus
   het klopt net zo goed op het citaat als op de kleine regels. */
.vallem-proof__line-mask {
	padding-bottom: 0.2em;
	margin-bottom: -0.2em;
}

/* --- De logorij en de knoppen -------------------------------------------- */

.vallem-proof__nav {
	display: flex;
	flex-wrap: wrap;
	gap: var(--wp--preset--spacing--30);
	align-items: flex-end;
	justify-content: space-between;

	/* Overschrijft de blockGap van het kopblok. De rij hoort niet bij de naam
	   erboven, hij hoort bij de sectie. */
	margin-block-start: var(--wp--preset--spacing--60);
}

/* Even brede segmenten, en niet elk segment zo breed als zijn eigen logo. Bij
   een logo van 60px naast een van 150px worden de haarlijntjes eronder even
   ongelijk, en dan leest de rij als vier losse dingen in plaats van als een
   balk waar je positie in staat. Vier gelijke vakken is wat er van te maken
   valt zonder de logo's zelf te verminken. */
.vallem-proof__ticks {
	display: flex;
	flex: 1 1 auto;
	gap: var(--wp--preset--spacing--20);
	align-items: flex-end;
	max-width: 720px;
}

/* `stretch`, zodat de haarlijn de volle breedte van zijn segment pakt. */
.vallem-proof__tick {
	display: flex;
	flex: 1 1 0;
	flex-direction: column;
	align-items: stretch;
	gap: 12px;
	min-width: 0;
	padding: 0;
	border: 0;
	background: none;
	color: inherit;
	cursor: pointer;
}

.vallem-proof__tick-mark {
	display: flex;
	align-items: center;
	justify-content: flex-start;
	height: 28px;
}

/* Een inkt voor de hele rij, net als bij de logorij onder de hero. Het actieve
   logo is niet een ander kleurtje maar hetzelfde merk dat helemaal opkomt, en
   de enige kleur in de rij is de violette klok eronder. De opacity staat hier
   niet: die schrijft GSAP inline, want hij animeert mee met de overgang. */
.vallem-proof__tick-mark img {
	display: block;
	width: auto;
	max-width: 100%;
	height: 100%;
	object-fit: contain;
	object-position: left center;
	filter: grayscale(1) brightness(0);
}

/* De terugval voor een review zonder klantlogo, bijvoorbeeld een particuliere
   reviewer op Trustpilot. Die krijgt zijn naam en geen verzonnen merk. */
.vallem-proof__tick-mark--text {
	font-size: var(--wp--preset--font-size--small);
	font-weight: 600;
	letter-spacing: -0.011em;
	white-space: nowrap;
}

.vallem-proof__tick-rail {
	display: block;
	width: 100%;
	height: 1px;
	overflow: hidden;
	background-color: var(--wp--preset--color--border);
	transition: background-color 150ms ease;
}

/* Dit is de autoplay zelf en niet een balkje dat er toevallig op lijkt. De tween
   die hem vult is dezelfde die de volgende review inschopt, dus pauzeren is hier
   ook zichtbaar. `transform-origin` links, want hij groeit mee met de tijd. */
.vallem-proof__tick-fill {
	display: block;
	width: 100%;
	height: 100%;
	background-color: var(--wp--preset--color--violet);
	transform: scaleX(0);
	transform-origin: left center;
}

/* De hover zit op de haarlijn en niet op het logo, omdat GSAP de opacity van het
   logo inline bezit en CSS daar niet meer bij kan zonder !important. */
@media (hover: hover) and (pointer: fine) {
	.vallem-proof__tick:hover .vallem-proof__tick-rail {
		background-color: color-mix(in srgb, var(--wp--preset--color--contrast) 45%, transparent);
	}
}

.vallem-proof__tick:focus-visible {
	outline: 2px solid var(--wp--preset--color--violet);
	outline-offset: 6px;
	border-radius: 2px;
}

/* Dezelfde chip als de pijlen op de reviews-rail, en om dezelfde reden in
   currentcolor: dan klopt hij op deze lichte waas net zo goed als op het navy
   en is er maar een kleurbesluit. */
.vallem-proof__controls {
	display: flex;
	gap: 8px;
}

.vallem-proof__control {
	display: flex;
	align-items: center;
	justify-content: center;
	width: 40px;
	height: 40px;
	padding: 0;
	border: 1px solid color-mix(in srgb, currentcolor 28%, transparent);
	border-radius: 4px;
	color: inherit;
	background-color: color-mix(in srgb, currentcolor 10%, transparent);
	cursor: pointer;
	transition:
		background-color 150ms ease,
		border-color 150ms ease;
}

.vallem-proof__control:hover,
.vallem-proof__control:focus-visible {
	background-color: color-mix(in srgb, currentcolor 18%, transparent);
	border-color: color-mix(in srgb, currentcolor 55%, transparent);
}

.vallem-proof__control svg {
	display: block;
	width: 20px;
	height: 20px;
}

@media (width <= 781px) {
	.vallem-proof__nav {
		gap: var(--wp--preset--spacing--40);
		margin-block-start: var(--wp--preset--spacing--50);
	}

	.vallem-proof__ticks {
		gap: var(--wp--preset--spacing--10);
		max-width: none;
	}

	.vallem-proof__tick {
		gap: 8px;
	}

	.vallem-proof__tick-mark {
		height: 18px;
	}
}

/* Zoals overal in dit bestand: de staten blijven, de beweging gaat eraf. De JS
   ruilt de regelonthulling voor een crossfade en start de autoplay niet, dus
   hier hoeft alleen de chrome nog stil te staan. */
@media (prefers-reduced-motion: reduce) {
	.vallem-proof__control,
	.vallem-proof__tick-rail {
		transition: none;
	}
}

/* ------------------------------------------------------------------------- *
 * Vergelijking, twee kaarten. See patterns/section-comparison.php
 *
 * Almost everything this section needs is a block attribute, so what is left
 * here is the row icon and two measures. The icon is the interesting one.
 *
 * A core/list-item is a RichText field: inline SVG dropped inside one is
 * stripped the first time somebody edits that row. The alternative, a group of
 * blocks per row, keeps the SVG but can only be extended by duplicating a
 * block. Drawing the icon on the row from here is what makes the list an
 * ordinary list again, so pressing Enter adds a sixth row and the icon comes
 * with it. Nothing about the list markup knows the icon exists.
 *
 * The two marks are the section's own SVG, inlined as data URIs rather than
 * fetched, so they cost no request and cannot 404. They are painted on a
 * pseudo-element, which means no assistive technology ever sees them: the row
 * text already says which list it is in.
 * ------------------------------------------------------------------------- */

/* The two lists are deliberately different lengths, so the cards are too. Left
   alone, the shorter card stops early and the green hairline on the other one
   ends in mid air next to it. Filling the column and letting the list panel
   take the slack means either card can be the longer one and the frame still
   closes around a whole card. Costs a strip of empty navy under the shorter
   list, which reads as a card rather than as something unfinished.

   If the columns block is ever set to align top, the column stops stretching,
   the percentage has nothing to resolve against and this quietly does nothing.
   That is the right way for it to fail.

   The clip is what makes the card's radius visible: the two panels inside carry
   their own backgrounds and would square the corners straight back off. */
.vallem-compare__card {
	display: flex;
	flex-direction: column;
	height: 100%;
	overflow: hidden;
}

.vallem-compare__card-body {
	flex: 1;
}

.vallem-compare__list {
	--vallem-compare-icon: 22px;

	/* The tick green is the section's whole argument, and it is spelled twice:
	   here, and on the card's border in the pattern, where the editor can reach
	   it. Two spellings of one idea drift apart the moment anyone recolours the
	   card, so the mark reads its colour from a property instead of carrying it
	   inside the glyph. Change this and the border together and the section
	   stays coherent; better still, point both at a palette slug once there is
	   one. See docs/pattern-types.md on why there is not one yet. */
	--vallem-compare-tick: var(--vallem-tick-color);
	--vallem-compare-cross: #cfd4dd;

	margin: 0;
	padding-inline-start: 0;
	list-style: none;
}

.vallem-compare__list li {
	display: flex;
	align-items: flex-start;
	gap: 12px;
	margin: 0;
}

.vallem-compare__list li + li {
	margin-block-start: 10px;
}

.vallem-compare__list li::before {
	content: "";
	flex: none;
	inline-size: var(--vallem-compare-icon);
	block-size: var(--vallem-compare-icon);
	background: center / contain no-repeat;

	/* Centred on the first line, not on the whole row, so a row that wraps
	   keeps its mark beside the words it belongs to. The em value is the
	   fallback for browsers without lh (before Chrome 109, Safari 16.4,
	   Firefox 120) and matches theme.json's 1.6 line height at 18px. */
	margin-block-start: 0.19em;
}

@supports (height: 1lh) {
	.vallem-compare__list li::before {
		margin-block-start: calc((1lh - var(--vallem-compare-icon)) / 2);
	}
}

/* The cross is a knockout: a disc with the X cut out of it, so whatever the card
   is painted shows through the mark. Drawn as a mask over a painted colour
   rather than as a coloured image, which is what lets the colour live in a
   property. Same construction as .vallem-reviews__verified::before. */
.vallem-compare__list--cross li::before {
	background-color: var(--vallem-compare-cross);
	-webkit-mask: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'><path d='M15 0C23.2843 0 30 6.71573 30 15C30 23.2843 23.2843 30 15 30C6.71573 30 0 23.2843 0 15C0 6.71573 6.71573 0 15 0ZM20.7334 9.2666C20.3783 8.91159 19.8033 8.91158 19.4482 9.2666L15 13.7139L10.5518 9.2666C10.1967 8.91158 9.62162 8.91158 9.2666 9.2666C8.91158 9.62162 8.91158 10.1967 9.2666 10.5518L13.7139 15L9.2666 19.4482C8.91158 19.8033 8.91159 20.3783 9.2666 20.7334C9.62162 21.0884 10.1967 21.0884 10.5518 20.7334L15 16.2861L19.4482 20.7334C19.8033 21.0884 20.3783 21.0884 20.7334 20.7334C21.0884 20.3783 21.0884 19.8033 20.7334 19.4482L16.2861 15L20.7334 10.5518C21.0884 10.1967 21.0884 9.62162 20.7334 9.2666Z'/></svg>") center / contain no-repeat;
	mask: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'><path d='M15 0C23.2843 0 30 6.71573 30 15C30 23.2843 23.2843 30 15 30C6.71573 30 0 23.2843 0 15C0 6.71573 6.71573 0 15 0ZM20.7334 9.2666C20.3783 8.91159 19.8033 8.91158 19.4482 9.2666L15 13.7139L10.5518 9.2666C10.1967 8.91158 9.62162 8.91158 9.2666 9.2666C8.91158 9.62162 8.91158 10.1967 9.2666 10.5518L13.7139 15L9.2666 19.4482C8.91158 19.8033 8.91159 20.3783 9.2666 20.7334C9.62162 21.0884 10.1967 21.0884 10.5518 20.7334L15 16.2861L19.4482 20.7334C19.8033 21.0884 20.3783 21.0884 20.7334 20.7334C21.0884 20.3783 21.0884 19.8033 20.7334 19.4482L16.2861 15L20.7334 10.5518C21.0884 10.1967 21.0884 9.62162 20.7334 9.2666Z'/></svg>") center / contain no-repeat;
}

/* The original tick sits on a two-stop vertical gradient, #79E66E to #30C947.
   Flattened to the midpoint of the two, because the gradient needs an id in a
   defs block and this mark repeats once per row, which would put the same id in
   the document six times over. At 22px there is nothing to see in the
   difference.

   Two tones, so it cannot be a single mask like the cross. The disc is painted
   and the glyph sits on top of it, which keeps the green in the property and
   out of the artwork. */
.vallem-compare__list--check li::before {
	background-color: var(--vallem-compare-tick);
	border-radius: 50%;
	background-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'><path fill='%23FFFFFF' fill-rule='evenodd' clip-rule='evenodd' d='M21.4056 8.21413C22.0123 8.6108 22.1825 9.42421 21.7859 10.0309L14.3485 21.4057C14.1225 21.7514 13.747 21.9705 13.3349 21.9972C12.9228 22.024 12.5222 21.8552 12.2534 21.5417L8.31599 16.948C7.84426 16.3976 7.908 15.569 8.45835 15.0973C9.00871 14.6256 9.83728 14.6893 10.309 15.2397L13.1084 18.5057L19.5888 8.59438C19.9855 7.98769 20.7989 7.81744 21.4056 8.21413Z'/></svg>");
}

/* The card subtitle runs on a narrower measure than the title above it. Two
   lines of each at the same width read as one paragraph with a bold first
   half; pulling the second one in is what separates them. */
.vallem-compare__card-head > p {
	max-width: 85%;
	margin-inline: auto;
}

/* Thinner and lower than the browser default, which at display sizes sits in
   the descenders of the g and the j.

   Matches a span rather than a <u>, because that is what core's Underline
   format saves and the only thing rich text will keep. Written against the
   declaration rather than a class, since the editor writes the style inline
   and there is no class to hook. */
.vallem-compare__head [style*="underline"] {
	text-decoration-thickness: 0.06em;
	text-underline-offset: 0.12em;
}

/* Matches the icon size the old section dropped to on phones. The columns
   block stacks on its own at this width. */
@media (width <= 781px) {
	.vallem-compare__list {
		--vallem-compare-icon: 18px;
	}

	.vallem-compare__list li {
		gap: 10px;
	}
}

/* ------------------------------------------------------------------------- *
 * Sectie, tekst naast beeld. See patterns/section-reassurance.php
 *
 * The lightest section on the site, and the stylesheet says so. Core's columns
 * already give the even split, the stacking and the vertical centring; what is
 * left is the picture, which core gives a bottom margin it must not have here,
 * and the room a button needs if anyone adds one.
 * ------------------------------------------------------------------------- */

/* Two classes on purpose: core's own `.wp-block-image` carries a bottom margin
   at one class, and the two columns are centred against each other, so that
   margin would push the picture off the centre line of the row. */
.vallem-reassurance .vallem-reassurance__media {
	margin: 0;
}

/* The block ships without an image so the editor shows its upload placeholder.
   The aspect ratio is set on the block, so this only has to give the box a
   width to apply it to.

   The 8px is the same corner the academy picture and the signup photo carry: a
   large photograph sitting straight on a section background with no plate
   around it. A default, not a lock, as everywhere else. */
.vallem-reassurance .vallem-reassurance__media img {
	display: block;
	width: 100%;
	border-radius: 8px;
}

/* No button ships with this section but one is meant to fit. The 16px rhythm
   that reads right between a label, a heading and a paragraph is too tight
   under a button, so whatever follows the copy gets the next step up instead.
   Two classes, because the column's own block-gap rule is one and would
   otherwise win. */
.vallem-reassurance .vallem-reassurance__copy > .wp-block-buttons {
	margin-block-start: var(--wp--preset--spacing--30);
}

/* ------------------------------------------------------------------------- *
 * Reviews, carrousel. See patterns/reviews-carousel.php
 *
 * Core has no carousel block and a theme must not register one, so the rail is
 * a scroll container with scroll-snap and the cards inside it are ordinary
 * groups. That is the whole trick: the browser does the scrolling, the snapping
 * and the keyboard, and a seventh review is a duplicated block.
 *
 * assets/js/reviews-rail.js adds the arrows, the dots and the rail's accessible
 * name, and only then sets `is-enhanced`. The scrollbar is hidden by that class
 * alone, so when the JS does not run the bar is still there doing the job the
 * dots would have done. Nothing is ever both hidden and unreachable.
 *
 * The head and the rail start on the same vertical line and the rail then runs
 * off the right edge, which is what tells you there is more. Both read the same
 * expression, and a percentage inside it resolves against the same box in both
 * places: the head is centred in the section's content box, and the rail's
 * padding and scroll-padding resolve against that same content box. They cannot
 * drift apart, and no viewport unit is involved so a scrollbar cannot shift one
 * without the other.
 * ------------------------------------------------------------------------- */

.vallem-reviews {
	/* The content line. Named because three rules read it: the head's cap, the
	   rail's inset and the two-card sliver, so head, first card and dots move
	   together and cannot drift apart. */
	--vallem-reviews-measure: var(--vallem-content-line);
	--vallem-reviews-inset: max(
		var(--wp--preset--spacing--50),
		(100% - var(--vallem-reviews-measure)) / 2
	);

	/* Two cards fill the measure, so the third is a sliver at the edge. This one
	   does use vw, because a flex-basis percentage would resolve against the
	   rail's content box and the rail has already spent its left padding there.
	   A scrollbar makes the card a few pixels wide of ideal, which nothing can
	   see. The subtracted gap is the rail's block gap as the pattern ships it;
	   change that in the editor and the sliver widens by the difference, which
	   is the only thing that goes out of step. */
	--vallem-reviews-card: calc(
		(
			min(
				var(--vallem-reviews-measure),
				100vw - 2 * var(--wp--preset--spacing--50)
			) - var(--wp--preset--spacing--30)
		) / 2
	);

	/* The source's tick, inlined so it costs no request and cannot 404. */
	--vallem-reviews-check: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E%3Cpath d='M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z'/%3E%3C/svg%3E");

	/* The design puts a blurred sky photo behind the bottom of the section at
	   24% opacity, which lifts it from about #051229 to #1D345C in the centre.
	   The photo is not ours to ship and a gradient measures the same. Fully
	   transparent is written as the same blue at zero alpha, because
	   `transparent` interpolates through transparent black and greys the fade. */
	background-image: radial-gradient(
		120% 70% at 50% 118%,
		rgb(60 110 200 / 32%) 0%,
		rgb(60 110 200 / 0%) 62%
	);
}

/* Centred on the measure, with the gutter as padding so the cap is the content
   width and not the border box. */
.vallem-reviews .vallem-reviews__head {
	max-width: calc(var(--vallem-reviews-measure) + 2 * var(--wp--preset--spacing--50));
	margin-inline: auto;
	padding-inline: var(--wp--preset--spacing--50);
}

.vallem-reviews .vallem-reviews__badge {
	margin: 0;
}

.vallem-reviews .vallem-reviews__badge img {
	display: block;
	width: 80px;
	height: auto;
}

/* 528px in the design, at the 48px it is set in. Written in em so the line
   length holds when somebody picks a different size. */
.vallem-reviews .vallem-reviews__title {
	max-width: 11em;
}

/* ------------------------------------------------------------------------- *
 * The rail
 * ------------------------------------------------------------------------- */

.vallem-reviews .vallem-reviews__rail {
	padding-inline-start: var(--vallem-reviews-inset);
	padding-inline-end: var(--wp--preset--spacing--50);
	overflow-x: auto;

	/* Reaching the end of the rail should not start scrolling the page sideways
	   or trigger a back gesture. */
	overscroll-behavior-inline: contain;
	scroll-snap-type: x mandatory;
	scroll-padding-inline-start: var(--vallem-reviews-inset);

	/* Equal-height cards, so the name can sit on a common baseline below. */
	align-items: stretch;
}

/* Only once the arrows and the dots exist does the bar stop being the only way
   to see where you are. */
.vallem-reviews.is-enhanced .vallem-reviews__rail {
	scrollbar-width: none;
}

.vallem-reviews.is-enhanced .vallem-reviews__rail::-webkit-scrollbar {
	display: none;
}

/* The rail is focusable, so it needs a ring. Inset, because the rail runs to
   the viewport edge and an outward offset would be clipped there. */
.vallem-reviews .vallem-reviews__rail:focus-visible {
	outline: 2px solid currentcolor;
	outline-offset: -2px;
}

.vallem-reviews .vallem-reviews__card {
	flex: 0 0 var(--vallem-reviews-card);
	display: flex;
	flex-direction: column;
	scroll-snap-align: start;
}

/* Equal-height cards leave the shorter ones with slack, and the name should sit
   at the bottom of the card rather than under the last line of the quote. The
   quote takes the slack, not a margin on the name: `margin-block-start: auto`
   would push the name down but it also *replaces* the block gap, so the card
   with the longest quote ended up with its name jammed against it. Growing the
   quote leaves the gap exactly where the editor set it, in every card. */
.vallem-reviews .vallem-reviews__quote {
	flex: 1 1 auto;
}

.vallem-reviews .vallem-reviews__stars {
	margin: 0;
}

.vallem-reviews .vallem-reviews__stars img {
	display: block;
	width: 104px;
	height: auto;
}

/* The tick is drawn on the label instead of sitting next to it as a second
   block, so the row stays one editable string and the mark follows whatever
   colour the label is given. Masked rather than filled for exactly that reason.
   The source green (#49945A) is 3.7:1 on white and fails AA at 14px; this one
   is 4.7:1. */
.vallem-reviews .vallem-reviews__verified {
	display: flex;
	align-items: center;
	gap: 6px;
	margin: 0;
	font-weight: 500;
}

.vallem-reviews .vallem-reviews__verified::before {
	content: "";
	flex: none;
	width: 12px;
	height: 12px;
	background-color: currentcolor;
	-webkit-mask: var(--vallem-reviews-check) center / contain no-repeat;
	mask: var(--vallem-reviews-check) center / contain no-repeat;
}

/* ------------------------------------------------------------------------- *
 * Arrows and dots, both built by assets/js/reviews-rail.js
 *
 * The design draws a black chip with a white glyph. On this navy the chip is
 * invisible and only the glyph reads, which is the same fault as the heading
 * being set in near-black. Both take their colour from the section instead, in
 * the language the outline and subtle buttons already speak.
 * ------------------------------------------------------------------------- */

.vallem-reviews .vallem-reviews__nav {
	display: flex;
	gap: 8px;
}

.vallem-reviews .vallem-reviews__nav button {
	/* 30px in the design, which is under any sane touch target. */
	display: flex;
	align-items: center;
	justify-content: center;
	width: 40px;
	height: 40px;
	padding: 0;
	border: 1px solid color-mix(in srgb, currentcolor 28%, transparent);
	border-radius: 4px;
	color: inherit;
	background-color: color-mix(in srgb, currentcolor 10%, transparent);
	cursor: pointer;
	transition:
		background-color 150ms ease,
		border-color 150ms ease,
		opacity 150ms ease;
}

.vallem-reviews .vallem-reviews__nav button:hover,
.vallem-reviews .vallem-reviews__nav button:focus-visible {
	background-color: color-mix(in srgb, currentcolor 18%, transparent);
	border-color: color-mix(in srgb, currentcolor 55%, transparent);
}

/* Kept in place rather than hidden at either end, so the pair does not jump. */
.vallem-reviews .vallem-reviews__nav button[disabled] {
	opacity: 0.35;
	cursor: default;
}

.vallem-reviews .vallem-reviews__nav button svg {
	display: block;
	width: 20px;
	height: 20px;
}

/* Aligned to the same line as the head and the rail. */
.vallem-reviews .vallem-reviews__dots {
	display: flex;
	padding-inline-start: var(--vallem-reviews-inset);
}

/* The dot is 8px and the button is 20px, which is both the design's spacing and
   a target you can actually hit. */
.vallem-reviews .vallem-reviews__dots button {
	display: grid;
	place-items: center;
	width: 20px;
	height: 20px;
	padding: 0;
	border: 0;
	background: none;
	color: inherit;
	cursor: pointer;
}

.vallem-reviews .vallem-reviews__dots button::before {
	content: "";
	width: 8px;
	height: 8px;
	border-radius: 50%;
	background-color: color-mix(in srgb, currentcolor 32%, transparent);
	transition: background-color 150ms ease;
}

.vallem-reviews .vallem-reviews__dots button[aria-current="true"]::before,
.vallem-reviews .vallem-reviews__dots button:hover::before {
	background-color: currentcolor;
}

/* One card at a time below the fold-out width, with the next one showing a
   sliver so a swipe is obviously on offer. */
@media (width <= 781px) {
	.vallem-reviews {
		--vallem-reviews-card: calc(
			100vw - 2 * var(--wp--preset--spacing--50) - 32px
		);
	}
}

@media (prefers-reduced-motion: reduce) {
	.vallem-reviews .vallem-reviews__nav button,
	.vallem-reviews .vallem-reviews__dots button::before {
		transition: none;
	}
}

/* ------------------------------------------------------------------------- *
 * FAQ. Reusable section, see patterns/section-faq.php
 *
 * Core's accordion carries all of the behaviour, so everything here is paint.
 *
 * Three colours make the design and none of them can sit on a block. Two of
 * them follow the open state, which is a runtime class and not something a
 * block attribute can express, and the third would have to be an inline style,
 * which beats every rule in this file including the open one. So they are
 * variables on the section. A colour chosen in the editor still wins over all
 * of it, which is the way round it should be.
 * ------------------------------------------------------------------------- */

.vallem-faq {
	/* Neither has a token. primary-dark #121F54 is a step darker than the pill,
	   and against primary-darkest the two navies stop reading as separate
	   plates, which is the whole of this design. */
	--vallem-faq-pill: #1c2b68;
	--vallem-faq-pill-open: #d5f6e3;

	/* 5.19:1 on the mint, so the open question clears AA. */
	--vallem-faq-ink-open: #007244;

	/* Font Awesome's chevron-down, the glyph the old site used, carried over as
	   a mask rather than an image so it takes the question's own colour and
	   flips with it. */
	--vallem-faq-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 448 512'%3E%3Cpath d='M207.029 381.476L12.686 187.132c-9.373-9.373-9.373-24.569 0-33.941l22.667-22.667c9.357-9.357 24.522-9.375 33.901-.04L224 284.505l154.745-154.021c9.379-9.335 24.544-9.317 33.901.04l22.667 22.667c9.373 9.373 9.373 24.569 0 33.941L240.971 381.476c-9.373 9.372-24.569 9.372-33.942 0z'/%3E%3C/svg%3E");
}

/* The plate is painted on the heading, not on the item, because the answer
   under an open item sits on the section background and not on the pill. */
.vallem-faq .wp-block-accordion-heading {
	--vallem-faq-plate: var(--vallem-faq-pill);

	background-color: var(--vallem-faq-plate);
	transition:
		background-color 200ms ease,
		color 200ms ease;
}

/* `is-open` is core's own class, bound from the item's openByDefault on the
   server and toggled by the interactivity runtime in the browser. It is the
   real state, not a guess at one, and it is there before any script runs. */
.vallem-faq .wp-block-accordion-item.is-open > .wp-block-accordion-heading {
	--vallem-faq-plate: var(--vallem-faq-pill-open);

	color: var(--vallem-faq-ink-open);
}

/* One hover rule for both states, because what it mixes into is a variable and
   what it mixes in is the question's own colour: white lifts the navy, the
   dark green deepens the mint. The open selector is first so that it outranks
   the rule above; on a closed pill only the second half matches at all. */
@media (hover: hover) and (pointer: fine) {
	.vallem-faq .wp-block-accordion-item.is-open > .wp-block-accordion-heading:hover,
	.vallem-faq .wp-block-accordion-heading:hover {
		background-color: color-mix(in srgb, currentcolor 8%, var(--vallem-faq-plate));
	}
}

/* Core's toggle has no gap, so a question long enough to fill the row runs into
   the chevron. 16px, as the old accordion had. */
.vallem-faq .wp-block-accordion-heading__toggle {
	gap: var(--wp--preset--spacing--20);
}

/* Core underlines the question on hover, which reads as a link on a plate this
   size. The plate answers the pointer instead, as the nav's items do. */
.vallem-faq .wp-block-accordion-heading__toggle:hover .wp-block-accordion-heading__toggle-title {
	text-decoration: none;
}

/* theme.json tracks h3 at -0.0223em, which is display tracking on a 16px
   question. Inter's own formula gives -0.011em at this size. It goes on the
   title rather than the heading because that is where the block puts its own
   letter-spacing, so a value set in the editor still lands on top of this. */
.vallem-faq .wp-block-accordion-heading__toggle-title {
	letter-spacing: -0.011em;
}

/* The chevron. Core saves a literal `+` in the icon span and rotates it 45
   degrees to make a ×. Replacing that markup would invalidate the block, so the
   plus is sized to nothing and the span is painted through a mask instead. It
   is still core's own element, so it still takes the question's colour and
   still uses core's transition. */
.vallem-faq .wp-block-accordion-heading__toggle-icon {
	flex: none;
	width: 16px;
	height: 16px;
	font-size: 0;
	background-color: currentcolor;
	-webkit-mask: var(--vallem-faq-chevron) center / contain no-repeat;
	mask: var(--vallem-faq-chevron) center / contain no-repeat;
}

/* Five classes, because core's rotation rule is four and the two stylesheets
   can print in either order. */
.vallem-faq .wp-block-accordion-item.is-open > .wp-block-accordion-heading .wp-block-accordion-heading__toggle-icon {
	transform: rotate(180deg);
}

@media (prefers-reduced-motion: reduce) {
	.vallem-faq .wp-block-accordion-heading {
		transition: none;
	}
}

/* Het open- en dichtvouwen. Hoort bij het blok hierboven, zie
   patterns/section-faq.php

   Een gesloten antwoord was geen niets maar een doos van 32px: core haalt het
   paneel niet weg, het slaat het alleen over, en overgeslagen inhoud laat de
   padding van zijn eigen doos gewoon staan. Zes dichte vragen, zes van die
   spookregels, en dat is wat de rij uit elkaar trok. Niet de 16px blockGap,
   die klopte al.

   Daarom staat de padding van het paneel niet meer op het blok. Inline wint van
   elke regel hier, dus zolang hij daar stond viel er niets aan weg te animeren.
   Hier valt dat wel, en daarmee is de spookregel weg en het vouwen mogelijk in
   één en dezelfde ingreep. */

/* De hoogte komt van het item en niet van een max-height op het paneel. Een
   max-height moet een plafond hebben dat het langste antwoord niet afknipt, en
   dat plafond is altijd veel hoger dan het echte antwoord: bij 480px plafond en
   een antwoord van 83px is 17% van de reis zichtbaar, en met een ease-out is
   dat in 15ms voorbij. Dan is er geen vouw meer, alleen een sprong.

   0fr naar 1fr heeft dat plafond niet nodig. In een grid met vrije hoogte
   groeit een fr-spoor mee met zijn inhoud, dus de animatie loopt van 0 tot
   precies de hoogte die het antwoord heeft, hoe lang dat antwoord ook is. Core
   verwacht dit ook: `transition: grid-template-rows` staat al op het item, in
   core's eigen stylesheet, alleen zonder grid eronder. */
.vallem-faq .wp-block-accordion-item {
	display: grid;
	grid-template-rows: auto 0fr;
	transition: grid-template-rows 300ms var(--vallem-ease);
}

.vallem-faq .wp-block-accordion-item.is-open {
	grid-template-rows: auto 1fr;
}

/* `min-height: 0`, want een grid-item krimpt van zichzelf niet onder zijn
   inhoud, en dan doet het spoor niets. `overflow: hidden` is wat het
   dichtgevouwen deel afdekt: het antwoord houdt zijn eigen hoogte en schuift
   niet mee, het wordt alleen minder ver getoond.

   De opacity is korter dan de hoogte, zodat de laatste 120ms van het dichtgaan
   al onzichtbaar is. Andersom komt de tekst er iets eerder in dan de doos af
   is, en dat is dezelfde keuze: het einde van de beweging hoort rustig te zijn,
   niet het moment waarop iets verschijnt of verdwijnt.

   content-visibility staat in de lijst omdat het de eigenschap is die core's
   runtime laat omslaan, en `allow-discrete` schuift dat omslaan naar het eind
   van de transitie. Zonder die regel is het dichtvouwen niet te zien: de
   inhoud is dan al weg voordat de doos begint te krimpen. Openen heeft geen
   @starting-style nodig, want een dicht paneel is een gerenderde staat en geen
   display:none: de waarden om vandaan te komen staan er al. */
.vallem-faq .wp-block-accordion-panel {
	min-height: 0;
	overflow: hidden;
	padding-block: var(--wp--preset--spacing--10) var(--wp--preset--spacing--30);
	padding-inline: var(--wp--preset--spacing--30);
	transition:
		padding-block-start 300ms var(--vallem-ease),
		padding-block-end 300ms var(--vallem-ease),
		opacity 180ms ease,
		content-visibility 300ms allow-discrete;
}

/* De dichte staat hangt aan `is-open` en niet aan het attribuut waarmee core
   verbergt, omdat dat attribuut per versie verschilt en de klasse de staat
   zelf is, server-side al aanwezig en in de editor ook.

   `display: block` zet core's eigen `[inert]`/`[aria-hidden]` display:none
   opzij, want een doos die er niet is valt niet te animeren.
   `content-visibility: hidden` doet daar precies het werk voor terug dat die
   regel deed: de inhoud gaat de toegankelijkheidsboom uit en is niet meer te
   focussen. Dat is dus geen a11y-schuld, het is dezelfde staat langs een
   andere weg.

   De padding met !important, want dit is de enige regel hier die dat nodig
   heeft: kiest iemand in de editor alsnog padding op het paneel, dan is die
   inline en staat de spookregel er weer. Open houdt die keuze wel; dicht is
   nul, wat er ook gekozen is. */
.vallem-faq .wp-block-accordion-item:not(.is-open) > .wp-block-accordion-panel {
	display: block;
	content-visibility: hidden;
	padding-block: 0 !important;
	opacity: 0;
}

/* De chevron draaide al, maar op core's eigen 200ms ease-in-out, naast een
   vouw van 300ms met een andere curve. Twee bewegingen die tegelijk beginnen
   en niet tegelijk eindigen lezen als twee dingen. Vier klassen, want core's
   regel is er drie. */
.vallem-faq .wp-block-accordion-item > .wp-block-accordion-heading .wp-block-accordion-heading__toggle-icon {
	transition: transform 300ms var(--vallem-ease);
}

/* Zoals overal in dit bestand: de staten blijven, de beweging gaat eraf. */
@media (prefers-reduced-motion: reduce) {
	.vallem-faq .wp-block-accordion-item,
	.vallem-faq .wp-block-accordion-panel,
	.vallem-faq .wp-block-accordion-item > .wp-block-accordion-heading .wp-block-accordion-heading__toggle-icon {
		transition: none;
	}
}

/* ------------------------------------------------------------------------- *
 * Site footer, parts/footer.html
 *
 * Everything that carries meaning, the navy, the veil over the background
 * image, the muted text and the link colours, is set on the blocks so the Site
 * Editor can change it. What is left here is the stuff theme.json and the
 * block controls have no word for: list bullets, an inline SVG's size, and the
 * two breakpoints where four columns stop fitting. The gap between the last
 * section and the navy is not one of them: that is the root-level block gap,
 * and it is settled with `.wp-site-blocks > main` at the top of this file.
 * ------------------------------------------------------------------------- */

/* The nav insets its content twice: spacing-50 down to the 1376px shell, then
   spacing-20 again inside the bar. The footer's outer group does the first
   step; this does the second, so the emblem starts on the same vertical as the
   logo above it. Same arrangement as `.vallem-topbar > *`. */
.vallem-footer__inner > * {
	padding-inline: var(--wp--preset--spacing--20);
}

/* Our colour slug `border` and core's border support collide on one class name.
   A block given the text colour `border` is served `.has-border-color` for the
   preset text colour; core reads the same class as "this block has a border
   colour" and answers with `html :where(.has-border-color){border-style:solid}`.
   Nothing states a width or a colour, so both fall to their initial values,
   `medium` and `currentColor`, and the footer draws a 3px #D4D4D8 frame on all
   four sides. It was also pushing the columns 3px off the nav logo's vertical.

   Core's selector is (0,0,1); two classes clear it. The `:not()` stands this
   rule down the moment a real border is set here, and a border set in the
   editor ships an inline `border-style` that outranks it anyway, so the frame
   stays available rather than being forbidden.

   The slug cannot be renamed, because blocks store the slug and not the hex, so
   this collision is permanent and any block given that text colour will grow
   the same frame. section-comparison.php carries `has-border-color` too, but
   there it is a real 1px #89ED75 border with an inline border-style, so it is
   both out of this selector's scope and protected by the guard. */
.vallem-footer__inner.has-border-color:not([style*="border"]) {
	border-style: none;
}

/* The emblem is an inline SVG in an HTML block, because template parts are
   static HTML and `get_theme_file_uri()` does not exist there. It inherits
   currentcolor, so the mark follows whatever text colour the column is given
   rather than being a second place white is written down.

   Block, not inline-block: an inline-block sits on the baseline and drags a
   descender's worth of space under the mark. An explicit width stops the block
   from stretching to the column. */
.vallem-footer__mark {
	display: block;
	width: 40px;
	color: var(--wp--preset--color--base);
}

.vallem-footer__mark svg {
	display: block;
	width: 100%;
	height: auto;
}

/* Footer links read as a list of destinations, not as links inside prose, so
   they drop theme.json's underline and answer with colour instead. The hover
   colour itself is on the block, under Link, where it can be changed. */
.vallem-footer__inner a {
	text-decoration: none;
}

/* Bullets off, and the rhythm the old footer had. `core/list` has no blockGap
   control, its spacing support is margin and padding only, so the gap between
   items cannot come from the editor and has to be stated here.

   No `margin: 0` reset here, deliberately. The column's flow layout already
   writes `margin-block: 0` onto every direct child and the column's blockGap
   onto `* + *`, both at one class of specificity, and this stylesheet is
   enqueued after those generated rules. A reset here therefore wins and
   flattens the gap under every heading to nothing, which is what put "TOS"
   against "Legal" while the items below it sat 10px apart. Leaving margin
   alone hands that gap back to the column's blockGap, where the editor can
   reach it. The UA's `1em` on a `ul` never lands, because core's own
   `margin-block: 0` covers the first child too. */
.vallem-footer__list {
	padding-inline-start: 0;
	list-style: none;
}

.vallem-footer__list li + li {
	margin-block-start: 10px;
}

/* The address sits tighter than the link lists: two lines that belong
   together, not two separate destinations. */
.vallem-footer__list--address li + li {
	margin-block-start: 4px;
}

/* The flag is decorative next to its own label, so it is sized in em and
   follows the text. `flex: none` because a flex item shrinks by default and
   this one has nothing to give. */
.vallem-footer__flag {
	flex: none;
	width: 1.25em;
	height: 1.25em;
}

/* Tablet. Four columns of 19% put "Non Disclosure Agreement" on two lines, so
   they go two up. Both !important flags are aimed at core, not at the editor:
   `.wp-block-columns` is nowrap !important above 782px, and a column's width
   is an inline flex-basis. Anything set in the editor still wins over this,
   because it is the same inline style being overridden. */
@media (782px <= width <= 1024px) {
	.wp-block-columns.vallem-footer__cols {
		flex-wrap: wrap !important;
	}

	.vallem-footer__cols > .wp-block-column {
		flex-basis: 50% !important;
	}
}

/* The bottom row holds one item now that the compliance badges are gone, but the
   rule still earns its place: below 782px the columns above stack hard left, and
   a single item still pinned to the right edge under them reads as a stray. Two
   classes to clear the layout block support's own justify-content, which is a
   single generated class. */
@media (width < 782px) {
	.vallem-footer .vallem-footer__bottom {
		justify-content: flex-start;
	}
}

/* ------------------------------------------------------------------------- *
 * Hero, gesplitst. See patterns/hero-split.php
 *
 * The same hero as hero-home, turned on its side: copy left on the content
 * line, product image right, running off the right edge of the screen and off
 * the bottom of the plate. Because the plate is full width, "clipped by the
 * plate" and "off the screen" are the same edge, so one overflow: hidden buys
 * both.
 *
 * Two grid areas on one row, the way .vallem-partner-hero does it, rather than
 * absolute positioning. The copy sets the height of the row and the image is
 * stretched to whatever that turns out to be, so nothing is measured and no
 * height is hardcoded. The bleed is then two negative margins on top of that.
 *
 * Nothing here sets a colour. The plate carries `textColor` and every child
 * inherits it, so recolouring the section is one control in the editor. The
 * pieces that need to be quieter than the heading use opacity, and the two
 * hairlines are mixed out of currentcolor, so both follow that one colour
 * wherever it is taken. Do not add a colour class to the heading or the intro.
 * ------------------------------------------------------------------------- */

.vallem-hero-split {
	/* ---- The knobs -----------------------------------------------------
	 * --vallem-hero-split-copy          how wide the copy column is allowed
	 *                                   to get. Caps the measure of the h1;
	 *                                   the column itself is a ratio.
	 * --vallem-hero-split-bleed-inline  how far the image runs past the right
	 *                                   edge of the screen. 0 parks it flush.
	 * --vallem-hero-split-bleed-block   how far it runs past the bottom of the
	 *                                   plate, on top of the section padding
	 *                                   it already swallows. 0 sits it flush
	 *                                   on the bottom edge.
	 * Set any of them on the section in the editor's Advanced > Additional CSS
	 * or in a style variation; none of the selectors below need rewriting.
	 * -------------------------------------------------------------------- */
	--vallem-hero-split-copy: 38rem;
	--vallem-hero-split-bleed-inline: 10vw;
	--vallem-hero-split-bleed-block: var(--wp--preset--spacing--60);

	/* Where the copy starts: the content line, or the page gutter once the
	   viewport is narrower than it. Measured against 100vw rather than 100%
	   because a full-width section's containing block is the padded page, not
	   the viewport, so a percentage here would resolve against the wrong box.
	   The cost is the scrollbar's width, the same trade .vallem-partner-hero
	   makes. */
	--vallem-hero-split-inset: max(
		var(--wp--preset--spacing--50),
		calc((100vw - var(--vallem-content-line)) / 2)
	);

	display: grid;

	/* A ratio, not the content line. Snapping the seam to 1344px would leave
	   the image only as wide as the page gutter on a large screen. 46/54 puts
	   it at about 56% of the viewport at 1920, which is where the reference
	   has it. */
	grid-template-columns: minmax(0, 46fr) minmax(0, 54fr);
	column-gap: var(--wp--preset--spacing--60);
	align-items: center;

	/* No padding on the right: the image owns that edge. */
	padding-inline: var(--vallem-hero-split-inset) 0;

	/* The clip. Both bleeds are drawn and then cut off here. */
	overflow: hidden;

	/* Holds its shape if someone deletes the intro or the buttons. Dimensions
	   set in the editor still wins, because block spacing serialises inline. */
	min-block-size: 36rem;
}

/* Two classes on every child that sets a margin, and this one is measured
   rather than defensive. Core's flow layout puts a block gap on every child of
   a group, and the rule that zeroes it on the last one reads
   `:root :where(.is-layout-flow) > :last-child`. The `:last-child` sits outside
   the `:where()`, so with `:root` that is two pseudo-classes, (0,2,0), and a
   single class loses to it. The image's bleed is a negative bottom margin, so
   losing that one silently cancels the whole effect. */
.vallem-hero-split .vallem-hero-split__copy {
	grid-area: 1 / 1 / 2 / 2;
	max-width: var(--vallem-hero-split-copy);
	margin-block: 0;
}

/* Quieter than the heading without being a second colour. A muted preset would
   hardcode light-on-dark and break the moment the plate is recoloured; opacity
   follows whatever colour the plate is set to. */
.vallem-hero-split .vallem-hero-split__intro {
	opacity: 0.72;
}

/* The announcement pill. The tint is mixed out of the inherited text colour
   rather than set on the block, because no palette slug is translucent and a
   fixed one would stop following the plate. It is still only a default: a
   background set in the editor is either a preset class carrying !important or
   an inline style, and both win over this. */
.vallem-hero-split .vallem-hero-split__pill {
	background-color: color-mix(in srgb, currentcolor 12%, transparent);

	/* The pill used to sit in an anonymous flex group whose only job was to stop
	   it running the full width of the copy column. That group had no class and
	   no rule in this file, so it was a level in List View that bought nothing.
	   This is what replaces it: the pill is itself a flex group, so as a direct
	   child of the flow-layout copy column it stretches.

	   Measured at 1440px on 7.0.4: without this the pill is [48,228,608,28],
	   with it [48,228,319,28], which is what the wrapper gave. The h1 is
	   [48,280,608,227] either way, so nothing else moves. */
	width: fit-content;
}

.vallem-hero-split .vallem-hero-split__pill-divider {
	border-inline-start: 1px solid color-mix(in srgb, currentcolor 22%, transparent);
	padding-inline-start: 12px;
}

.vallem-hero-split .vallem-hero-split__pill-divider a {
	color: inherit;
	text-decoration: none;
}

/* ---- The image ---------------------------------------------------------- *
 *
 * Stretched to the row, then pushed past the plate on two sides. The inline
 * margin widens it past the right edge of the screen; the block margin has to
 * cover the section's own bottom padding before the bleed starts, which is why
 * the spacing step is named twice in this file.
 *
 * Stretch plus negative margins rather than a fixed size, so the crop keeps the
 * same shape whatever the copy above it turns out to be.
 * ------------------------------------------------------------------------- */

.vallem-hero-split .vallem-hero-split__media {
	grid-area: 1 / 2 / 2 / 3;
	align-self: stretch;
	position: relative;
	overflow: hidden;
	margin-block: 0
		calc(-1 * (var(--wp--preset--spacing--70) + var(--vallem-hero-split-bleed-block)));
	margin-inline: 0 calc(-1 * var(--vallem-hero-split-bleed-inline));

	/* The only corner anyone sees. The other three are off the screen or below
	   the plate, and placeholder.png ships square-cornered so the crop lives
	   here. */
	border-start-start-radius: 12px;
}

/* Absolute so the image contributes nothing to the row's height. Left to sit in
   flow it would measure itself from its own aspect ratio and set the height of
   the section, which is the one thing the copy is supposed to decide.

   object-position is top left: the file is 1200 x 800, so a slot taller than it
   is wide keeps the window chrome and the first cards, and loses the right and
   the bottom, which is the crop the reference has. */
.vallem-hero-split .vallem-hero-split__media img {
	position: absolute;
	inset: 0;
	display: block;
	inline-size: 100%;
	block-size: 100%;
	object-fit: cover;
	object-position: left top;
}

/* Stacked. Below this the copy column is too narrow to hold a heading at
   display-hero, so the split ends here rather than at 781px. The inline bleed
   stops outright, the image becomes an ordinary full-width image, and it keeps
   only enough negative block margin to sit flush on the bottom edge of the
   plate the way the home hero's image does. Nothing overflows, so there is
   nothing to scroll. */
@media (width < 1024px) {
	.vallem-hero-split {
		grid-template-columns: minmax(0, 1fr);
		row-gap: var(--wp--preset--spacing--60);
		padding-inline: var(--wp--preset--spacing--50);
		min-block-size: 0;
	}

	.vallem-hero-split .vallem-hero-split__copy {
		grid-area: auto;
		max-width: none;
	}

	.vallem-hero-split .vallem-hero-split__media {
		grid-area: auto;
		align-self: auto;
		block-size: min(22rem, 58vw);
		margin-block: 0 calc(-1 * var(--wp--preset--spacing--70));
		margin-inline: 0;
		border-start-end-radius: 12px;
	}
}

/* The pill wraps somewhere around here once anyone lengthens the placeholder,
   and a vertical rule at the start of the second line reads as a mistake. Same
   breakpoint and same reasoning as the home hero's pill. */
@media (width <= 781px) {
	.vallem-hero-split .vallem-hero-split__pill-divider {
		border-inline-start: 0;
		padding-inline-start: 0;
	}
}

/* ------------------------------------------------------------------------- *
 * Twee kaarten met beeldvlak. See patterns/section-feature-duo.php
 *
 * A left-aligned header over a row of cards. Almost everything a person would
 * want to change is a block attribute: the section colour, the card plate, the
 * panel tint, every string, both pictures and both buttons. What is here is the
 * two things a block cannot say. How many cards fit on a row, and where the
 * picture is cut.
 * ------------------------------------------------------------------------- */

/* Half the row, which is where the reference wraps both the heading and the
   lead. A measure rather than a percentage of the content line: 36px type wants
   roughly this many characters whatever the row underneath it is doing.

   On the head's children and not on the head itself, which is the same
   arrangement the process section's title uses and for the same reason. The
   head is a direct child of a constrained group, and core's constrained layout
   gives every one of those `margin-inline: auto`. Capped there it would centre
   itself over the cards; capped inside a flow group it stays against the left
   edge, where the reference has it. */
.vallem-feature-duo__head > * {
	max-width: 44rem;
}

/* The row is core's grid layout now: the block carries columnCount and
   minimumColumnWidth, so how many cards sit in a row is a slider in the editor
   and nothing about it is written here. The gap comes from blockGap on the block
   for the same reason. Core's rules are in an earlier sheet than this one at the
   same specificity, so leaving a grid-template-columns here would beat them and
   the Columns control would silently do nothing.

   The rule below is only for pages built before that change. The pattern is
   unsynced, so their copy of the markup is still a flow group and would stack
   without it. `is-layout-grid` is added by the server for grid layout only, so
   the guard matches exactly the old rows and never the new ones.

   auto-fit rather than auto-fill was the point of the old row: it collapses the
   empty tracks, so two cards fill a row with space for three. Core's grid uses
   auto-fill and keeps the hole. That trade is what bought the Columns control,
   and the hole doubles as the appender for putting a card back. */
.vallem-feature-duo__grid:not(.is-layout-grid) {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(min(24rem, 100%), 1fr));
	gap: var(--wp--preset--spacing--10);
	align-items: stretch;
}

/* A default, not a lock. The border control on the group lands as an inline
   style and still wins. Same 8px the academy picture uses. */
.vallem-feature-duo__card {
	border-radius: 8px;
}

/* The panel is the picture's plate. It holds the card's shape whether or not
   anybody has swapped the picture yet, and it is what cuts the picture off.

   The picture fills it edge to edge and is cropped by it. The panel used to
   inset the picture a tenth of its own width on the left, top and right and cut
   it flush at the foot; Nol asked for the whole plate, so the padding is gone
   and the crop is the panel's four edges.

   The ratio stays here rather than moving onto the image block as an
   aspectRatio attribute. The panel is the thing that has to keep standing when
   there is no picture in it, in the moment between deleting one and dropping in
   the next, and a ratio on the picture would take the plate with it.

   1.75 is our asset's number rather than the reference's 1.54. placeholder.png
   is 1200x800, so 1.5, and the slot being wider than the file is what makes
   cover draw the picture's full width and take its crop off the foot.

   box-sizing is spelled out because the ratio has to be the border box. There
   is no padding here now, but the border control on the group lands as an
   inline style, and a border on the content box would grow the panel by twice
   its own width. */
.vallem-feature-duo__panel {
	box-sizing: border-box;
	aspect-ratio: 1.75;
	overflow: hidden;
	border-radius: 6px;
}

/* Fills the panel, which is the whole plate now rather than what its top
   padding left over. Core gives every image block a bottom margin and the panel
   owns its own foot, so that goes. */
.vallem-feature-duo__shot {
	height: 100%;
	margin: 0;
}

/* cover, so the crop comes out of the picture rather than out of the layout.
   The slot at 1.75 is wider than the 1200x800 file at 1.5, so the whole width is
   drawn and it is the foot of the file that goes: the last row of the table is
   cut mid-row at the panel's bottom edge, which is what makes the picture read
   as a window onto something larger. top left rather than centre, so the crop
   is taken off the end of the page and not off the chrome.

   No radius. The picture is flush with the panel on all four sides, so its
   corners are the panel's corners and the panel's own `overflow: hidden` draws
   them. A 4px corner inside a 6px clip leaves a crescent of panel tint, and a
   radius hardcoded here would stop following the panel's border control. */
.vallem-feature-duo__shot img {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
	object-position: left top;
}

/* The grid makes every card as tall as the tallest, and this is what hands that
   height down to the copy: a flex item takes its content height unless it is
   told to grow, so without this the slack pools under the body and the button
   sits wherever the paragraph left it. min-width because a flex item will not
   shrink below its longest word, which is how one long compound pushes a card
   out of its column. */
.vallem-feature-duo__body {
	flex: 1;
	min-width: 0;
}

/* The button is pinned to the foot of the card, so two cards holding different
   amounts of copy still line their buttons up on one line. The body is a vertical
   flex group from the block, so auto here eats the slack the rule above handed
   it, and the group's own gap survives underneath: an auto margin adds to a
   flex gap rather than replacing it. The padding is what makes the step down to
   the button bigger than the one above it, which is the rhythm the reference
   has.

   A class and a pseudo-class, because core's flex gap lands at one class and a
   tie would be settled by whichever stylesheet the page printed last. */
.vallem-feature-duo__body > :last-child {
	margin-block-start: auto;
	padding-block-start: var(--wp--preset--spacing--10);
}

/* ------------------------------------------------------------------------- *
 * Statement met kaarten. See patterns/section-statement-cards.php
 *
 * Three rules. Everything else in this section is core layout and preset
 * colours set on the blocks themselves, which is what keeps it editable.
 * ------------------------------------------------------------------------- */

/* The header runs to the site's own prose measure, a little over half the
   content line, which is the proportion the design has.

   The cap is on the children and not on the header group, because the section
   is a constrained layout and core gives every direct child of one
   `margin-left: auto !important; margin-right: auto !important`. A narrowed
   group would centre, and there is no beating an !important at that
   specificity without a fight. Inside the header the layout is flow, which
   adds no margins at all, so a plain max-width leaves the label, the heading,
   the intro and the button hard left. A width set in the editor lands as an
   inline style and still wins. */
.vallem-statement__header > * {
	max-width: var(--wp--style--global--content-size);
}

/* The same 8px the academy image uses. A default, not a lock: the border panel
   on the group block lands as an inline style and still wins. */
.vallem-statement__card {
	border-radius: 8px;
}

/* Secondary copy is the plate's own colour turned down, not a second colour.
   A muted grey would hardcode dark-on-light and break the moment the section
   or a card is recoloured; opacity follows whatever colour is inherited. */
.vallem-statement__lead,
.vallem-statement__card-body {
	opacity: 0.72;
}

/* ------------------------------------------------------------------------- *
 * Sectie, uitgelicht met voortgangsbalk. See patterns/section-feature-tabs.php
 *
 * A header row, one large media panel, and a row of items under it. Each item
 * carries a rule across its top that fills from left to right; when it is full
 * the section moves to the next item and the panel shows that item's image.
 *
 * Three decisions worth knowing before touching any of this.
 *
 * The fill is a CSS animation, not a JS timer. `animationend` is what tells
 * assets/js/feature-tabs.js to advance, so the bar and the image can never drift
 * apart, and pausing is one `animation-play-state` rather than a timer that has
 * to remember how much of itself it had already spent.
 *
 * Nothing counts to three. The row is shared out by flex-basis, the bar is a
 * pair of pseudo-elements on the item itself, and the crops cycle every third
 * child. Duplicate a column and the fourth behaves like the first.
 *
 * Everything the script does is behind `.is-enhanced`, which it adds only once
 * it has wired itself up. Without it the first image is on show, every column is
 * readable and the bars stand still. That is these rules, not something the
 * script has to reveal.
 * ------------------------------------------------------------------------- */

.vallem-feature-tabs {
	/* How long one item holds the panel. The only place this number exists: the
	   script advances on the animation ending rather than on a duration of its
	   own, so there is nothing here to keep in step with anything there. */
	--vallem-feature-tabs-dwell: 6s;
}

/* Capped to the content line by the block's own constrained layout, the way
   section-process is, so nothing here has to out-specify core's alignfull
   margins. */

/* ------------------------------------------------------------------------- *
 * The header row
 * ------------------------------------------------------------------------- */

/* Long enough for a real heading, short enough that it wraps rather than
   crushing the link next to it. In em, so it follows whatever size the heading
   is set to instead of pinning the measure to 36px. */
.vallem-feature-tabs__title {
	max-width: 18em;
}

/* Two corrections, both for the same thing: the button's padding is inside its
   box, so the box edge is not where the label is.

   Core's `bottom` vertical alignment lines up the two boxes, which leaves the
   label riding above the heading's baseline by the button's own padding plus
   the difference in descender space. The reference has the two on one baseline.
   Measured across 600 to 1440px, where the heading runs 30 to 35px: 12.1 to
   12.6px throughout, so one number covers the range to under a pixel. It is em
   against the row's 16px and not against the heading, which is why it does not
   have to move when the heading size does.

   And once the row wraps, the same padding on the inline side shows up as a
   24px indent against the heading's left edge. Space-between pins the last
   item's end edge, so a negative start margin is invisible while the two share
   a line and only lands once the link has a line of its own.

   On the container, never on the button. The four variations own everything
   about the button itself.

   Two classes, not one. Core's flex layout ships
   `.is-layout-flex > :is(*, div) { margin: 0 }`, and the `div` inside the
   `:is()` makes that (0,1,1), which beats a single class outright. Cost half an
   hour of the margin being in the file and not on the page. */
.vallem-feature-tabs__head .vallem-feature-tabs__more {
	margin-block-end: -0.8em;
	margin-inline-start: calc(var(--wp--custom--button-padding-inline) * -1);
}

/* ------------------------------------------------------------------------- *
 * The media panel
 * ------------------------------------------------------------------------- */

/* Every image in the same grid cell, so the panel is as tall as the tallest of
   them and nothing moves when the item changes. Same arrangement as the signup
   badge and the review slider.

   The image fills the panel edge to edge: the block has no padding, and the
   clip plus the radius here are what round the image, so the shot itself needs
   no radius of its own. Put padding back on the block and the surface plate
   shows again as a mount around the image, which is what this was before. */
.vallem-feature-tabs__stage {
	display: grid;
	overflow: hidden;
	border-radius: 8px;
}

.vallem-feature-tabs__shot {
	grid-area: 1 / 1;
	margin: 0;

	/* Hidden rather than removed. A shot on `display: none` stops holding the
	   cell open, and the panel would collapse to nothing the moment somebody
	   replaced one of the three with a taller image. */
	visibility: hidden;
	opacity: 0;
	transition: opacity 400ms ease;
}

/* Without the script nothing carries `is-current`, so the first shot is the one
   on show and the section reads as an ordinary image with three captions. */
.vallem-feature-tabs__shot:first-child,
.vallem-feature-tabs.is-enhanced .vallem-feature-tabs__shot.is-current {
	visibility: visible;
	opacity: 1;
}

/* Once the script is running, only `is-current` counts. Three classes, so this
   beats the `:first-child` default above rather than depending on source order. */
.vallem-feature-tabs.is-enhanced .vallem-feature-tabs__shot:not(.is-current) {
	visibility: hidden;
	opacity: 0;
}

/* The ratio and the crop live here rather than in the block's attributes.
   `aspectRatio` on core/image serialises to an inline style, which no media
   query can reach, and this panel needs a different shape on a phone. Somebody
   setting a size in the editor still writes that inline style and still wins. */
.vallem-feature-tabs__shot img {
	display: block;
	width: 100%;
	aspect-ratio: 2.4;
	object-fit: cover;
}

/* All three items ship the same mock, so the crop is the only thing that says
   the panel changed. Every third child, not `:nth-child(1|2|3)`, so a fourth
   column gets the first crop back instead of falling out of the pattern.

   This block is scaffolding for the placeholder. Delete it the day each item
   has its own real image, or it will crop those too. */
.vallem-feature-tabs__shot:nth-child(3n + 1) img {
	object-position: left top;
}

.vallem-feature-tabs__shot:nth-child(3n + 2) img {
	object-position: center center;
}

.vallem-feature-tabs__shot:nth-child(3n) img {
	object-position: right bottom;
}

/* ------------------------------------------------------------------------- *
 * The items
 * ------------------------------------------------------------------------- */

/* Equal shares of the row whatever the count. Basis rather than a percentage,
   so two, three, four or five columns all work and the row wraps on its own
   when they would get too narrow to read. 13rem leaves three of them fitting on
   one line all the way down to the 781px breakpoint, where the media query
   below takes over and stacks them. */
.vallem-feature-tabs__items > * {
	flex: 1 1 13rem;
	min-width: 0;
}

/* The rule across the top of an item, as two layers on one element: the resting
   hairline is ::before and the fill that travels over it is ::after. A bar is a
   state, not content, so neither of them belongs in the markup where an editor
   would have to look after it.

   Both start at the same top edge and the fill is the thicker of the two, which
   is how the reference draws it: the active column's rule reads as the same
   line grown heavier rather than as a second line next to it.

   Dark and not the brand blue. In the reference the yellow is the wash behind
   the image and the rule itself is near black, and that maps straight onto
   contrast over border. Swap in primary here if the section ever wants to be
   louder; accent is not an option, it fails on white.

   The transform is safe here in a way it is not elsewhere in this file: a
   pseudo-element has no descendants, so the containing block it creates can
   never catch the header's `position: fixed` overlay.

   The copy is inset from its own rule, so the rule reads as the head of the
   column rather than as an underline on the first line of it. Measured off the
   reference: 24px, against a 24px gap between the rules themselves, which is
   what makes two neighbouring columns read as 48px apart in the text and 24px
   apart in the rules.

   That inset is here rather than as a padding attribute on the block because it
   is wrong once the columns stack: it would leave every title 24px in from a
   heading that is flush with the page, and a block attribute serialises to an
   inline style that no media query can reach. A padding set in the editor is
   also an inline style, and still wins over this. */
.vallem-feature-tabs__item {
	position: relative;
	padding-inline: var(--wp--preset--spacing--30);
}

.vallem-feature-tabs__item::before,
.vallem-feature-tabs__item::after {
	content: "";
	position: absolute;
	inset-block-start: 0;
	inset-inline-start: 0;
	inline-size: 100%;
}

.vallem-feature-tabs__item::before {
	block-size: 1px;
	background-color: var(--wp--preset--color--border);
}

/* Brand blue, not ink. The bar is the only moving thing in the section, so it
   is the one place the section gets to say which brand it belongs to. The 1px
   track above it stays on border, so the unfilled part still reads as a rule
   rather than as a pale blue bar. */
.vallem-feature-tabs__item::after {
	block-size: 2px;
	background-color: var(--wp--preset--color--primary);
	transform: scaleX(0);
	transform-origin: left center;
}

/* No script: the first column is drawn to match the first image, so the two
   halves of the section agree about which item is on show. */
.vallem-feature-tabs:not(.is-enhanced) .vallem-feature-tabs__item:first-child::after {
	transform: scaleX(1);
}

@keyframes vallem-feature-tabs-fill {
	from {
		transform: scaleX(0);
	}

	to {
		transform: scaleX(1);
	}
}

/* `forwards`, so the bar stays full for the frame between the animation ending
   and the script moving the class along. Without it the bar snaps back to empty
   and flickers on every change. */
.vallem-feature-tabs.is-enhanced .vallem-feature-tabs__item.is-active::after {
	animation: vallem-feature-tabs-fill var(--vallem-feature-tabs-dwell) linear forwards;
}

/* One switch for every reason to stop: the pointer is on the section, the focus
   is inside it, or the section is not on screen. The script works out which and
   sets the class; the animation keeps its place either way. */
.vallem-feature-tabs.is-enhanced.is-paused .vallem-feature-tabs__item.is-active::after {
	animation-play-state: paused;
}

/* An item is only worth pointing at once the script can act on the click. */
.vallem-feature-tabs.is-enhanced .vallem-feature-tabs__item {
	cursor: pointer;
}

.vallem-feature-tabs__item:focus-visible {
	outline: 2px solid currentcolor;
	outline-offset: 4px;
}

/* Secondary copy by opacity and not by colour, so it follows whatever colour
   the section is set to instead of hardcoding dark-on-light. */
.vallem-feature-tabs__note {
	opacity: 0.72;
}

/* ------------------------------------------------------------------------- *
 * Narrow
 * ------------------------------------------------------------------------- */

@media (width <= 781px) {
	/* One column. Above this the flex basis sorts the row out by itself, but
	   between roughly 700 and 800px three columns of 13rem no longer fit and
	   the row would break to two and one, which reads as a mistake. */
	.vallem-feature-tabs__items > * {
		flex-basis: 100%;
	}

	/* Stacked, the inset has nothing to hold a column apart from and only
	   leaves every title standing 24px in from the heading above it. */
	.vallem-feature-tabs__item {
		padding-inline: 0;
	}

	/* A 2.4 panel is a 100px letterbox on a phone. Taller instead, which also
	   turns the per-item crop into a horizontal one and keeps it visible. */
	.vallem-feature-tabs__shot img {
		aspect-ratio: 4 / 3;
	}
}

/* ------------------------------------------------------------------------- *
 * Reduced motion
 * ------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
	/* No autoplay and no fill. The active item still gets its rule drawn, so the
	   state survives; it arrives instead of travelling. And because the script
	   advances on `animationend`, taking the animation away is also what stops
	   the section moving on its own: it only changes on a real click now. */
	.vallem-feature-tabs.is-enhanced .vallem-feature-tabs__item.is-active::after {
		animation: none;
		transform: scaleX(1);
	}

	.vallem-feature-tabs__shot {
		transition: none;
	}
}

/* ------------------------------------------------------------------------- *
 * Zo werkt het. Numbered steps.
 *
 * Three patterns share this block: section-how-it-works.php, three steps in
 * three columns, section-how-it-works-quad.php, four steps in two columns, and
 * section-how-it-works-duo.php, two steps in two columns. They differ only in a
 * step count and a columnCount and share a body in
 * inc/pattern-parts/how-it-works.php.
 *
 * A heading, then a row of steps. Each step is a dark card the picture fills
 * edge to edge, and under the card a numbered title, a short paragraph and a
 * list of points marked with a thin rule instead of a bullet.
 *
 * Written so that nothing here knows how many steps there are, which is what
 * lets one block of CSS serve all three patterns and lets a person duplicate a
 * step in any of them. The tracks come from columnCount and minimumColumnWidth
 * on the block and the per-card image crop is a cycle rather than a list. Same
 * arrangement, and the same reasoning, as .vallem-process__steps further up
 * this file.
 * ------------------------------------------------------------------------- */

/* The heading wraps at the measure the type wants rather than at whatever
   fraction of the row it happens to occupy. Borrowed from .vallem-process__title,
   which arrived at the same number for the same reason. */
.vallem-howto__title {
	max-width: 620px;
}

/* The row is core's grid layout now, so the tracks come from columnCount and
   minimumColumnWidth on the block and the equal-share flex is gone. min-width
   stays: a grid item's min-width is auto as well, so a long compound in a step
   title would still push it out of its column. */
.vallem-howto__grid > * {
	min-width: 0;
}

/* The row gap is here and the column gap is not, and that is deliberate. A
   two-value blockGap on a grid row carrying both column attributes makes 7.0.4
   substitute the whole gap string into its column formula, emit invalid CSS and
   collapse the row to one column, so the block ships a single-value gap and the
   taller gap between wrapped rows is this rule. It is the one dimension in this
   section that is not editor-controllable: changing blockGap moves the columns
   apart and leaves the rows where they are.

   Two classes, not one. Core writes its gap on its own container hash class at
   (0,1,0), and in the EDITOR CANVAS that sheet is injected after this file,
   which is the reverse of the front end. A single-class row-gap measured 64px on
   the front end and 24px in the canvas for the same row. At (0,2,0) it is 64px
   in both. */
.wp-block-group.vallem-howto__grid {
	row-gap: var(--wp--preset--spacing--60);
}

/* Pages built before the row became a grid still carry the flex markup and would
   sit in one column without this. `is-layout-grid` is added by the server for
   grid layout only, so this matches the old rows and never the new ones. */
.vallem-howto__grid:not(.is-layout-grid) > * {
	flex: 1 1 0;
}

/* The stage the mock sits on. Its height is not set here: it comes from the
   image's own aspect ratio, which is a block attribute, so the card follows
   whatever ratio the editor sets.

   The picture is flush with all four edges, so this clip is what draws its
   corners: placeholder.png is square-cornered and the image carries no radius
   of its own. It stays a group with a background colour rather than becoming a
   bare image, because the colour is the fallback while somebody is swapping the
   picture and because the padding control has to keep working. The padding is
   set to zero in the pattern, not removed from it.

   8px is the outer card plate on the scale section-feature-duo sets, see
   .vallem-feature-duo__card above. It is a default, not a lock: the border
   control on the group lands as an inline style and still wins. */
.vallem-howto__card {
	border-radius: 8px;
	overflow: hidden;
}

/* Core gives every image block a bottom margin, and with the card's padding at
   zero that margin is the one thing between the picture and the card's bottom
   edge: leave it in and a strip of primary-darkest shows under every card.
   Same reason the hero zeroes it. */
.vallem-howto__shot {
	margin: 0;
}

/* Width from the column, height from the aspect ratio on the block. No radius:
   the picture runs to all four edges and the card's own clip draws its corners,
   so a radius here would round an already rounded corner. */
.vallem-howto__shot img {
	display: block;
	width: 100%;
}

/* The mock is wider than any of the slots it goes in, 4/5 at three across and
   4/3 at two, so every card shows a crop of it and cards side by side would
   otherwise show the same crop. This walks the crop across the file, left,
   middle, right.

   A cycle of three, not a list of three: it counts DOM order, which a grid does
   not change, so a fifth card starts it again and a deleted card renumbers the
   rest and the rotation survives editing. It also lands well on all three
   patterns. Two cards get 0% and 50%. Four in a two by two get [0%, 50%] over
   [100%, 0%], where the repeat is diagonal and no card sits next to its own
   crop. A cycle of two would repeat straight down the columns of that two by
   two and a cycle of four would hand the three-across pattern a fourth crop it
   has nowhere to put. */
.vallem-howto__grid > :nth-child(3n + 1) .vallem-howto__shot img {
	object-position: 0% 50%;
}

.vallem-howto__grid > :nth-child(3n + 2) .vallem-howto__shot img {
	object-position: 50% 50%;
}

.vallem-howto__grid > :nth-child(3n + 3) .vallem-howto__shot img {
	object-position: 100% 50%;
}

/* Muted with opacity, not with a colour. A muted colour hardcodes dark-on-light
   and breaks the moment the section is recoloured; opacity follows whatever
   colour is inherited. */
.vallem-howto__body {
	opacity: 0.72;
}

/* The points. An ordinary core/list, so pressing Enter adds a fourth one, with
   the mark drawn on the item the way .vallem-tick-list draws its tick.

   Its own class rather than vallem-tick-list, which two other sections already
   use and which paints a disc: this one is a hairline rule, which is what the
   reference has and what keeps the list quiet under the paragraph. */
.vallem-howto__points {
	--vallem-howto-rule-width: 2px;
	--vallem-howto-rule-height: 1em;

	margin-block: 0;
	padding-inline-start: 0;
	list-style: none;
}

.vallem-howto__points li {
	display: flex;
	align-items: flex-start;
	gap: var(--wp--preset--spacing--20);
}

.vallem-howto__points li + li {
	margin-block-start: var(--wp--preset--spacing--10);
}

/* currentcolor at low opacity rather than the border slug, for the same reason
   the paragraph above is muted with opacity: the mark then survives the section
   being recoloured, and it is one definition instead of a light one and a dark
   one. */
.vallem-howto__points li::before {
	content: "";
	flex: none;
	inline-size: var(--vallem-howto-rule-width);
	block-size: var(--vallem-howto-rule-height);
	background: currentcolor;
	opacity: 0.3;

	/* Centred on the first line rather than on the whole item, so a point that
	   wraps keeps its rule beside the words it belongs to. The em value is the
	   fallback for browsers without lh. */
	margin-block-start: 0.3em;
}

@supports (height: 1lh) {
	.vallem-howto__points li::before {
		margin-block-start: calc((1lh - var(--vallem-howto-rule-height)) / 2);
	}
}

/* Stacked, a share of the row means nothing, so each step takes the page. Only
   for pages built before the row became a grid: on a grid, minimumColumnWidth
   drops the row to one column at about 786px, which is where this stacked it. */
@media (width <= 781px) {
	.vallem-howto__grid:not(.is-layout-grid) > * {
		flex-basis: 100%;
	}
}

/* ------------------------------------------------------------------------- *
 * Feature box. See patterns/section-feature-box.php
 *
 * A plate on the page, not a stripe across it: the colour stops at the content
 * line and there is page either side of it. That is the whole difference
 * between this and the academy band, and it is one rule, on the inner group.
 * The outer group stays uncoloured and only carries the section's own padding.
 *
 * Not one colour is written in this block. The plate takes its background and
 * its text from a block style variation, styles/blocks/plate-{dark,light,mix}
 * .json, which is why the pattern sets no backgroundColor and no textColor: a
 * preset class carries !important and the variations could then never change
 * anything. The one exception is the mixed plate's dark panel at the bottom of
 * this block, which has no block to sit on.
 *
 * The picture runs off the right edge and is cut by it. Grid areas plus
 * overflow on the plate, not absolute positioning, so the copy column still
 * sets the height of the box and nothing has to be measured. Same arrangement
 * as .vallem-partner-hero.
 * ------------------------------------------------------------------------- */

/* Two classes. Enough, because the plate is not .alignfull, so core's alignment
   rules never reach it and there is nothing here to out-specify.

   The inset is the plate's own padding and it is here rather than on the block
   so that it can shrink on a phone; block spacing serialises to an inline style,
   which no media query can reach. The cost is that the Dimensions panel reads
   empty for this group. A value set there still wins, which is the right way
   round. The end is zero because the picture's column has to reach the edge of
   the plate for the cut to happen. */
.vallem-feature-box .vallem-feature-box__plate {
	--vallem-feature-box-inset: var(--wp--preset--spacing--70);

	max-width: var(--vallem-content-line);
	margin-inline: auto;
	padding-block: var(--vallem-feature-box-inset);
	padding-inline-start: var(--vallem-feature-box-inset);
	padding-inline-end: 0;

	display: grid;

	/* 46/54, measured off the reference: its seam sits at 51% of the plate and
	   the picture shows a little under half of it. An even split plus the gap
	   below put the seam at 56% and left the picture visibly smaller than the
	   reference's. minmax(0, ...) rather than a bare fr because an auto minimum
	   would measure the oversized picture and widen its own column to fit it,
	   which is exactly what must not happen. */
	grid-template-columns: minmax(0, 46fr) minmax(0, 54fr);
	align-items: center;

	/* A gap, not slack. Above 1280 the copy is held well short of its column by
	   its own measure and the gap is invisible; just above the stacking point
	   the column is narrower than that measure, and without this the sentences
	   run straight into the edge of the picture. Measured at 800px before the
	   stacking point moved. */
	column-gap: var(--wp--preset--spacing--60);

	/* The cut. Whatever the picture pushes past the right edge is clipped by the
	   plate's own corner, so the crop follows the radius for free and the
	   overhang can never reach the page and start a horizontal scrollbar.
	   overflow is not one of the properties that creates a containing block for
	   position: fixed, so the mobile nav overlay is unaffected. */
	overflow: hidden;
}

/* WordPress renders this group as is-layout-flow whatever the CSS does with it,
   so its children carry the block gap as a top margin. On a grid that is an
   offset inside the cell rather than a gap between rows. */
.vallem-feature-box__plate > * {
	margin-block: 0;
}

/* The measure the type wants, not a fraction of the column. Holds the heading to
   about three lines at display-medium, which is what the reference shows, and
   leaves the slack between the copy and the picture that the reference has. */
.vallem-feature-box__copy {
	grid-area: 1 / 1 / 2 / 2;
	max-width: 27rem;
}

/* Opacity, not a colour. A muted grey would hardcode light-on-dark and break the
   moment the plate is switched to the light variation; opacity follows whatever
   colour the plate inherits down. */
.vallem-feature-box__body {
	opacity: 0.72;
}

/* Wider than the column it sits in, which is the whole of the bleed. Measured at
   1440: an 844px picture in a 639px column, so 205px of it is cut away, which is
   about what the reference loses. justify-self keeps it pinned to the left of
   its cell so the overhang all goes to the right. */
.vallem-feature-box__media {
	grid-area: 1 / 2 / 2 / 3;
	align-self: stretch;
	justify-self: start;
	inline-size: 132%;
}

/* Fills the cell at any shape without anyone setting a ratio, the same pair of
   rules the partner hero uses. The file's own height is what holds the plate
   open before the copy is long enough to do it. */
.vallem-feature-box__media,
.vallem-feature-box__media img {
	display: block;
	block-size: 100%;
	object-fit: cover;
}

.vallem-feature-box__media img {
	inline-size: 100%;

	/* The file is 1200x800 and the slot ends up taller than that once the copy
	   column grows, so the crop is taken from the left: the sidebar and the
	   start of the dashboard, which is the part that stays inside the plate. */
	object-position: left center;

	/* A default, not a lock. The border control on the image block lands as an
	   inline style and still wins. Only the left pair of corners is ever seen;
	   the right pair is outside the plate. */
	border-radius: 6px;
}

/* ---- The field behind the picture ---------------------------------------- *
 *
 * Was a ::before on the plate, painting the right-hand half of it. Half a plate
 * of colour with no block underneath is a colour nobody can select, recolour or
 * remove, so it is an ordinary empty core/group now, sitting in the picture's
 * own grid cell and first in the document so the picture paints over it. It
 * ships the same colour as the plate and is therefore invisible as delivered;
 * recolour the plate and it becomes the seam .vallem-partner-hero draws between
 * its panel and its photo.
 *
 * No colour here. The group carries backgroundColor, which is the point.
 *
 * Two classes, because .vallem-feature-box__plate > * zeroes the block margins
 * at one, and the negative margins are what take the field back out through the
 * plate's padding so it runs the full height of the box.
 * ------------------------------------------------------------------------- */
.vallem-feature-box .vallem-feature-box__panel {
	grid-area: 1 / 2 / 2 / 3;
	align-self: stretch;
	justify-self: stretch;
	margin-block: calc(-1 * var(--vallem-feature-box-inset));
}

/* ---- Stacked ------------------------------------------------------------- *
 *
 * The bleed stops and the picture goes under the copy. 1024, not 781, because
 * the heading is display-medium and the column has to hold it: at 800 the copy
 * column is 336px, which puts the heading on five lines and leaves the points
 * wrapping every second word. It is the same point the academy band flips at.
 *
 * A picture that runs off the edge of a plate on a phone is a horizontal
 * scrollbar waiting to happen, and there is no width to spare for an overhang
 * nobody can see.
 * ------------------------------------------------------------------------- */
@media (width <= 1024px) {
	.vallem-feature-box .vallem-feature-box__plate {
		/* Step 6, not 5: it is 61px at 1024 and 40px at 390, so a plate that is
		   nearly a thousand pixels wide keeps an inset in the region of the 96px
		   it had a pixel earlier, and a phone gets one it can afford. */
		--vallem-feature-box-inset: var(--wp--preset--spacing--60);

		grid-template-columns: minmax(0, 1fr);
		gap: var(--wp--preset--spacing--40);
		padding-inline-end: var(--vallem-feature-box-inset);
	}

	/* Both rows written out rather than left to auto placement. The mixed
	   plate's panel is a pseudo-element with a grid area of its own, and auto
	   placement walks past an occupied cell: on `auto` the picture was pushed
	   into a third row below the panel instead of sitting on it. Seen, not
	   reasoned about.

	   The measure is not `none`: stacked at 1000px the plate is nearly 940px
	   wide and the copy would run the whole of it, well past anything readable,
	   so it falls back to the site's own prose measure. */
	.vallem-feature-box__copy {
		grid-area: 1 / 1 / 2 / 2;
		max-width: var(--wp--style--global--content-size);
	}

	.vallem-feature-box__media {
		grid-area: 2 / 1 / 3 / 2;
		align-self: auto;
		justify-self: stretch;
		inline-size: 100%;
	}

	/* Back to an ordinary picture: there is no cell height to fill any more, so
	   the ratio has to come from the picture itself. */
	.vallem-feature-box__media,
	.vallem-feature-box__media img {
		block-size: auto;
	}

	.vallem-feature-box__media img {
		aspect-ratio: 16 / 10;
	}

	/* Stacked, the seam is horizontal: the field starts halfway up the gap above
	   the picture and runs to the bottom edge of the plate. */
	.vallem-feature-box .vallem-feature-box__panel {
		grid-area: 2 / 1 / 3 / 2;
		margin-block: calc(-1 * var(--wp--preset--spacing--20)) calc(-1 * var(--vallem-feature-box-inset));
		margin-inline: calc(-1 * var(--vallem-feature-box-inset));
	}
}

/* ------------------------------------------------------------------------- *
 * Sectie, feature-band. See patterns/section-feature-band.php
 *
 * The full-bleed twin of the feature box. Two columns on one grid: copy on the
 * left against the content line, and a panel on the right holding the product
 * image and a row of logos under it. The image is wider than the column it sits
 * in, so it runs past the right edge of the screen and the section clips it.
 *
 * Overlap and bleed by grid, not by absolute positioning, the way
 * .vallem-partner-hero does it. Nothing is measured against anything else and
 * the taller of the two columns sets the height of the band.
 *
 * Nothing here styles .vallem-points or the button. The points are a shared
 * list with its own block in this file, and the four button variations already
 * carry their own look; both would drift the moment a second copy existed.
 * ------------------------------------------------------------------------- */

.vallem-feature-band {
	/* Where the copy starts: the content line, or the page gutter once the
	   viewport is narrower than it. Measured against 100vw and not against a
	   percentage, because the section runs the full width of the viewport
	   regardless of what the page around it is doing. */
	--vallem-feature-band-inset: max(
		var(--wp--preset--spacing--50),
		calc((100vw - var(--vallem-content-line)) / 2)
	);

	/* How far the image runs past its own column, and so past the right edge of
	   the screen. A fixed amount rather than a share of the viewport, so the
	   crop stays the same on a 1440 laptop and on a 2560 monitor instead of
	   quietly disappearing on the wide one. The vw term is only there to give
	   it back gracefully on a narrow desktop. */
	--vallem-feature-band-bleed: min(22rem, 26vw);

	display: grid;
	grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
	align-items: center;
	gap: var(--wp--preset--spacing--60);

	/* Start only. The right-hand side is the bleed: the image is supposed to
	   reach the screen edge and go past it. */
	padding-inline-start: var(--vallem-feature-band-inset);

	/* What actually makes the bleed a bleed. The image overflows its column to
	   the right and this cuts it off flush at the edge of the band, so there is
	   never a horizontal scrollbar to chase. */
	overflow: hidden;
}

/* Holds the heading to two lines and the paragraphs to a readable measure at
   the top of the type scale. Wider than this and the h2 runs to one line and
   stops carrying the band. */
.vallem-feature-band__copy {
	max-width: 32rem;
}

/* Opacity, not a lighter colour. A muted colour would hardcode light-on-dark
   and break the moment the plate is recoloured; opacity follows whatever colour
   the section inherits down. */
.vallem-feature-band__body {
	opacity: 0.72;
}

/* The panel is a plain flow group so the logo row sits under the image and
   under nothing else. min-width because a grid item defaults to min-content
   width, which the oversized image inside would otherwise widen the track to. */
.vallem-feature-band__panel {
	min-width: 0;
}

/* Wider than the column it is in, which is what puts it past the screen edge.
   max-inline-size is reset because the image block ships a 100% cap that would
   pull it straight back to the column. */
/* 8px, the plate step, because the band runs to both screen edges and cannot be
   rounded itself, which leaves this picture as the outermost rounded shape in
   the section. Same call .vallem-academy__media img makes on the same kind of
   full-bleed dark band. Its twin section-feature-box drops its picture to 6px
   instead, because there a real rounded plate encloses it. */
.vallem-feature-band__media {
	inline-size: calc(100% + var(--vallem-feature-band-bleed));
	max-inline-size: none;
	margin: 0;
	overflow: hidden;
	border-radius: 8px;
}

/* The mock is 1200 x 800 and this slot is wider than it is tall, so the crop is
   vertical: the top of the interface, anchored left, with the bottom of it cut
   off the way the reference cuts it. Sized here rather than through the block's
   own width and height controls, which have to match what save() emits exactly
   or the block is lost. */
.vallem-feature-band__media img {
	display: block;
	inline-size: 100%;
	block-size: clamp(16rem, 39vw, 35rem);
	object-fit: cover;
	object-position: left top;
}

/* The column the row sits in ends at the screen edge, because that is what the
   image above it needs. The logos are not bleeding, so they get the page gutter
   back. The site gutter rather than the content line: on a wide monitor the
   content line would leave the row less room than a laptop has, since the image
   column starts past it. */
.vallem-feature-band__logos {
	align-items: center;
	padding-inline-end: var(--wp--preset--spacing--50);
}

.vallem-feature-band__logo {
	margin: 0;
}

/* One height for all three, which is only optically safe because the three
   placeholders are all word-height marks; the big logo row normalises its seven
   in JavaScript because they are not.

   The height scales with the viewport rather than stepping at a breakpoint,
   because the block gap between the logos is fluid too and a fixed height plus
   a shrinking row is what wraps three logos onto two lines with one stranded.
   Measured: the three come to 10.6 times their own height side by side, which
   fits the row from 360px up.

   One ink for the whole row, the same treatment the logo row's dark scheme
   uses, so three marks with three different palettes read as one material and
   no second set of files is needed for a dark plate. Knockouts have to be
   transparent holes: a white shape flattens to black here and the mark becomes
   a solid block. Sat back to 0.76 because pure white on near-black is harsh,
   which is the value the logo row settled on for the same reason. */
.vallem-feature-band__logo img {
	display: block;
	inline-size: auto;
	block-size: clamp(1.25rem, 2.2vw, 2rem);
	max-inline-size: 100%;
	object-fit: contain;
	filter: grayscale(1) brightness(0) invert(1);
	opacity: 0.76;
}

/* Stacked. The copy leads, the image goes back to being an ordinary full-width
   image and the logos sit under it. 1024 rather than 781: below that the copy
   column is too narrow to hold a 48px heading and a points list side by side
   with an image worth looking at.

   The bleed is undone rather than clamped. An image that runs off the right of
   a phone is a horizontal scrollbar waiting to happen even with the section
   clipping it, and there is no design reason for it once the columns stack. */
@media (width < 1024px) {
	.vallem-feature-band {
		grid-template-columns: minmax(0, 1fr);
		padding-inline: var(--wp--preset--spacing--50);
	}

	/* Wider now that it has the whole band, but still short of the full width:
	   a paragraph running the width of a tablet is not a measure anyone reads. */
	.vallem-feature-band__copy {
		max-width: 40rem;
	}

	.vallem-feature-band__media {
		inline-size: 100%;
	}

	/* Its own proportions. There is nothing left to crop to, and a fixed height
	   here is a height text cannot overflow but the image can be squashed by. */
	.vallem-feature-band__media img {
		block-size: auto;
		object-fit: fill;
	}

	/* The section pads both sides once it is stacked, so the row would
	   otherwise carry the gutter twice on the right. */
	.vallem-feature-band__logos {
		padding-inline-end: 0;
	}
}

/* ------------------------------------------------------------------------- *
 * Feature points. See patterns/list-feature-points.php
 *
 * A group per point rather than a list, because each line picks its own icon and
 * an image cannot live inside a core/list-item. Shared by the feature box and
 * the feature band, which both embed the pattern's markup verbatim, so nothing
 * here may be scoped to either of them.
 * ------------------------------------------------------------------------- */

.vallem-points__item {
	align-items: start;
}

.vallem-points__icon {
	flex: none;
	margin: 0;

	/* Centred on the first line of the sentence rather than on the whole point,
	   so a point that wraps keeps its icon beside the words it belongs to. The
	   em value is the fallback for browsers without lh. */
	margin-block-start: 0.15em;
}

@supports (height: 1lh) {
	.vallem-points__icon {
		margin-block-start: calc((1lh - var(--vallem-points-icon-size)) / 2);
	}
}

.vallem-points {
	--vallem-points-icon-size: 24px;
}

/* Sized here rather than through the image block's width control. That control
   serialises width and height into the block markup, which then has to match
   what save() emits exactly or the block is lost; sizing in CSS has no such
   contract. A size set in the editor is an inline style and still wins. */
.vallem-points__icon img {
	display: block;
	inline-size: var(--vallem-points-icon-size);
	block-size: var(--vallem-points-icon-size);
	object-fit: contain;
}


/* ------------------------------------------------------------------------- *
 * Partnerkaarten. See patterns/section-partner-cards.php
 *                     patterns/card-media-split.php
 *
 * Two patterns, one stylesheet block, because they are one thing: a section
 * that stacks cards, and the card it stacks. The section pattern ships with a
 * single card and inserts card-media-split.php with require, so the markup
 * cannot drift between them.
 *
 * Ported from uniqkey.eu/partner (.header84_card and .feature_vertical-flex),
 * measured in that site's own stylesheet rather than estimated from a
 * screenshot. What came across is the construction:
 *
 *   - one grid, two equal columns, the card clipping its own overflow
 *   - the photo inset from the plate edge by a fixed gap, corner rounded
 *   - a second, smaller image absolutely positioned so it overhangs the seam
 *     into the copy column, and can run off the bottom edge to be cut by the
 *     plate
 *   - a small tag, a heading, one paragraph, one arrow call to action
 *   - 16px between stacked cards, two columns collapsing to one on a phone
 *
 * What did not come across is the look. The reference's plate is #f9f9f9 with a
 * #f1f1f1 chip on it, i.e. two off-whites a hair apart; this site's plate is
 * `surface` and the chip is `base`, so the relationship is inverted and the
 * chip is the lighter of the two. Its tag is a 4px rounded rectangle at 10px;
 * ours is a pill at `small`, matching hero-split's announcement pill, because
 * 10px is below the type scale and the pill is the shape this site already
 * uses for an eyebrow. Its media wrapper is inset 24px on three sides and 0 on
 * the fourth, which looks like an oversight rather than a decision, so ours is
 * even on all four.
 *
 * Nothing here sets a colour on the copy. The plate carries `backgroundColor`
 * and `textColor` as presets on the block and every child inherits, so
 * recolouring a whole card is one control. The two exceptions are named where
 * they occur: the arrow chip, which has no block to sit on, and the tag, which
 * has its own plate.
 * ------------------------------------------------------------------------- */

/* ---- The section -------------------------------------------------------- */

/* The stack, capped to the content line. Two classes is enough: the stack is
   not .alignfull, so core's alignment rules never reach it and there is
   nothing to out-specify. The gap between cards is blockGap on the block, not
   here, so it stays in the editor's Dimensions panel. */
.vallem-partner-cards .vallem-partner-cards__stack {
	max-width: var(--vallem-content-line);
	margin-inline: auto;
}

/* ---- The card ----------------------------------------------------------- */

.vallem-media-card {
	/* ---- The knobs -----------------------------------------------------
	 * --vallem-media-card-media-inset     air between the plate edge and the
	 *                                     large photo
	 * --vallem-media-card-copy-inset      air between the plate edge and the
	 *                                     copy, on the three outer sides
	 * --vallem-media-card-copy-gutter     air on the copy's inner side, the one
	 *                                     the small image overhangs. Larger
	 *                                     than the inset on purpose, as in the
	 *                                     reference (8rem against 4.6rem)
	 * --vallem-media-card-measure         how wide the copy is allowed to get
	 * --vallem-media-card-photo-height    height of the large photo. The card
	 *                                     is this plus two insets, unless the
	 *                                     copy is taller
	 * --vallem-media-card-photo-radius    corner of the large photo
	 * --vallem-media-card-overlay-width   width of the small image, as a share
	 *                                     of the media column
	 * --vallem-media-card-overlay-bleed   how far it sticks out past the media
	 *                                     column, into the copy. 0 keeps it
	 *                                     inside
	 * --vallem-media-card-overlay-top     where its top edge hangs, as a share
	 *                                     of the card's height. A large value
	 *                                     pushes it out of the bottom of the
	 *                                     plate, where the plate cuts it off,
	 *                                     which is what the reference's phone
	 *                                     mock does
	 * --vallem-media-card-overlay-radius  corner of the small image
	 *
	 * Set any of them on the card in the editor's Advanced panel or in a style
	 * variation; none of the selectors below need rewriting.
	 *
	 * Both radiuses are 6px and the plate's own is 8px, set in
	 * styles/blocks/media-left.json and media-right.json. That is the scale
	 * section-feature-duo uses, 8 on the outer plate and 6 on the panel inside
	 * it, and this card was moved onto it because the two sections sit on the
	 * same page and read as one family. They were 24 and 12, which was a
	 * different rounding language at every level.
	 * -------------------------------------------------------------------- */
	--vallem-media-card-media-inset: 24px;
	--vallem-media-card-copy-inset: var(--wp--preset--spacing--60);
	--vallem-media-card-copy-gutter: var(--wp--preset--spacing--70);
	--vallem-media-card-measure: 28rem;
	--vallem-media-card-photo-height: 22rem;
	--vallem-media-card-photo-radius: 6px;
	--vallem-media-card-overlay-width: 58%;
	--vallem-media-card-overlay-bleed: 10%;
	--vallem-media-card-overlay-top: 16%;
	--vallem-media-card-overlay-radius: 6px;

	display: grid;
	grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
	align-items: stretch;

	/* The clip. The small image is drawn overhanging the seam and past the
	   bottom edge, and this is what cuts it off, so "runs off the card" and
	   "cut by the plate" are the same edge. The corner comes from the block
	   style variation, so the clip follows whatever radius is set. */
	overflow: hidden;

	/* Holds its shape if someone deletes the photo. Dimensions set in the
	   editor still wins, because block spacing serialises inline. */
	min-block-size: 22rem;
}

/* Standalone. Dropped straight onto a page the card is a child of the post
   content, which is a constrained layout with global padding, so alignfull
   gives it negative margins out to the screen edge. That is one step too far:
   the reference sits inside a page container. Pulled back to the content line
   here, with the page gutter kept below that width.

   Four classes, because core's `.has-global-padding > .alignfull` is (0,2,0)
   and a lighter selector would lose and pin the card to the left edge.

   Inside the section pattern this rule does not match at all: the stack is a
   flow layout without global padding, core's alignment rules only exist for
   constrained layouts, so the alignfull class is inert there and the card
   simply fills the stack. Both routes end up the same width, which is the
   point of doing it this way rather than shipping the card unaligned. */
.has-global-padding > .wp-block-group.vallem-media-card.alignfull {
	max-width: min(
		var(--vallem-content-line),
		calc(100vw - 2 * var(--wp--preset--spacing--50))
	);
	margin-inline: auto;
}

/* Three classes, and this one is measured rather than defensive. Core's flow
   layout puts a block gap on every child of a group, and the rule that zeroes
   it on the last one reads `:root :where(.is-layout-flow) > :last-child`. The
   `:last-child` sits outside the `:where()`, so with `:root` that is (0,2,0)
   and a two-class selector only ties it. On a grid those margins are real
   space inside the row, so they have to go. */
.wp-block-group.vallem-media-card > .vallem-media-card__copy,
.wp-block-group.vallem-media-card > .vallem-media-card__media {
	margin-block: 0;
}

/* ---- Left or right ------------------------------------------------------ *
 *
 * The whole of the flip. Two grid areas swapped, nothing else, which is why it
 * is one click in the Styles tab and not a second pattern. The copy stays
 * first in the source in both cases: the images are decorative, so their
 * position in the document is invisible to a screen reader, and reading order
 * should follow the words.
 *
 * The corner radius of the plate lives in the two variation files rather than
 * here, because a variation with only slug/title/blockTypes and no styles
 * silently fails to register. It needed one real property, and the radius is a
 * real property this card wants a default for.
 * ------------------------------------------------------------------------- */

.vallem-media-card.is-style-media-left .vallem-media-card__media {
	grid-area: 1 / 1 / 2 / 2;
}

.vallem-media-card.is-style-media-left .vallem-media-card__copy {
	grid-area: 1 / 2 / 2 / 3;
	padding-inline: var(--vallem-media-card-copy-gutter) var(--vallem-media-card-copy-inset);
}

.vallem-media-card.is-style-media-right .vallem-media-card__media {
	grid-area: 1 / 2 / 2 / 3;
}

.vallem-media-card.is-style-media-right .vallem-media-card__copy {
	grid-area: 1 / 1 / 2 / 2;
	padding-inline: var(--vallem-media-card-copy-inset) var(--vallem-media-card-copy-gutter);
}

/* The small image leans towards the copy, so which way it leans is the other
   half of the flip. The bleed is written as a positive number and negated
   here, so the knob reads "how far it sticks out" rather than "how negative". */
.vallem-media-card.is-style-media-left .vallem-media-card__overlay {
	inset-inline-end: calc(-1 * var(--vallem-media-card-overlay-bleed));
}

.vallem-media-card.is-style-media-right .vallem-media-card__overlay {
	inset-inline-start: calc(-1 * var(--vallem-media-card-overlay-bleed));
}

/* ---- The media side ----------------------------------------------------- */

/* Stretched to the card, with the photo centred inside it, so the percentages
   the small image is placed with resolve against the height of the card and
   not against the height of the photo. That is what keeps the overlap in the
   same place when the copy column turns out to be the taller of the two. */
.vallem-media-card .vallem-media-card__media {
	position: relative;
	display: flex;
	align-items: center;
	padding: var(--vallem-media-card-media-inset);
}

.vallem-media-card .vallem-media-card__photo,
.vallem-media-card .vallem-media-card__overlay {
	margin: 0;
}

/* The large slot is meant for a photograph. placeholder.png ships square-cornered
   and full-bleed on purpose, so the corner and the crop are here.

   object-position is left top: the file is 1200 x 800 and this slot is wider
   than that ratio, so cover crops the bottom and keeps the window chrome and
   the sidebar. The small slot below takes a different corner of the same file,
   so the pair does not read as one picture printed twice. */
.vallem-media-card .vallem-media-card__photo {
	inline-size: 100%;
}

.vallem-media-card .vallem-media-card__photo img {
	display: block;
	inline-size: 100%;
	block-size: var(--vallem-media-card-photo-height);
	object-fit: cover;
	object-position: left top;
	border-radius: var(--vallem-media-card-photo-radius);
}

/* The small slot is meant for a product shot. Absolutely positioned, and that
   is the one place in this section where grid areas were the wrong tool: it
   has to be able to hang out of the bottom of the plate and be cut off, and an
   item in a grid area would make the card taller instead. It also means
   deleting it leaves the layout exactly as it was.

   The shadow is the one .vallem-partner-hero__card already uses. Nothing new.

   5/4 against the file's own 8/5 is a hard crop, and right bottom takes the far
   corner of the dashboard, so this reads as a different image from the one
   behind it. */
.vallem-media-card .vallem-media-card__overlay {
	position: absolute;
	inset-block-start: var(--vallem-media-card-overlay-top);
	inline-size: var(--vallem-media-card-overlay-width);
	border-radius: var(--vallem-media-card-overlay-radius);
	overflow: hidden;
	box-shadow: 0 1px 2px rgb(7 26 58 / 8%), 0 24px 48px rgb(7 26 58 / 16%);
}

.vallem-media-card .vallem-media-card__overlay img {
	display: block;
	inline-size: 100%;
	aspect-ratio: 5 / 4;
	object-fit: cover;
	object-position: right bottom;
}

/* ---- The copy side ------------------------------------------------------ */

/* Centred against the media, as in the reference. Core's flow layout spaces
   the children with margins rather than gap, and margins on flex items do not
   collapse, so the block gap set on the group still measures exactly what it
   says. */
.vallem-media-card .vallem-media-card__copy {
	display: flex;
	flex-direction: column;
	justify-content: center;
	padding-block: var(--vallem-media-card-copy-inset);
}

/* The measure caps the children and not the group, because box-sizing is
   border-box here: a max-width on the group would have the two insets counted
   inside it and the text would come out a good deal narrower than the number
   says. Measured, after the first render came back with a 320px measure where
   28rem had been asked for.

   Inside a flex column a child with a max-width sizes to it and sits at the
   start of the cross axis, so this leaves everything hard left without any
   margin work. */
.vallem-media-card .vallem-media-card__copy > * {
	max-width: var(--vallem-media-card-measure);
}

/* Quieter than the heading without being a second colour. A muted preset would
   hardcode dark-on-light and break the moment the plate is recoloured; opacity
   follows whatever colour is inherited. */
.vallem-media-card .vallem-media-card__body {
	opacity: 0.72;
}

/* The tag shrinks to its own text. It used to sit in an anonymous flex group for
   exactly this, which cost a level in the block list and an unnamed "Groep"
   inside it, for one CSS property. Measured unwrapped and wrapped, the pill
   renders identically at [78,228,319,28], so the wrapper is gone and this rule
   is the whole of what it was doing.

   Its own layout is flex, so this is the width of a flex container and not of a
   flex item. Pages built before this change still carry the wrapper, and there
   the pill is a flex item whose base size is already fit-content, so this is a
   no-op on them rather than a regression. */
.vallem-media-card .vallem-media-card__tag {
	width: fit-content;
}

/* ---- The call to action ------------------------------------------------- *
 *
 * A fifth button style variation, styles/blocks/button-arrow-lead.json. The
 * reference's CTA is a 24px dark rounded square holding an arrow with the label
 * beside it, and none of the four button styles this theme ships is that
 * shape: primary, inverted and outline all draw a frame around the label, and
 * subtle draws nothing at all.
 *
 * The variation file only registers the style and states the intent. The look
 * has to be here, for the same reason the outline variation is owned outright
 * in this file rather than adjusted in theme.json: variation CSS is emitted as
 * `:root :where(.wp-block-button.is-style-arrow-lead--N .wp-block-button__link)`,
 * which is (0,1,0), and core's own button rules are heavier. The hover state has
 * to be here as well, because WordPress emits no pseudo states for block style
 * variations at all.
 *
 * The :hover selector is not optional. theme.json's button hover comes out as
 * `:root :where(.wp-element-button:hover, .wp-block-button__link:hover)`, read
 * off the generated stylesheet rather than guessed. Everything inside :where()
 * counts for nothing, so that is (0,1,0) and it would otherwise fill the
 * transparent button in blue under the pointer. The selectors below are (0,3,1).
 *
 * Two properties carry the colours, because there is no block to hang them on:
 * a preset on the button block would colour the label and the plate behind it,
 * not the chip. Set them on the card, or on the button, to recolour the chip.
 * ------------------------------------------------------------------------- */

.wp-block-button.is-style-arrow-lead > .wp-block-button__link {
	--vallem-arrow-lead-plate: var(--wp--preset--color--contrast);
	--vallem-arrow-lead-plate-hover: var(--wp--preset--color--primary);
	--vallem-arrow-lead-ink: var(--wp--preset--color--base);
	--vallem-arrow-lead-chip: 1.75rem;
	--vallem-arrow-lead-chip-radius: 8px;

	position: relative;
	display: inline-flex;
	align-items: center;
	gap: 0.5rem;
	padding: 0;
	background-color: transparent;

	/* currentcolor rather than a preset, so one variation serves a light plate
	   and a dark one. Same arrangement as is-style-subtle. */
	color: currentcolor;
}

.wp-block-button.is-style-arrow-lead > .wp-block-button__link:hover,
.wp-block-button.is-style-arrow-lead > .wp-block-button__link:focus-visible {
	background-color: transparent;
	color: currentcolor;
}

/* The chip. */
.wp-block-button.is-style-arrow-lead > .wp-block-button__link::before {
	content: "";
	flex: none;
	inline-size: var(--vallem-arrow-lead-chip);
	block-size: var(--vallem-arrow-lead-chip);
	border-radius: var(--vallem-arrow-lead-chip-radius);
	background-color: var(--vallem-arrow-lead-plate);
	transition: background-color 200ms ease;
}

/* The only feedback the pointer gets, since the label's roll is the theme's own
   and the press is already global. */
.wp-block-button.is-style-arrow-lead > .wp-block-button__link:hover::before,
.wp-block-button.is-style-arrow-lead > .wp-block-button__link:focus-visible::before {
	background-color: var(--vallem-arrow-lead-plate-hover);
}

/* The arrow, drawn rather than typed, for the reason already written against
   .vallem-nav__panel-title: U+2192 is outside the unicode-range of both Inter
   subsets this theme ships, so a text arrow would come from the OS fallback.
   Same glyph, same data URI. It is worth hoisting to :root the next time a
   third place needs it.

   Masked rather than drawn into the data URI so the colour stays a property.
   Centred on the chip with a negative half-size margin rather than a translate,
   because a transform here would be a second transform on a button that
   already scales under the pointer. */
.wp-block-button.is-style-arrow-lead > .wp-block-button__link::after {
	/* The glyph is --vallem-arrow, declared once on :root and shared with the
	   mega menu's panel titles. */
	--vallem-arrow-lead-glyph: 0.75rem;

	content: "";
	position: absolute;
	inset-block-start: 50%;
	inset-inline-start: calc(var(--vallem-arrow-lead-chip) / 2);
	inline-size: var(--vallem-arrow-lead-glyph);
	block-size: var(--vallem-arrow-lead-glyph);
	margin-block-start: calc(-0.5 * var(--vallem-arrow-lead-glyph));
	margin-inline-start: calc(-0.5 * var(--vallem-arrow-lead-glyph));
	background-color: var(--vallem-arrow-lead-ink);
	-webkit-mask: var(--vallem-arrow) center / contain no-repeat;
	mask: var(--vallem-arrow) center / contain no-repeat;
}

/* ---- Stacked ------------------------------------------------------------ *
 *
 * 781px, WordPress's own stacking point. The media goes on top whichever way
 * the card is aligned, because a phone reads top to bottom and the picture is
 * the thing that introduces the card.
 *
 * The sideways overhang stops outright and the small image tucks into the
 * bottom corner of the photo instead. Nothing overflows the card at any width,
 * because the card clips itself, so there is nothing that can put a horizontal
 * scrollbar on the page.
 * ------------------------------------------------------------------------- */

@media (width <= 781px) {
	.vallem-media-card {
		--vallem-media-card-photo-height: min(15rem, 44vw);
		--vallem-media-card-copy-gutter: var(--wp--preset--spacing--50);
		--vallem-media-card-copy-inset: var(--wp--preset--spacing--50);
		--vallem-media-card-overlay-width: 58%;

		grid-template-columns: minmax(0, 1fr);
		min-block-size: 0;
	}

	.vallem-media-card.is-style-media-left .vallem-media-card__media,
	.vallem-media-card.is-style-media-right .vallem-media-card__media {
		grid-area: 1 / 1 / 2 / 2;
	}

	.vallem-media-card.is-style-media-left .vallem-media-card__copy,
	.vallem-media-card.is-style-media-right .vallem-media-card__copy {
		grid-area: 2 / 1 / 3 / 2;
		padding-inline: var(--vallem-media-card-copy-inset);
	}

	.vallem-media-card .vallem-media-card__copy > * {
		max-width: none;
	}

	/* Bottom right of the media cell for both alignments. The inline offsets
	   are reset one at a time rather than with a shorthand, because the two
	   alignment rules above set only one side each. */
	.vallem-media-card.is-style-media-left .vallem-media-card__overlay,
	.vallem-media-card.is-style-media-right .vallem-media-card__overlay {
		inset-block-start: auto;
		inset-block-end: 0;
		inset-inline-start: auto;
		inset-inline-end: 0;
	}
}

/* ------------------------------------------------------------------------- *
 * Klantverhalen, overzicht. See templates/archive-vallem_use_case.html
 *
 * A port of uniqkey.eu/case-studies, measured rather than eyeballed: the page
 * was rendered at 1440, 900 and 520 and the card edges read off the pixels.
 * Their container is 1200 inside a 1440 viewport, the gutter 40, the gap
 * between cards 8, and the pairs come out at 478 and 718, which is 2fr and 3fr
 * of 1196. The rhythm is one full-width card, a 2/3 pair, a 3/2 pair, then
 * another full-width card.
 *
 * That rhythm is the whole reason this works as an archive rather than as a
 * hand-placed page. Five cards to a cycle, so a five-column grid with
 * :nth-child(5n) spans reproduces it exactly, for six stories or sixty, with
 * nobody choosing where anything goes. Positions 1 and 6 are the wide ones, and
 * inc/post-types.php orders the archive on menu_order first so which story
 * lands there is the Order field rather than a code change.
 *
 * What is ours rather than theirs: the container is 1280 (wideSize, Resend's
 * max-w-7xl), the corner is 6px (the services bento's card radius, measured off
 * Stripe), the dark ground is primary-darkest (the footer's navy and the
 * partner hero's), the accent on the heading is `accent`, which is 6.6:1 on
 * that navy and would fail on white, and the chip on the button is the same
 * construction as the reviews rail's arrows.
 *
 * The scrim is a flat gradient and not a backdrop-filter, for the same reason
 * the bento dialog is: the reference has no blur on it either.
 * ------------------------------------------------------------------------- */

.vallem-cases-hero {
	padding-block: var(--wp--preset--spacing--70);
}

/* Balanced, so the break lands between the two halves of the sentence rather
   than wherever 760px happens to fall. Without it the coloured phrase splits
   across the two lines and reads as two fragments. */
.vallem-cases-hero h1 {
	text-wrap: balance;
}

/* The button wants more air under the copy than the 30 step between the other
   three children gives it. */
.vallem-cases-hero .wp-block-buttons {
	margin-block-start: var(--wp--preset--spacing--30);
}

.vallem-cases {
	padding-block: var(--wp--preset--spacing--70);
}

/* ---- The grid ----------------------------------------------------------- *
 *
 * Five columns and 8px, so a span of 2 and a span of 3 land on the same
 * proportions the reference uses. core/post-template renders a <ul> and one
 * <li> per post, and its flow layout puts a block gap between the items, which
 * a grid does not want. Zeroing the margin is cheaper than turning the layout
 * off, and leaves the block's own Layout panel alone.
 * ------------------------------------------------------------------------- */

.vallem-cases .vallem-cases__list {
	display: grid;
	grid-template-columns: repeat(5, minmax(0, 1fr));
	gap: 8px;
	margin: 0;
	padding: 0;
	list-style: none;
}

.vallem-cases .vallem-cases__list > li {
	display: grid;
	grid-column: span 2;
	margin-block: 0;
	min-height: clamp(320px, 31vw, 440px);
}

.vallem-cases .vallem-cases__list > li:nth-child(5n + 3),
.vallem-cases .vallem-cases__list > li:nth-child(5n + 4) {
	grid-column: span 3;
}

/* The wide card, and the two counts that would otherwise leave a hole.
 *
 * Five cards to a cycle, so any count that is not a multiple of five has a tail
 * to deal with. Only two of the five remainders actually break:
 *
 *   rem 0, 1, 3   the tail already fills its rows. Nothing to do.
 *   rem 4         the last card is a span of 3 alone in a row of five. It is
 *                 promoted to full width, which also makes it a second featured
 *                 card rather than a stretched small one.
 *   rem 2         the last two would be a full-width card and a lone span of 2.
 *                 The full one steps down to a span of 3 and the pair completes
 *                 the row. That is the rule directly below this one.
 *
 * Which is why the wide card is `:nth-child(5n + 1)` minus the case where it is
 * second to last. Checked against every count from one to ten; all of them come
 * out with complete rows and no gaps.
 *
 * :is() takes the specificity of its heaviest branch, so every selector in the
 * group weighs the same and file order is the only thing deciding them. The
 * group is repeated twice more below; CSS has no way to name it once. */
.vallem-cases .vallem-cases__list > li:is(
	:nth-child(5n + 1):not(:nth-last-child(2)),
	:last-child:nth-child(5n + 4)
) {
	grid-column: span 5;
	min-height: clamp(380px, 44vw, 640px);
}

/* rem 2. Steps the would-be featured card down so the last two share a row
   rather than stacking as a full-width card and a lonely third.
   Narrow then wide, so the tail row leans the opposite way to the one above it
   and the page keeps alternating rather than repeating itself at the end. */
.vallem-cases .vallem-cases__list > li:nth-child(5n + 1):nth-last-child(2) {
	grid-column: span 2;
}

.vallem-cases .vallem-cases__list > li:nth-child(5n + 2):last-child {
	grid-column: span 3;
}

/* ---- The card ----------------------------------------------------------- */

/* A grid, so the body stretches to the card on its own.
 *
 * It used to be the body asking for `height: 100%`, which is 28px of padding
 * short at both ends: the box is content-box, so 100% plus the padding makes
 * the body 56px taller than the card, and `overflow: hidden` quietly ate the
 * bottom half of that. Measured 28px of air to the left of the button and 5px
 * underneath it. Letting the grid do the stretching means the padding is the
 * padding on all four sides.
 *
 * The photograph and the scrim are both absolutely positioned, so the body is
 * the only child in flow and there is nothing to place. */
.vallem-cases .vallem-cases__card {
	position: relative;
	display: grid;
	overflow: hidden;
	border-radius: 6px;
	isolation: isolate;
}

/* core/post-featured-image renders a <figure> with the image inside, so the
   figure is what has to fill the card. Height on the img as well as the figure:
   without it the intrinsic height wins and a landscape photo letterboxes. */
.vallem-cases .vallem-cases__photo {
	position: absolute;
	inset: 0;
	margin: 0;
	z-index: 0;
}

.vallem-cases .vallem-cases__photo img {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
	transform-origin: center;
}

/* Navy rather than black, and heaviest at both ends: the logo sits at the top
   and the button at the bottom, and the middle of the photograph is the part
   worth being able to see. */
.vallem-cases .vallem-cases__card::before {
	content: "";
	position: absolute;
	inset: 0;
	z-index: 1;
	background:
		linear-gradient(
			180deg,
			rgb(7 26 58 / 0.74) 0%,
			rgb(7 26 58 / 0.38) 42%,
			rgb(7 26 58 / 0.82) 100%
		);
}

.vallem-cases .vallem-cases__body {
	position: relative;
	z-index: 2;
	display: flex;
	flex-direction: column;
	gap: var(--wp--preset--spacing--30);
	box-sizing: border-box;
	padding: clamp(1.25rem, 2.2vw, 1.75rem);
	color: #fff;
}

/* Pushes the button and the attribution to the floor of the card. */
.vallem-cases .vallem-cases__foot {
	margin-block-start: auto;
}

/* ---- The logo ----------------------------------------------------------- *
 *
 * Knocked out white, same construction as the dark logo row, so one uploaded
 * asset serves both. Not quite opaque, because pure white on this navy is
 * harsher than the type next to it.
 *
 * The block is bound to the vallem_klant_logo field, and an unfilled field is
 * an empty string rather than null, which core writes into the markup as
 * src="". functions.php drops the block instead, so an anonymous story shows no
 * logo rather than a broken one. */
.vallem-cases .vallem-cases__logo {
	margin: 0;
}

.vallem-cases .vallem-cases__logo img {
	display: block;
	width: auto;
	max-width: 150px;
	height: clamp(18px, 1.7vw, 24px);
	object-fit: contain;
	object-position: left center;
	opacity: 0.88;
	filter: grayscale(1) brightness(0) invert(1);
}

/* ---- The headline ------------------------------------------------------- *
 *
 * The title is the story, not the customer name: "AVEX bracht wachtwoordbeheer
 * en IT-beheer samen", not "AVEX". Its link is stretched over the whole card so
 * the card is one target with a descriptive name, which is why the button below
 * it is a paragraph and not a second link to the same place.
 * ------------------------------------------------------------------------- */

.vallem-cases .vallem-cases__title {
	max-width: 22ch;
	margin: 0;
	color: #fff;
	text-wrap: balance;
}

.vallem-cases .vallem-cases__list > li:is(
	:nth-child(5n + 1):not(:nth-last-child(2)),
	:last-child:nth-child(5n + 4)
) .vallem-cases__title {
	max-width: 26ch;
	font-size: var(--wp--preset--font-size--display-small);
}

.vallem-cases .vallem-cases__title a {
	color: inherit;
	text-decoration: none;
}

.vallem-cases .vallem-cases__title a::after {
	content: "";
	position: absolute;
	inset: 0;
	z-index: 3;
}

.vallem-cases .vallem-cases__card:focus-within {
	outline: 2px solid var(--wp--preset--color--accent);
	outline-offset: 3px;
}

.vallem-cases .vallem-cases__title a:focus-visible {
	outline: none;
}

/* ---- The button --------------------------------------------------------- *
 *
 * A paragraph, deliberately. The whole card is already the link.
 *
 * The chip is the reviews rail's arrows in a different colour: a square, a 4px
 * corner and a glyph in the middle. The glyph is a data URI because rich text
 * strips an inline SVG out of a paragraph the moment the block is parsed, the
 * same trap the comparison section's ticks are built around.
 * ------------------------------------------------------------------------- */

.vallem-cases__cta {
	/* The services bento's expand mark, split into its two halves so each can
	   move on its own. One <p> has two pseudo-elements and both are spoken for,
	   so the pair rides as two background layers on ::before instead, and the
	   splay is two background-positions rather than two transforms. Same
	   geometry, same 20x20 box, same 1px each way. */
	--vallem-cases-glyph-a: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'><path fill='%2318181B' d='M13.75 6.75L10.25 6.75L10.25 5L15.5 5L15.5 10.25L13.75 10.25L13.75 6.75Z'/></svg>");
	--vallem-cases-glyph-b: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'><path fill='%2318181B' d='M6.75 10.25L5 10.25L5 15.5L10.25 15.5L10.25 13.75L6.75 13.75L6.75 10.25Z'/></svg>");
	--vallem-cases-glyph-ease: cubic-bezier(0.16, 1, 0.3, 1);
}

/* One solid element, no chip inside a plate. The bento draws this as a 38px
   square sitting on `surface` with the mark in brand blue, filling on hover;
   this is that square inverted to white, stretched sideways to carry a label,
   with the mark in `contrast`.
   
   It is a paragraph and not a button block, because the card's real link is the
   headline and it is stretched over the whole card. A second link here would
   point at the same page under a label that does not name it. */
.vallem-cases__cta {
	display: inline-flex;
	align-items: center;
	gap: 0.7em;
	margin: 0;
	padding: 0.62em 1.15em 0.62em 0.8em;
	border-radius: 4px;
	background-color: var(--wp--preset--color--base);
	color: var(--wp--preset--color--contrast);
	white-space: nowrap;
}

.vallem-cases__cta::before {
	content: "";
	flex: 0 0 auto;
	width: 20px;
	height: 20px;
	background-image: var(--vallem-cases-glyph-a), var(--vallem-cases-glyph-b);
	background-repeat: no-repeat, no-repeat;
	background-size: 20px 20px, 20px 20px;
	background-position: 0 0, 0 0;
}

/* The label rolls character by character, the same motion every button on this
   site uses. assets/js/text-roll.js splits it because the pill carries the
   `vallem-roll-target` class; without the script it is an ordinary label.
   
   The trigger is the card and not the pill, because the pill is not the thing
   being pointed at. */
.vallem-cases__cta {
	--vallem-roll: 1.35em;
}

@supports (height: 1lh) {
	.vallem-cases__cta {
		--vallem-roll: 1lh;
	}
}

/* ---- Who said it -------------------------------------------------------- *
 *
 * Only on the wide cards. At 507px the button and a two-line attribution have
 * to share one row and neither wins, and the reference makes the same call.
 * ------------------------------------------------------------------------- */

.vallem-cases .vallem-cases__who {
	display: none;
	text-align: right;
	line-height: 1.35;
	color: rgb(255 255 255 / 0.72);
}

.vallem-cases .vallem-cases__list > li:is(
	:nth-child(5n + 1):not(:nth-last-child(2)),
	:last-child:nth-child(5n + 4)
) .vallem-cases__who {
	display: block;
}

.vallem-cases .vallem-cases__who p {
	margin: 0;
}

.vallem-cases .vallem-cases__who-name {
	color: #fff;
}

/* ---- Motion ------------------------------------------------------------- *
 *
 * The photograph moves, not the card. The services bento grows its cards with a
 * clip-path because a size change would reflow the row; here the card is a grid
 * item with a fixed span, so there is nothing to reflow and a transform on the
 * image inside an overflow-hidden box is the whole effect. The duration and the
 * easing are the bento's, measured off Stripe.
 * ------------------------------------------------------------------------- */

@media (hover: hover) and (pointer: fine) {
	.vallem-cases .vallem-cases__photo img {
		transition: transform 800ms cubic-bezier(0.165, 0.84, 0.44, 1);
	}

	/* The bento's own timing for the glyph, which is a faster curve than the
	   card's: the mark answers, the photograph drifts. */
	.vallem-cases__cta::before {
		transition: background-position 600ms var(--vallem-cases-glyph-ease);
	}

	.vallem-cases .vallem-cases__card:hover .vallem-cases__photo img,
	.vallem-cases .vallem-cases__card:focus-within .vallem-cases__photo img {
		transform: scale(1.045);
	}

	/* The two halves pull apart along the diagonal, one pixel each. */
	.vallem-cases .vallem-cases__card:hover .vallem-cases__cta::before,
	.vallem-cases .vallem-cases__card:focus-within .vallem-cases__cta::before {
		background-position: 1px -1px, -1px 1px;
	}

	.vallem-cases .vallem-cases__card:hover .vallem-cases__cta .vallem-roll__r,
	.vallem-cases .vallem-cases__card:focus-within .vallem-cases__cta .vallem-roll__r {
		transform: translateY(calc(-1 * var(--vallem-roll))) rotate(0.001deg);
	}
}

@media (prefers-reduced-motion: reduce) {
	.vallem-cases .vallem-cases__photo img,
	.vallem-cases__cta::before {
		transition: none;
	}

	.vallem-cases .vallem-cases__card:hover .vallem-cases__photo img,
	.vallem-cases .vallem-cases__card:focus-within .vallem-cases__photo img {
		transform: none;
	}

	.vallem-cases .vallem-cases__card:hover .vallem-cases__cta .vallem-roll__r,
	.vallem-cases .vallem-cases__card:focus-within .vallem-cases__cta .vallem-roll__r {
		transform: none;
	}
}

/* ---- One column --------------------------------------------------------- *
 *
 * 781px is this stylesheet's breakpoint everywhere else, and the reference
 * collapses somewhere between 520 and 900, so there is nothing to gain from a
 * third number. The wide card keeps a taller box, because it is the only thing
 * left telling you it is the featured one once every card is the same width.
 * ------------------------------------------------------------------------- */

@media (width <= 781px) {
	.vallem-cases .vallem-cases__list {
		grid-template-columns: minmax(0, 1fr);
	}

	.vallem-cases .vallem-cases__list > li,
	.vallem-cases .vallem-cases__list > li:nth-child(5n + 3),
	.vallem-cases .vallem-cases__list > li:nth-child(5n + 4) {
		grid-column: 1 / -1;
		min-height: clamp(300px, 62vw, 380px);
	}

	.vallem-cases .vallem-cases__list > li:is(
		:nth-child(5n + 1):not(:nth-last-child(2)),
		:last-child:nth-child(5n + 4)
	) {
		grid-column: 1 / -1;
		min-height: clamp(360px, 80vw, 460px);
	}

	.vallem-cases .vallem-cases__foot {
		flex-direction: column;
		align-items: flex-start;
		gap: var(--wp--preset--spacing--20);
	}

	.vallem-cases .vallem-cases__who {
		text-align: left;
	}
}

/* ---- The logo marquee --------------------------------------------------- *
 *
 * The shared band, see `.vallem-marquee` further down this file. Only its
 * placement is the archive's business; the row, the loop and the knockout are
 * the component's.
 *
 * On the hero's own navy so there is no seam. The homepage logo row cannot be
 * reused here: its dark block style paints `contrast` (#18181B) and this band
 * is `primary-darkest` (#071A3A), and two darks that close together read as a
 * mistake rather than as a change.
 * ------------------------------------------------------------------------- */

.vallem-cases-hero .vallem-cases-hero__logos {
	margin-block-start: var(--wp--preset--spacing--60);
}

/* ---- Pagination --------------------------------------------------------- */

.vallem-cases .vallem-cases__pagination {
	margin-block-start: var(--wp--preset--spacing--50);
}

/* ------------------------------------------------------------------------- *
 * Klantverhaal, losse pagina. See templates/single-vallem_use_case.html
 *
 * The shape is uniqkey.eu/avex-netherlands: a company overview standing beside
 * the story rather than above it, so the facts stay in view while the narrative
 * runs. Their hero is a full 100vh and carries a logo carousel; ours is roughly
 * half that and carries neither, which was the one instruction attached to
 * copying the page.
 *
 * The grounds alternate the way theirs do, navy for the overview and the story,
 * white for the quote, navy again for the related stories. One switch rather
 * than their three, because our story lives in a single post-content block and
 * cannot be cut in half from a template.
 *
 * Everything the panel draws comes from the fields, and every row of it can be
 * empty: vallem_hide_empty_facts() in functions.php takes the whole row away
 * when its field is blank, so a story with no doorlooptijd has no doorlooptijd
 * row rather than a labelled gap.
 * ------------------------------------------------------------------------- */

/* ---- The hero ----------------------------------------------------------- *
 *
 * Half the reference's height. Theirs is 100vh, which on a laptop means the
 * headline is the entire first screen and the story starts below the fold. The
 * clamp tops out at 620 so it never eats a large display either.
 * ------------------------------------------------------------------------- */

/* The sections stack edge to edge. Without this the constrained layout on
   <main> puts a block gap between them and the page background shows through as
   a white seam between the hero and the navy under it. Same fix as
   `.wp-site-blocks > main` at the top of this file, one level down. */
.vallem-case-hero,
.vallem-case-body,
.vallem-case-story,
.vallem-case-more,
.vallem-cases-hero,
.vallem-cases,
.vallem-blog-hero,
.vallem-blog,
.vallem-post-hero,
.vallem-post-body,
.vallem-post-more {
	margin-block: 0 !important;
}

.vallem-case-hero {
	position: relative;
	display: flex;
	align-items: flex-end;
	min-height: clamp(380px, 52vh, 620px);
	padding-block: var(--wp--preset--spacing--60);
	overflow: hidden;
	isolation: isolate;
}

/* max-width matters as much as the inset here. The figure is a direct child of
   a constrained group, so core hands it the content measure and centres it, and
   `inset: 0` does not undo a max-width: the photograph came out as a 770px band
   down the middle of a full-bleed hero. */
.vallem-case-hero .vallem-case-hero__photo {
	position: absolute;
	inset: 0;
	max-width: none;
	margin: 0;
	z-index: 0;
}

.vallem-case-hero .vallem-case-hero__photo img {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
}

/* The same navy veil the archive cards use, weighted to the bottom because that
   is where the headline sits. One scrim, two places. */
.vallem-case-hero::before {
	content: "";
	position: absolute;
	inset: 0;
	z-index: 1;
	background:
		linear-gradient(
			180deg,
			rgb(7 26 58 / 0.62) 0%,
			rgb(7 26 58 / 0.40) 38%,
			rgb(7 26 58 / 0.88) 100%
		);
}

.vallem-case-hero .vallem-case-hero__copy {
	position: relative;
	z-index: 2;
	width: 100%;
}

/* A chip rather than plain type, so the label reads as a category and not as a
   first line of the headline. Built from currentcolor, the same construction
   the reviews rail's arrows use, so it needs no colour of its own. */
.vallem-case-hero .vallem-case-hero__eyebrow {
	display: inline-block;
	margin: 0;
	padding: 0.3em 0.7em;
	border-radius: 4px;
	background-color: color-mix(in srgb, currentcolor 14%, transparent);
	text-transform: none;
}

/* margin-block-start is deliberately not reset here.
   Core emits `> * + * { margin-block-start: <blockGap> }` for the copy group at
   one class of specificity, and this selector carries two, so a `margin: 0`
   shorthand silently ate the gap: the eyebrow and the h1 measured 0px apart
   whatever the template asked for, and changing the blockGap in the editor did
   nothing at all. Resetting only the sides and the bottom leaves the gap to the
   layout, which is the control an editor can actually reach. */
.vallem-case-hero .vallem-case-hero__title {
	margin-inline: 0;
	margin-block-end: 0;
	max-width: 20ch;
	text-wrap: balance;
}

/* ---- Overview beside story ---------------------------------------------- */

.vallem-case-body {
	padding-block: var(--wp--preset--spacing--80);
}

.vallem-case-body .vallem-case-body__cols {
	gap: clamp(2rem, 6vw, 5rem);
}

/* Sticky, which is the whole idea: the facts are meant to still be there when
   the reader is four paragraphs into the story. `align-self: start` is what
   makes it possible at all, because a flex item stretches to the row by default
   and a full-height box has nothing to stick to. */
.vallem-case-body .vallem-case-profile {
	position: sticky;
	top: var(--wp--preset--spacing--60);
	align-self: start;
	display: flex;
	flex-direction: column;
	gap: var(--wp--preset--spacing--30);
}

.vallem-case-body .vallem-case-profile__logo {
	margin: 0 0 var(--wp--preset--spacing--10);
}

.vallem-case-body .vallem-case-profile__logo img {
	display: block;
	width: auto;
	max-width: 160px;
	height: 30px;
	object-fit: contain;
	object-position: left center;
	filter: grayscale(1) brightness(0) invert(1);
}

.vallem-case-body .vallem-fact p {
	margin: 0;
}

.vallem-case-body .vallem-fact__label {
	color: rgb(255 255 255 / 0.52);
}

.vallem-case-body .vallem-fact__value {
	color: #fff;
	line-height: 1.5;
}

/* Terms as pills, and deliberately without a label. A labelled row that can be
   empty needs the wrapper trick the facts use; a row of pills that can be empty
   simply draws nothing, because core/post-terms renders nothing without terms
   and the flex row collapses. Fewer moving parts for the same result. */
.vallem-case-body .vallem-case-profile__terms:empty {
	display: none;
}

.vallem-case-body .vallem-case-profile__pills {
	display: contents;
}

.vallem-case-body .vallem-case-profile__pills a {
	display: inline-block;
	margin-inline-end: 6px;
	margin-block-end: 6px;
	padding: 0.28em 0.7em;
	border: 1px solid color-mix(in srgb, currentcolor 22%, transparent);
	border-radius: 4px;
	color: rgb(255 255 255 / 0.78);
	font-size: var(--wp--preset--font-size--small);
	text-decoration: none;
}

.vallem-case-body .vallem-case-profile__pills a:hover {
	border-color: color-mix(in srgb, currentcolor 48%, transparent);
	color: #fff;
}

/* The one number, given its own weight at the foot of the panel. */
.vallem-case-body .vallem-case-profile__kpi {
	margin-block-start: var(--wp--preset--spacing--20);
	padding-block-start: var(--wp--preset--spacing--30);
	border-top: 1px solid rgb(255 255 255 / 0.14);
}

/* ---- The facts, tightened ------------------------------------------------ *
 *
 * The panel was a flex column at spacing--30, 24px, with the label sitting
 * straight on top of its value. So the distance from a label to the value it
 * describes and the distance from one fact to the next were 0 and 24, and six
 * rows of that read as six separate things rather than as one block of facts.
 *
 * The two numbers now are 2 and 16, and the ratio is what does the work: eight
 * to one is unambiguous, at 14px type, about which line belongs to which. 16px
 * is spacing--20 exactly, so the row rhythm is a preset and not a magic number.
 * 2px is not: the smallest step in theme.json is 8px, which at a 21px line box
 * would open the pair back up again. It is written as a raw value rather than
 * pretending a preset fits.
 *
 * Tightening a flex gap tightens every child of the column, including the logo,
 * the pills and the kerncijfer, none of which were asked to move. The two
 * compensating margins hand those their old separation back.
 * ------------------------------------------------------------------------- */

/* SUPERSEDES: gap: var(--wp--preset--spacing--30) */
.vallem-case-body .vallem-case-profile {
	gap: var(--wp--preset--spacing--20);
}

/* The column is a flow layout, so core prints `> * + * { margin-block-start }`
   from the root block gap on top of the flex gap this panel sets for itself.
   Both applied: the gap said 16 and the rows measured 40, which is 16 and 24
   stacked. Zeroed, so the panel has one source of rhythm and the two
   compensating margins below are the only exceptions to it. */
.vallem-case-body .vallem-case-profile > * + * {
	margin-block-start: 0;
}

/* SUPERSEDES: margin: 0 0 var(--wp--preset--spacing--10)
   8px of its own plus the old 24px gap put 32px under the logo. The gap is now
   16, so the margin takes the other 16 and the logo keeps its air. */
.vallem-case-body .vallem-case-profile__logo {
	margin: 0 0 var(--wp--preset--spacing--20);
}

/* The label was on the body's 1.6, which at 14px is a 22px line box around a
   single short word: 4px of leading above and below a label that is never more
   than one line. That leading is what a reader sees, not the margin, so it works
   against the pairing the two numbers above are trying to create. 1.35 gives the
   label the same treatment the value already has, and takes 21px out of the six
   rows on the way. */
.vallem-case-body .vallem-fact__label {
	line-height: 1.35;
}

/* The class is spelled out on the tag on purpose. `.vallem-case-body
   .vallem-fact p { margin: 0 }` sits higher up the file at (0,2,1), and a
   longhand only beats a shorthand of equal weight by coming later. (0,3,1)
   settles it either way. Only the labelled rows, so the kerncijfer keeps the
   0.4em its own rule gives it. */
.vallem-case-body .vallem-fact p.vallem-fact__value {
	margin-block-start: 2px;
}

/* The pills are a different kind of thing from a labelled row, so once the rows
   are tight they need more air than the rows give each other, not less. 16 of
   gap plus 16 of margin, the same 32 the logo gets. */
.vallem-case-body .vallem-case-profile__terms {
	margin-block-start: var(--wp--preset--spacing--20);
}

.vallem-case-body .vallem-case-profile__kpi-label {
	margin-block-start: 0.4em;
	color: rgb(255 255 255 / 0.6);
	line-height: 1.45;
}

/* ---- Prestaties --------------------------------------------------------- *
 *
 * Three fixed slots under the facts, above the kerncijfer. The shape is
 * uniqkey.eu/avex-netherlands: a coloured eyebrow, then three items, each a
 * line icon over one line of text whose first few words are white and bold and
 * whose remainder is muted ("Stronger Password Creation. Enforces best
 * practices for password strength.").
 *
 * The title and the toelichting are two bound paragraphs and one sentence,
 * which is the only reason the paragraphs go inline. Meta is sanitised text, so
 * a <strong> typed into a single field is stripped before it reaches the page;
 * two fields with two colours is the only way to get the effect and keep both
 * halves editable.
 *
 * Same hairline-and-margin construction as the kerncijfer under it, so the
 * panel reads as three stacked groups rather than one long list.
 * ------------------------------------------------------------------------- */

.vallem-case-body .vallem-prestaties {
	/* One accent colour in this panel, not two. terminal is already the
	   kerncijfer under it and the crest of the wave beside it; a second blue
	   for the eyebrow and the icons would be a third thing to look at in a
	   column that exists to be scanned. 9.5:1 on primary-darkest, so it is legible at 14px rather than
	   decorative. */
	--vallem-prestatie-icoon-kleur: var(--wp--preset--color--terminal);
	--vallem-prestatie-icoon-maat: 20px;

	margin-block-start: var(--wp--preset--spacing--20);
	padding-block-start: var(--wp--preset--spacing--30);
	border-top: 1px solid rgb(255 255 255 / 0.14);
}

/* The group carries blockGap 0 from the template, so core's layout rule is
   `> * { margin-block-start: 0 }` at (0,1,0) and this (0,2,0) rule owns the
   rhythm. 24px between items, because each one is an icon plus up to three
   lines and 16 would run them together. */
.vallem-case-body .vallem-prestaties > * + * {
	margin-block-start: var(--wp--preset--spacing--30);
}

/* The eyebrow belongs to the list under it rather than sitting between the
   list and the border, so it sits closer to the first item than the items sit
   to each other. Still matches when slot 1 is empty and the filter has taken
   it away. */
.vallem-case-body .vallem-prestaties__eyebrow + .vallem-prestatie {
	margin-block-start: var(--wp--preset--spacing--20);
}

/* Colour comes from textColor on the block, not from here, so an editor can
   change it. This is only the tracking, matching .vallem-case-block__eyebrow
   in the story column. */
.vallem-case-body .vallem-prestaties__eyebrow {
	margin: 0;
	letter-spacing: 0.01em;
}

.vallem-case-body .vallem-prestatie {
	line-height: 1.5;
	text-wrap: pretty;
}

/* The whole point of the row. Two paragraphs, one sentence. */
.vallem-case-body .vallem-prestatie p {
	display: inline;
}

/* The 600 weight is on the block, the way the hero eyebrow carries its own, so
   an editor can still change it. All this rule adds is the two things the block
   cannot express.

   Two jobs in one declaration. It is the full stop the field's help text
   promises ("zonder punt, die zet het ontwerp erbij"), the same arrangement
   .vallem-case-quote__text uses for its quotation marks. And it is the word
   space between the two paragraphs, which would otherwise be the newline
   between them in the template file and would vanish the day somebody
   reformats it. A plain space rather than a nbsp, so a long title can still
   wrap before the toelichting starts. */
.vallem-case-body .vallem-prestatie__titel::after {
	content: ". ";
}

/* The slug reaches the CSS as data-icoon, put there by
   vallem_case_achievement_icon() in functions.php. A row whose icon field is
   empty, or holds something not in the list, has no attribute and draws no
   icon, which is why the ::before hangs off the attribute rather than off the
   class: no attribute, no box, no 20px hole where a glyph should be.

   currentcolor is deliberately not used. The row's own colour is the base
   white it inherits from the section, and a white glyph would read as a fourth
   white thing in a stack of white things. The custom property keeps it in one
   place, so recolouring the set is one line. */
.vallem-case-body .vallem-prestatie[data-icoon]::before {
	content: "";
	display: block;
	width: var(--vallem-prestatie-icoon-maat);
	height: var(--vallem-prestatie-icoon-maat);
	margin-block-end: 6px;
	background-color: var(--vallem-prestatie-icoon-kleur);
	mask-image: var(--vallem-prestatie-glyph);
	mask-repeat: no-repeat;
	mask-position: center;
	mask-size: contain;
}

/* Nine glyphs, one line each, same construction as --vallem-tick-glyph and
   --vallem-cases-glyph-a: a data-URI with the angle brackets and quotes left
   raw and only the hex encoded. All nine are viewBox 0 0 24 24, fill none,
   stroke-width 1.5, round caps and joins. The stroke colour is irrelevant,
   mask-image reads the alpha channel and not the luminance; it is there
   because a stroked path with no stroke paints nothing.

   Rendered at 72px and at 20px against primary-darkest before they shipped.
   See profile.icons.md for what each one is for. */
.vallem-case-body .vallem-prestatie[data-icoon="schild"] {
	--vallem-prestatie-glyph: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'><path d='M12 3 5 6v5.5c0 4.3 2.9 7.7 7 8.9 4.1-1.2 7-4.6 7-8.9V6l-7-3Z'/></svg>");
}

.vallem-case-body .vallem-prestatie[data-icoon="slot"] {
	--vallem-prestatie-glyph: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'><path d='M8 10V7.5a4 4 0 0 1 8 0V10M7 10h10a1.5 1.5 0 0 1 1.5 1.5v6.5a1.5 1.5 0 0 1-1.5 1.5H7a1.5 1.5 0 0 1-1.5-1.5V11.5A1.5 1.5 0 0 1 7 10Z'/></svg>");
}

.vallem-case-body .vallem-prestatie[data-icoon="klok"] {
	--vallem-prestatie-glyph: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'><path d='M12 3a9 9 0 1 0 0 18 9 9 0 0 0 0-18ZM12 7.5V12h4'/></svg>");
}

.vallem-case-body .vallem-prestatie[data-icoon="grafiek"] {
	--vallem-prestatie-glyph: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'><path d='M4 20h16M7 20v-4.5M12 20v-8M17 20v-12'/></svg>");
}

.vallem-case-body .vallem-prestatie[data-icoon="mensen"] {
	--vallem-prestatie-glyph: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'><path d='M9 11.5a3 3 0 1 0 0-6 3 3 0 0 0 0 6ZM3.5 20a5.5 5.5 0 0 1 11 0M17 11a2.5 2.5 0 1 0 0-5 2.5 2.5 0 0 0 0 5ZM16.6 15.3a4.9 4.9 0 0 1 3.9 4.7'/></svg>");
}

.vallem-case-body .vallem-prestatie[data-icoon="vinkje"] {
	--vallem-prestatie-glyph: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'><path d='M12 3a9 9 0 1 0 0 18 9 9 0 0 0 0-18ZM8 12l2.8 2.8L16 9.5'/></svg>");
}

.vallem-case-body .vallem-prestatie[data-icoon="oog"] {
	--vallem-prestatie-glyph: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'><path d='M2.5 12C4.5 8 8 5.5 12 5.5S19.5 8 21.5 12c-2 4-5.5 6.5-9.5 6.5S4.5 16 2.5 12ZM12 8.5a3.5 3.5 0 1 0 0 7 3.5 3.5 0 0 0 0-7Z'/></svg>");
}

.vallem-case-body .vallem-prestatie[data-icoon="server"] {
	--vallem-prestatie-glyph: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'><path d='M5.5 4.5h13a1.5 1.5 0 0 1 1.5 1.5v3a1.5 1.5 0 0 1-1.5 1.5h-13A1.5 1.5 0 0 1 4 9V6a1.5 1.5 0 0 1 1.5-1.5ZM5.5 13.5h13a1.5 1.5 0 0 1 1.5 1.5v3a1.5 1.5 0 0 1-1.5 1.5h-13A1.5 1.5 0 0 1 4 18v-3a1.5 1.5 0 0 1 1.5-1.5ZM7.5 7.5h.01M7.5 16.5h.01'/></svg>");
}

.vallem-case-body .vallem-prestatie[data-icoon="document"] {
	--vallem-prestatie-glyph: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'><path d='M14 3H7a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V8l-5-5ZM14 3v5h5M9 13h6M9 17h4'/></svg>");
}

/* ---- The lead ------------------------------------------------------------ *
 *
 * The right hand column of the dark block: the story summary that lights up as
 * the reader scrolls, an optional paragraph under it, and the testimonial. None
 * of it is post content. Everything from the hero down to the quote is filling,
 * so a writer fills fields and the block editor stays empty until the
 * onderwerpen start below.
 *
 * A flex column with its own gap rather than the block gap the column layout
 * prints, because the three things in it are three different weights and want
 * more air between them than a stack of paragraphs does.
 * ------------------------------------------------------------------------- */

.vallem-case-body .vallem-case-lead {
	display: flex;
	flex-direction: column;
	gap: var(--wp--preset--spacing--60);
}

/* The wave.
 *
 * Osmo's gradient wave splits this into characters and drags a crest along it
 * at the speed the reader scrolls, so the length of the field decides how far
 * they scroll before the sentence has finished arriving. The colour here is the
 * finished state: the dim one is written inline by gradient-wave.js, and only
 * after it has established it can write this one back. So a reader with no JS
 * gets the same readable paragraph.
 *
 * text-wrap: pretty and not balance. Balance evens out the lines of a heading,
 * which is right for two or three of them and wrong for a paragraph, where it
 * fights the ragged edge the whole way down. */
.vallem-case-body .vallem-case-wave {
	/* The crest. gradient-wave.js reads this custom property, falls back to no
	   crest at all when nothing names one, and that fallback is what shipped:
	   characters went dim straight to white and the colour Osmo's effect is
	   actually about never appeared. Terminal is the accent this navy already
	   carries, on the kerncijfer beside it and on the archive's headline, and at
	   9.5:1 it is the one palette colour that can be read on this ground. */
	--gradient-wave-color-wave: var(--wp--preset--color--terminal);

	margin: 0;
	text-wrap: pretty;
}

.vallem-case-body .vallem-case-lead__extra {
	margin: 0;
	max-width: 58ch;
	line-height: 1.6;
}

/* ---- The testimonial ----------------------------------------------------- *
 *
 * The same object as `social-proof-band`, which is the site's testimonial and
 * is categorised as one. That section carries the design in block attributes
 * rather than in CSS, so most of the matching is in the template: the quote at
 * display-small on a 1.25 leading, then a wrapping flex row at spacing--20 with
 * a 56px rounded portrait, the name at medium and 600, and the role at small
 * and muted with no gap between the two.
 *
 * Three things there do not survive the move and are worth writing down.
 *
 * The three-tone quote does not: it is built from inline `<mark>` colour spans,
 * and this quote comes out of `vallem_citaat`, which is sanitised with
 * sanitize_textarea_field(). Any tag typed into that field is stripped before
 * it reaches the page, so the tones would need three fields rather than one.
 *
 * Violet does not: it is the third tone there, on the canvas, and it is 1.4:1
 * on this navy. base-muted is the dark-ground stand-in for contrast-muted and
 * carries the role line.
 *
 * The customer logo does not: the profile panel two columns to the left is
 * already showing it, and the hairline in front of it in the proof band exists
 * to separate it from the reviewer, which is a problem this layout does not
 * have.
 *
 * No rule down the side and no quotation marks, because the testimonial has
 * neither. The portrait and the name are what say who is speaking.
 * ------------------------------------------------------------------------- */

.vallem-case-body .vallem-case-quote__text {
	margin: 0;
	text-wrap: pretty;
}

.vallem-case-body .vallem-case-quote__portrait {
	margin: 0;
}

.vallem-case-body .vallem-case-quote__source > * {
	margin: 0;
}

.vallem-case-body .vallem-case-quote__who p {
	margin: 0;
}

/* ---- The klantverhaal ---------------------------------------------------- *
 *
 * Where the post content lands, and the only part of this page that is page
 * content. The ground flips to white here and that break is the point: above it
 * is the filling, below it is what somebody actually wrote. The anchor dock
 * floats over this section and nowhere else.
 *
 * Larger than body copy and on a short measure. The reference runs its
 * narrative at roughly 28px, which is what makes it read as a story rather than
 * as documentation, and it is the one thing about that page doing the most
 * work.
 *
 * Links take primary and not terminal. Terminal is a dark ground colour, 9.5:1
 * on the navy and nowhere near that on white; primary is the other way round.
 * ------------------------------------------------------------------------- */

.vallem-case-story {
	padding-block: var(--wp--preset--spacing--80);
	font-size: clamp(1.125rem, 1.5vw, 1.375rem);
	line-height: 1.55;
	letter-spacing: -0.014em;
}

/* A topic's own margin is what separates it from the topic before it, so on the
   first and last it is separating it from the section's padding, which is
   already doing that job. Measured at 214px of white above the first heading
   before this: 128 of padding and 64 of margin, plus the heading's own. */
.vallem-case-story .vallem-topic:first-child {
	margin-block-start: 0;
}

.vallem-case-story .vallem-topic:last-child {
	margin-block-end: 0;
}

.vallem-case-story p {
	max-width: 62ch;
}

.vallem-case-story a {
	color: var(--wp--preset--color--primary);
}

/* ---- More klantverhalen -------------------------------------------------- *
 *
 * A different card from the archive's on purpose. There the photograph is the
 * card and the type sits on it; here the type sits under it on the section's
 * own navy, which reads as a footnote to the story rather than as a second
 * archive stapled to the bottom of it. Same treatment the reference uses.
 * ------------------------------------------------------------------------- */

.vallem-case-more {
	padding-block: var(--wp--preset--spacing--80);
}

.vallem-case-more .vallem-case-more__query {
	margin-block-start: var(--wp--preset--spacing--60);
}

.vallem-case-more .vallem-case-more__list {
	display: grid;
	grid-template-columns: repeat(2, minmax(0, 1fr));
	gap: clamp(1rem, 2vw, 1.5rem);
	margin: 0;
	padding: 0;
	list-style: none;
}

.vallem-case-more .vallem-case-more__list > li {
	margin-block: 0;
}

.vallem-case-more .vallem-case-more__card {
	position: relative;
	display: flex;
	flex-direction: column;
	height: 100%;
	overflow: hidden;
	border: 1px solid rgb(255 255 255 / 0.12);
	border-radius: 6px;
	background-color: rgb(255 255 255 / 0.04);
}

.vallem-case-more .vallem-case-more__photo {
	margin: 0;
	aspect-ratio: 16 / 9;
}

.vallem-case-more .vallem-case-more__photo img {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
}

.vallem-case-more .vallem-case-more__body {
	display: flex;
	flex: 1;
	flex-direction: column;
	align-items: flex-start;
	box-sizing: border-box;
	padding: clamp(1.25rem, 2.2vw, 1.75rem);
}

.vallem-case-more .vallem-case-more__logo {
	margin: 0;
}

.vallem-case-more .vallem-case-more__logo img {
	display: block;
	width: auto;
	max-width: 130px;
	height: 20px;
	object-fit: contain;
	object-position: left center;
	opacity: 0.85;
	filter: grayscale(1) brightness(0) invert(1);
}

.vallem-case-more .vallem-case-more__title {
	margin: 0;
	color: #fff;
	text-wrap: balance;
}

.vallem-case-more .vallem-case-more__title a {
	color: inherit;
	text-decoration: none;
}

/* Same stretched link as the archive card, so the whole card is one target with
   a descriptive name and the pill under it stays a paragraph. */
.vallem-case-more .vallem-case-more__title a::after {
	content: "";
	position: absolute;
	inset: 0;
	z-index: 3;
}

.vallem-case-more .vallem-cases__cta {
	margin-block-start: auto;
	padding-block-start: 0;
}

.vallem-case-more .vallem-case-more__card:focus-within {
	outline: 2px solid var(--wp--preset--color--terminal);
	outline-offset: 3px;
}

@media (hover: hover) and (pointer: fine) {
	.vallem-case-more .vallem-case-more__photo img {
		transition: transform 800ms cubic-bezier(0.165, 0.84, 0.44, 1);
	}

	.vallem-case-more .vallem-case-more__card:hover .vallem-case-more__photo img {
		transform: scale(1.04);
	}

	.vallem-case-more .vallem-case-more__card:hover .vallem-cases__cta::before {
		background-position: 1px -1px, -1px 1px;
	}

	.vallem-case-more .vallem-case-more__card:hover .vallem-cases__cta .vallem-roll__r {
		transform: translateY(calc(-1 * var(--vallem-roll))) rotate(0.001deg);
	}
}

/* ---- One column --------------------------------------------------------- */

@media (width <= 781px) {
	.vallem-case-body .vallem-case-profile {
		position: static;
	}

	.vallem-case-more .vallem-case-more__list {
		grid-template-columns: minmax(0, 1fr);
	}
}

/* ---- The blocks the story is built from --------------------------------- *
 *
 * All five patterns live in the klantverhaal, which is on white, and every tint
 * in them is mixed out of currentcolor rather than naming a colour. That is the
 * same construction the reviews rail's arrows and the profile pills use, and it
 * is what lets one rule serve a section whatever colour the editor gives it.
 * These rules were written when the story sat on the navy and they needed no
 * change when it moved, which is the whole argument for the idiom.
 *
 * Sizes here are deliberately smaller than the story's own type. The narrative
 * runs large; a spec list or a timeline that ran at the same size would compete
 * with it instead of supporting it.
 * ------------------------------------------------------------------------- */

.vallem-case-story .vallem-case-block,
.vallem-case-story .vallem-case-spec,
.vallem-case-story .vallem-case-timeline,
.vallem-case-story .vallem-case-metrics,
.vallem-case-story .vallem-case-pull {
	margin-block: var(--wp--preset--spacing--50);
}

.vallem-case-block__eyebrow {
	margin: 0 0 0.5em;
	letter-spacing: 0.01em;
}

/* The spec list and the timeline are the same object with different labels: a
   narrow first column, a wide second, a hairline between rows. */
.vallem-case-spec__row,
.vallem-case-timeline__step {
	display: grid;
	grid-template-columns: minmax(7.5rem, 22%) 1fr;
	gap: 0 var(--wp--preset--spacing--20);
	padding-block: 0.75rem;
	border-top: 1px solid color-mix(in srgb, currentcolor 14%, transparent);
}

.vallem-case-spec__row p,
.vallem-case-timeline__step p,
.vallem-case-metrics__row p,
.vallem-case-metrics__head p {
	margin: 0;
	line-height: 1.5;
}

.vallem-case-spec__key,
.vallem-case-timeline__when {
	color: color-mix(in srgb, currentcolor 58%, transparent);
}

/* Three columns: what it measures, what it was, what it became. The middle one
   is struck through, which says "this is the old number" without a word. */
.vallem-case-metrics__head,
.vallem-case-metrics__row {
	display: grid;
	grid-template-columns: 1fr 6rem 6rem;
	gap: 0 var(--wp--preset--spacing--20);
	align-items: baseline;
	padding-block: 0.75rem;
	border-top: 1px solid color-mix(in srgb, currentcolor 14%, transparent);
}

.vallem-case-metrics__head {
	border-top: 0;
	color: color-mix(in srgb, currentcolor 52%, transparent);
}

.vallem-case-metrics__head p:first-child {
	grid-column: 2;
}

.vallem-case-metrics__was {
	color: color-mix(in srgb, currentcolor 52%, transparent);
	text-decoration: line-through;
	text-decoration-thickness: 1px;
}

/* A card, not a rule down the side.
 *
 * It was a 2px violet border with a hanging indent, on the argument that a box
 * inside a column inside a section is one container too many. Nol asked for the
 * box on 2026-09-06: "Give it a background so its in a card with little
 * padding." It is the better call on this ground. The story runs on white and
 * the quote is somebody else speaking in the middle of it, and a tinted card
 * says that without a quotation mark or a rule.
 *
 * The design is `social-proof-band`'s, which is the site's testimonial: quote,
 * then a wrapping row with a rounded portrait and the name over the role. Same
 * shape as the testimonial in the dark block above, two sizes down.
 *
 * Ground, padding and corner are block attributes in the pattern rather than
 * rules here, so all three stay editable in the sidebar. What is left is what
 * the block cannot express. */
.vallem-case-pull__text {
	margin: 0;
	text-wrap: pretty;
}

/* Core's flex layout already zeroes the margins on the row, and the flow layout
   inside `__who` zeroes the two paragraphs. Both are (0,1,0) rules keyed to a
   generated container class, and the equivalent pair under the testimonial
   upstairs is what keeps that row on 2px instead of 24. Spelled out here so the
   two rows cannot drift apart. */
.vallem-case-pull__source > *,
.vallem-case-pull__who p {
	margin: 0;
}

.vallem-case-pull__role {
	color: color-mix(in srgb, currentcolor 58%, transparent);
}

@media (width <= 600px) {
	.vallem-case-spec__row,
	.vallem-case-timeline__step {
		grid-template-columns: 1fr;
		gap: 0.15rem;
	}

	.vallem-case-metrics__head,
	.vallem-case-metrics__row {
		grid-template-columns: 1fr 4.5rem 4.5rem;
	}
}

/* ---- The topic ---------------------------------------------------------- *
 *
 * The unit the story repeats: one heading, some prose, sometimes a set of
 * pictures. The anchor dock builds itself from these at render time. It finds
 * every `.vallem-topic`, takes the text of the first heading inside it, and
 * injects the id it needs, which is why nothing here carries one.
 *
 * Everything is scoped to `.vallem-case-story` for the same two reasons the
 * story blocks above are: every hairline and tint comes from currentcolor and
 * so follows whatever colour the editor gives the section, and two classes beat
 * the one-class `> * + *` block gap the layout support prints, which is what a
 * section that owns its own rhythm has to win.
 * ------------------------------------------------------------------------- */

/* Larger than the spacing-50 the spec list, the timeline and the pull quote
   use, because those sit inside a topic and this sits between two of them.
   Where a jump from the dock has to stop is the dock's own rule, one class up
   and deliberately weaker than this one, so nothing here may set scroll-margin:
   two classes would beat it and quietly drop the admin bar out of the sum. */
.vallem-case-story .vallem-topic {
	margin-block: var(--wp--preset--spacing--60);
}

/* Balance only. Size, weight and letter-spacing come from theme.json's h2, so a
   heading the client types by hand in the story matches one that arrived with
   the pattern. Written against the tag rather than against `vallem-topic__title`
   because deleting the heading and typing a new one loses the class, and the
   dock finds the heading by tag for exactly the same reason. */
.vallem-case-story .vallem-topic > :is(h2, h3, h4) {
	text-wrap: balance;
}

/* ---- The picture set ---------------------------------------------------- *
 *
 * uniqkey.eu/avex-netherlands runs two stacked shots beside one taller one,
 * between two paragraphs. The shape is worth taking. The count is not: a story
 * with two screenshots should not have to ship a third empty frame. So the
 * media area is an ordinary group holding ordinary image blocks, and the rules
 * below count the figures and lay out accordingly. One is a band, two is a
 * pair, three is the collage, four is a grid.
 *
 * Which means no rule here may depend on a class on an image. An image the
 * client inserts next year arrives with nothing but `wp-block-image`.
 *
 * The set is wider than the prose on purpose. `.vallem-case-story p` is capped
 * at 62ch and this is not, so the pictures break the measure and the page gets
 * a change of rhythm out of it.
 * ------------------------------------------------------------------------- */

.vallem-case-story .vallem-topic__media {
	display: grid;
	grid-template-columns: repeat(2, minmax(0, 1fr));

	/* A definite row height, deliberately, rather than an aspect ratio on the
	   figures. The tall shot spans two rows, and a spanning item whose own
	   height comes from its content feeds that content's height back into the
	   rows it spans: one 3000px photograph and the collage is three screens
	   tall. Fixed rows cut the circle. Every cell is definite before anything
	   inside it is measured, so `height: 100%` on the picture resolves and the
	   crop is the same whatever gets uploaded. It tracks the viewport because
	   the story column does, and the cap lands where wideSize stops the page
	   growing. */
	grid-auto-rows: clamp(9rem, 17vw, 15rem);
	gap: var(--wp--preset--spacing--20);
	margin-block: var(--wp--preset--spacing--40);
}

/* The group's flow layout spaces its children with margins, which in a grid
   land on top of the grid gap. The pattern ships blockGap 0, and this is what
   holds when somebody sets the block spacing in the editor instead. */
.vallem-case-story .vallem-topic__media > * {
	margin-block: 0;
}

/* An unfilled slot leaves nothing behind on the front end: core's image render
   callback returns an empty string for an `<img>` with no src, so the figure is
   gone by the time the page is built and only this group's own margins are
   left, which read as a hole between two paragraphs. In the editor the block is
   still there and still shows its placeholder, which is the whole point of
   shipping the slot. So the group is only taken away where it is actually
   empty. */
.vallem-case-story .vallem-topic__media:not(:has(figure)) {
	display: none;
}

/* Two rows: the picture, then the caption if there is one. minmax(0, 1fr)
   rather than 1fr because a replaced element's automatic minimum size would
   otherwise stop the picture shrinking to make room for that caption. */
.vallem-case-story .vallem-topic__media > figure {
	display: grid;
	grid-template-rows: minmax(0, 1fr) auto;
	border-radius: 6px;

	/* Visible only while a slot is empty, which is what makes the collage read
	   as a collage in the inserter preview and in the editor before anyone has
	   uploaded anything. A picture covers it completely. */
	background-color: color-mix(in srgb, currentcolor 8%, transparent);
}

.vallem-case-story .vallem-topic__media img {
	display: block;
	width: 100%;
	height: 100%;
	min-height: 0;
	border-radius: 6px;
	object-fit: cover;
}

.vallem-case-story .vallem-topic__media figcaption {
	margin: 0.5rem 0 0;
	color: color-mix(in srgb, currentcolor 58%, transparent);
	font-size: var(--wp--preset--font-size--small);
	line-height: 1.45;
}

/* Counting selectors, and `of-type` rather than `child` on purpose: the editor
   renders its own block appender as a child of the group, so a count of
   children is one out in the editor and right on the front end. A figure is a
   figure in both. It also means a paragraph dropped into the media area does
   not change the shape of the collage. */

/* One picture. A band across both columns, two rows tall, which comes out at
   roughly 16:10 at every width. */
.vallem-case-story .vallem-topic__media > figure:first-of-type:nth-last-of-type(1) {
	grid-column: 1 / -1;
	grid-row: span 2;
}

/* Three. The reference shape: the first one is the tall one, so reordering the
   images in the list view is how you pick which. Auto placement does the rest,
   because the two that follow can only go in the column the tall one left. */
.vallem-case-story .vallem-topic__media > figure:first-of-type:nth-last-of-type(3) {
	grid-row: span 2;
}

/* One column below the breakpoint, where the story column is already the full
   page and a two-up cell would be a letterbox slot. The row spans go with it,
   because stacked, a two-row cell is a portrait the width of the screen. With
   one column the figure's width is definite, so an aspect ratio on the figure
   is safe here in a way it is not on the collage above. */
@media (width <= 781px) {
	.vallem-case-story .vallem-topic__media {
		grid-template-columns: minmax(0, 1fr);
		grid-auto-rows: auto;
	}

	.vallem-case-story .vallem-topic__media > figure {
		grid-row: auto;
		aspect-ratio: 16 / 10;
	}
}

/* ------------------------------------------------------------------------- *
 * Section anchor dock, klantverhaal
 *
 * Markup from vallem_dock_render() in functions.php, behaviour in
 * assets/js/section-dock.js. Two states live in this file and they are almost
 * two different components:
 *
 *   default        what the server sent. A plain list of jump links at the foot
 *                  of the document, in the flow, on the page's own ground.
 *                  Everything works, nothing floats.
 *   is-enhanced    the dock. Fixed to the bottom of the viewport, a pill closed
 *                  and a panel open, set by the script only once GSAP and
 *                  ScrollTrigger have actually registered.
 *
 * Same arrangement as `is-enhanced` on the reviews rail. It costs a prefix on
 * every rule below and it buys a component that degrades to something usable
 * rather than to a button that does nothing.
 *
 * The plate is the reviews rail's arrow construction at a larger size: a fill
 * and a hairline both mixed out of currentcolor, so one set of rules serves the
 * dock whatever colour it is put on. It floats over the klantverhaal, which is
 * white, so the plate names primary-darkest and base and everything else
 * follows from those two.
 *
 * No backdrop-filter. Two reasons, both of them from this build: a filter makes
 * an element the containing block for any fixed descendant, which is the trap
 * that cost the most time on the nav, and a blurring fixed element repaints its
 * backdrop on every scroll frame for an effect that is nearly invisible over a
 * ground this dark. The plate is 92% opaque instead.
 * ------------------------------------------------------------------------- */

:root {
	/* Where an anchor jump has to stop. The nav detaches into a floating bar
	   that sits 8px from the top and is 56px tall, so its bottom edge is at 64,
	   and spacing-30 is the breathing room under it. The admin offset is the
	   same variable the nav's own fixed positioning uses. */
	--vallem-dock-anchor: calc(
		var(--vallem-admin-offset, 0px) + 8px +
		var(--vallem-nav-height-compact, 56px) +
		var(--wp--preset--spacing--30)
	);

	/* Chevron up, masked rather than filled so it takes the pill's own colour.
	   Same construction as --vallem-arrow on the buttons. */
	--vallem-dock-caret: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M4 10l4-4 4 4' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}

/* The topic sections themselves. Set here rather than in the topic pattern's own
   block, because the number is the sticky header's and the dock is the only
   thing that jumps to them. */
.vallem-topic {
	scroll-margin-top: var(--vallem-dock-anchor);
}

/* A section is given tabindex="-1" by the script when it is jumped to, so focus
   lands on the heading a reader just asked for. That focus is programmatic and
   the ring would be noise. */
.vallem-topic:focus {
	outline: none;
}

/* ---- Default: a plain list at the foot of the document ------------------- */

.vallem-dock {
	box-sizing: border-box;
	max-width: var(--vallem-content-line);
	margin-inline: auto;
	padding-block: var(--wp--preset--spacing--40);
	padding-inline: var(--wp--preset--spacing--50);
	border-block-start: 1px solid var(--wp--preset--color--border);
	color: var(--wp--preset--color--contrast);
	font-size: var(--wp--preset--font-size--small);
}

/* Nothing to toggle and nothing to indicate while the list is simply open. */
.vallem-dock .vallem-dock__toggle,
.vallem-dock .vallem-dock__indicator {
	display: none;
}

.vallem-dock .vallem-dock__items {
	display: flex;
	flex-wrap: wrap;
	gap: 0.5rem 1.5rem;
	margin: 0;
	padding: 0;
	list-style: none;
}

.vallem-dock .vallem-dock__item {
	margin: 0;
}

.vallem-dock .vallem-dock__link {
	display: inline-flex;
	align-items: baseline;
	gap: 0.5em;
}

.vallem-dock .vallem-dock__num {
	font-variant-numeric: tabular-nums;
	letter-spacing: 0.04em;
	color: var(--wp--preset--color--contrast-muted);
}

/* ---- Enhanced: the dock -------------------------------------------------- *
 *
 * A full width strip with pointer events off, and the pill inside it with them
 * back on. Centring by flex rather than by translateX, because the script
 * animates the strip's own transform to bring it in and out and two owners of
 * one transform is one too many.
 * ------------------------------------------------------------------------- */

.vallem-dock.is-enhanced {
	position: fixed;
	inset-inline: 0;
	inset-block-end: calc(var(--wp--preset--spacing--30) + env(safe-area-inset-bottom, 0px));

	/* Under the nav's 100, so an open mega menu and its scrim cover the dock
	   rather than the dock punching through them. */
	z-index: 90;

	display: flex;
	justify-content: center;
	max-width: none;
	margin: 0;
	padding-block: 0;
	padding-inline: var(--wp--preset--spacing--50);
	border: 0;
	color: var(--wp--preset--color--base);
	pointer-events: none;
}

/* The box everything is measured against. It carries no padding and no size of
   its own: closed it is exactly the toggle, open the script gives it exactly the
   list's box. A border would break that, because it would sit outside the
   content box the script measured, so the hairline is an inset shadow instead.
   Same construction the nav bar's floating state uses.

   align-items: flex-end pins the toggle to the bottom of the pill, so when the
   pill grows upward on open the toggle stays where the eye left it while it
   fades rather than riding the top edge up. */
.vallem-dock.is-enhanced .vallem-dock__pill {
	position: relative;
	display: flex;
	align-items: flex-end;
	justify-content: center;
	overflow: hidden;

	/* Osmo's own value, and the reason matters: the script tweens this box from
	   the toggle's size to the list's size, so a radius in px large enough to
	   round a 40px tall pill turns the 250px tall open panel into an ellipse.
	   1.5em is a radius the shape can survive being four times taller. */
	border-radius: 1.5em;

	/* Opaque. It was 92%, which was enough to read the story through it. */
	background-color: var(--wp--preset--color--primary-darkest);
	background-image: linear-gradient(
		color-mix(in srgb, currentcolor 10%, transparent),
		color-mix(in srgb, currentcolor 10%, transparent)
	);
	box-shadow:
		inset 0 0 0 1px color-mix(in srgb, currentcolor 20%, transparent),
		0 10px 30px rgb(0 0 0 / 32%);
	pointer-events: auto;
}

.vallem-dock.is-enhanced .vallem-dock__toggle {
	display: flex;
	flex: none;
	align-items: center;
	gap: 0.5rem;
	min-height: 40px;
	padding-block: 0;
	padding-inline: 1rem 0.85rem;
	border: 0;
	background: none;
	color: inherit;
	font: inherit;
	font-size: var(--wp--preset--font-size--small);
	font-weight: 500;
	line-height: 1.5;
	cursor: pointer;
}

/* Both labels share one grid cell during a swap. Stacking them here rather than
   taking one out of flow keeps each of them able to ellipsis on its own, and
   leaves the wrap with a real intrinsic width for the frame before the script
   sets an explicit one. overflow clips the slide. */
.vallem-dock.is-enhanced .vallem-dock__label-wrap {
	display: grid;
	overflow: hidden;
	max-width: min(18rem, calc(100vw - 9rem));
}

.vallem-dock.is-enhanced .vallem-dock__label {
	grid-area: 1 / 1;
	min-width: 0;
	overflow: hidden;
	line-height: 1.5;
	white-space: nowrap;
	text-overflow: ellipsis;
}

/* A word alone at the foot of the screen says nothing about being openable. */
.vallem-dock.is-enhanced .vallem-dock__caret {
	flex: none;
	width: 14px;
	height: 14px;
	background-color: color-mix(in srgb, currentcolor 55%, transparent);
	-webkit-mask: var(--vallem-dock-caret) center / contain no-repeat;
	mask: var(--vallem-dock-caret) center / contain no-repeat;
	transition: background-color 150ms ease;
}

/* Absolute and at max-content, so its measurement never depends on how wide the
   pill happens to be halfway through a tween. Anchored to the bottom, so the
   list unrolls upward out of the closed pill instead of appearing above it.
   visibility rather than display, because a display:none list has no box to
   measure and the script measures it before it is ever shown. */
.vallem-dock.is-enhanced .vallem-dock__list {
	position: absolute;
	inset-block-end: 0;
	inset-inline-start: 0;
	width: max-content;

	/* No cap. It used to stop at 22rem, which is what wrapped "Wat we hebben
	   uitgerold" onto two lines and left the indicator sized for a box the
	   label had already outgrown. Osmo lets the list be as wide as its widest
	   entry and keeps every entry on one line; the viewport guard stays because
	   a phone is narrower than anything Osmo's demo has to survive. */
	max-width: calc(100vw - 2 * var(--wp--preset--spacing--50));
	padding: 6px;
	visibility: hidden;
	opacity: 0;
}

.vallem-dock.is-enhanced .vallem-dock__items {
	display: flex;
	flex-direction: column;
	flex-wrap: nowrap;
	gap: 2px;
}

/* Above the indicator, which is the only reason for the stacking order here. */
.vallem-dock.is-enhanced .vallem-dock__link {
	position: relative;
	z-index: 1;
	display: grid;
	grid-template-columns: auto 1fr;
	align-items: baseline;
	gap: 0.75em;
	padding-block: 0.5rem;
	padding-inline: 0.75rem;
	border-radius: 100em;
	white-space: nowrap;
	color: color-mix(in srgb, currentcolor 68%, transparent);
	font-size: var(--wp--preset--font-size--small);
	font-weight: 500;
	line-height: 1.45;
	text-decoration: none;
	transition: color 150ms ease;
}

.vallem-dock.is-enhanced .vallem-dock__num {
	color: color-mix(in srgb, currentcolor 55%, transparent);
	font-variant-numeric: tabular-nums;
	letter-spacing: 0.04em;
	transition: color 150ms ease;
}

.vallem-dock.is-enhanced .vallem-dock__link[data-active] {
	color: var(--wp--preset--color--base);
}

/* The one place the dock spends a colour rather than a mix of its own. On the
   pill's plate, which is the navy lifted 10% toward white, terminal measures
   7.2:1 and accent 5.0:1. primary is 1.7:1 there and can never appear on it. */
.vallem-dock.is-enhanced .vallem-dock__link[data-active] .vallem-dock__num {
	color: var(--wp--preset--color--terminal);
}

/* Sits behind the active link and hops. Sized and moved entirely by the script
   off the link's own offset box, so a two-line label is followed correctly and
   nothing here has to know what padding the list carries. */
.vallem-dock.is-enhanced .vallem-dock__indicator {
	display: block;
	position: absolute;
	inset-block-start: 0;
	inset-inline-start: 0;
	z-index: 0;
	width: 0;
	height: 0;

	/* Fully round, like the links it sits behind. The left bar that used to be
	   here was a rectangle's idea and reads as a snag on a pill, and it was
	   doing a job the active row's own colour already does. */
	border-radius: 100em;
	background-color: color-mix(in srgb, currentcolor 16%, transparent);
	pointer-events: none;
}

/* Inset, because the pill clips its own overflow and an outward ring would be
   cut off at the corner radius. */
.vallem-dock.is-enhanced .vallem-dock__toggle:focus-visible,
.vallem-dock.is-enhanced .vallem-dock__link:focus-visible {
	outline: 2px solid var(--wp--preset--color--terminal);
	outline-offset: -2px;
}

@media (hover: hover) and (pointer: fine) {
	.vallem-dock.is-enhanced .vallem-dock__link:hover {
		color: var(--wp--preset--color--base);
	}

	.vallem-dock.is-enhanced .vallem-dock__link:hover .vallem-dock__num {
		color: color-mix(in srgb, currentcolor 75%, transparent);
	}

	.vallem-dock.is-enhanced .vallem-dock__toggle:hover .vallem-dock__caret {
		background-color: currentcolor;
	}
}

/* The toggle's accessible name is this line plus whatever label is showing, so
   the button says what it does and still contains its own visible text. */
.vallem-dock .vallem-dock__sr {
	position: absolute;
	width: 1px;
	height: 1px;
	margin: -1px;
	padding: 0;
	overflow: hidden;
	clip-path: inset(50%);
	white-space: nowrap;
	border: 0;
}

/* ---- Narrow -------------------------------------------------------------- */

@media (width <= 781px) {
	.vallem-dock.is-enhanced {
		inset-block-end: calc(var(--wp--preset--spacing--20) + env(safe-area-inset-bottom, 0px));
		padding-inline: var(--wp--preset--spacing--30);
	}

	.vallem-dock.is-enhanced .vallem-dock__label-wrap {
		max-width: calc(100vw - 8rem);
	}

	.vallem-dock.is-enhanced .vallem-dock__list {
		max-width: calc(100vw - 2 * var(--wp--preset--spacing--30));
	}
}

/* ---- Reduced motion ------------------------------------------------------ *
 *
 * The script already runs a profile with every duration at zero. These are the
 * two transitions it does not own.
 * ------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
	.vallem-dock .vallem-dock__link,
	.vallem-dock .vallem-dock__num,
	.vallem-dock .vallem-dock__caret {
		transition: none;
	}
}


/* ------------------------------------------------------------------------- *
 * Hero, over ons. See patterns/hero-over-ons.php
 *
 * Four texts, two of them on screen at any moment. The hero loads carrying the
 * sentence a datasheet would write, skeleton bars drop over every line of it,
 * pulse, and what comes out from under them is three plain words. The
 * behaviour is assets/js/skeleton-swap.js; everything below is the two states
 * that file moves between, plus the ground they sit on.
 *
 * Three states, and which one is the default matters more than the look:
 *
 *   default          the plain heading and the plain sentence, jargon at
 *                    display: none. No JavaScript, a script that 404s, a GSAP
 *                    that never arrives and reduced motion all land here, and
 *                    so does the finished animation. The jargon is never what a
 *                    reader is left holding.
 *   .vallem-swap-js  on <html>, written by the blocking snippet in wp_head
 *                    before anything paints and taken off again when the swap
 *                    has landed. Both texts stacked in one grid cell, jargon
 *                    visible, punchline at opacity zero underneath it.
 *   editor           both texts visible and stacked, so the client can find and
 *                    edit each of them. Without this rule the jargon is at
 *                    display: none in the canvas too and there is no way to
 *                    click it. Same reasoning as the reviews slider, which
 *                    keeps its stacking behind a class the script adds so the
 *                    editor does not hide three of its four reviews.
 * ------------------------------------------------------------------------- */

/* Two classes, because core's own alignment rules are two classes deep and a
   single-class selector loses to them. Same fix as .vallem-hero.alignfull.

   The height is here rather than on the block because core/group has no
   min-height control to put it on, and because it is not decoration: the whole
   effect is a block that shrinks, and a section that shrank with it would drag
   every section under it up the page mid-animation. A fixed floor with the
   content centred in it means the inner block recentres and nothing below the
   fold moves. 880 is the cap so it never eats a large display; 88vh keeps the
   next section's top edge visible on a laptop, which is what stops a
   full-height hero reading as a dead end. */
.wp-block-group.vallem-about-hero {
	display: flex;
	flex-direction: column;
	justify-content: center;
	min-height: min(880px, 88vh);

	/* The bloom at the foot. Two radial gradients rather than a blurred image
	   or a backdrop-filter, for the three reasons already recorded in this
	   file: the reference has no blur on it either, a filter makes the element
	   the containing block for any fixed descendant, and a blurring element
	   repaints on every scroll frame for an effect nobody can see over a ground
	   this dark.

	   Mixed from palette tokens rather than written as flat hex, so the wash is
	   the brand's own blue and violet and follows either of them if they move.
	   background-image paints above the block's background colour and below the
	   content, which is what keeps the ground editable: pick another colour in
	   the editor and the bloom still reads over the top of it.

	   Both stops name the same colour and the far one carries zero alpha.
	   `transparent` is rgb(0 0 0 / 0), so a stop written that way interpolates
	   towards black and greys the fade; the same trap is written up at the
	   reviews rail's wash. Relative colour syntax states "this colour, no
	   alpha" without repeating the mix. A browser that cannot parse it drops
	   the whole declaration and gets the flat navy, which is the safe
	   direction.

	   Both blobs stay in primary and violet. The moment a bloom under white
	   text drifts towards accent or accent-surface the text on it fails, and
	   this one runs the full width of the hero. */
	--vallem-about-bloom-blue: var(--wp--preset--color--primary);
	--vallem-about-bloom-violet: color-mix(in srgb, var(--wp--preset--color--violet) 58%, var(--wp--preset--color--primary));

	background-image:
		radial-gradient(
			64% 42% at 50% 112%,
			rgb(from var(--vallem-about-bloom-violet) r g b / 42%) 0%,
			rgb(from var(--vallem-about-bloom-violet) r g b / 0%) 70%
		),
		radial-gradient(
			52% 34% at 18% 104%,
			rgb(from var(--vallem-about-bloom-blue) r g b / 30%) 0%,
			rgb(from var(--vallem-about-bloom-blue) r g b / 0%) 72%
		);
}

/* The heading's measure is the hero's own contentSize, set on the block. The
   sub carries its own, also on the block, so both are controls in the editor
   rather than numbers in here. Nothing about the measures is in this file on
   purpose: the bars are measured off the rendered line boxes, so they follow
   whatever those controls are changed to. */

/* Balance on the heading so the break lands between the halves of the sentence
   rather than wherever 1100px happens to fall, and pretty on the sub so the
   last line is never one word. Both texts of a pair carry it, because the
   illusion is that one element is being replaced and it falls apart the moment
   the two wrap differently. */
.vallem-swap--title .vallem-swap__before,
.vallem-swap--title .vallem-swap__after {
	text-wrap: balance;
}

.vallem-swap--sub .vallem-swap__before,
.vallem-swap--sub .vallem-swap__after {
	text-wrap: pretty;
}

/* No gap between the two versions, in any state, and the reason is worth
   writing down because it cost a build.

   Core gives every direct child of a layout container a block gap as a
   margin-block-start, and that rule reaches things it was never written for.
   The two texts are alternates of one another and are stacked in the same grid
   cell, so a gap between them is 24px of dead space above the punchline in the
   finished state and 24px of misalignment between the two while they are
   stacked. Worse, the bars are appended into the same container, and a margin
   moves an absolutely positioned box off its containing block just as happily
   as it moves an in-flow one: every bar painted 24px below the line it had just
   been measured onto, while its own inline top said 6px. Measured, not guessed
   - inline top 6px against a painted top of 30, on all eight bars. */
.vallem-about-hero .vallem-swap > * {
	margin-block: 0;
}

/* In the editor the two versions are stacked in the flow and want telling
   apart, so the gap goes back. Same two spellings as the rule above it, and
   inert on the front end for the same reason. */
body.editor-styles-wrapper .vallem-about-hero .vallem-swap > * + *,
.editor-styles-wrapper .vallem-about-hero .vallem-swap > * + * {
	margin-block-start: var(--wp--preset--spacing--30);
}

/* The knobs, on the pair rather than on the hero, so the effect travels to any
   section that carries the class.

   The bars are the navy lifted towards accent, which is the palette's move for
   anything that has to read as a surface on this ground without becoming text.
   They are opaque, so no contrast rule binds them; what does bind is that the
   pulse has to be visible against the base without either of them getting
   bright enough to read as content. */
.vallem-swap {
	--vallem-swap-hold: 2.2s;
	--vallem-swap-bar-base: color-mix(in srgb, var(--wp--preset--color--primary-darkest) 62%, var(--wp--preset--color--accent));
	--vallem-swap-bar-pulse: color-mix(in srgb, var(--wp--preset--color--primary-darkest) 34%, var(--wp--preset--color--accent));
}

/* The default, and the finished state, and the state a reader with no
   JavaScript never leaves. */
.vallem-about-hero .vallem-swap__before {
	display: none;
}

/* The editor canvas, where both texts have to be reachable.

   The front end has no element carrying editor-styles-wrapper, and the canvas
   has it on its <body>. Written twice because core namespaces theme editor
   styles by rewriting a leading `body` into the wrapper class, and the two
   spellings cover it whether that rewrite happens or the file is injected into
   the iframe as it stands. Both are inert on the front end. */
body.editor-styles-wrapper .vallem-about-hero .vallem-swap__before,
.editor-styles-wrapper .vallem-about-hero .vallem-swap__before {
	display: block;
}

/* ---- The live state ------------------------------------------------------ *
 *
 * Anchored on <html> because that is the only element that exists before the
 * body is parsed, and the class has to be on screen from the first paint or the
 * punchline paints and is then replaced by the jargon, which is the effect
 * running backwards.
 *
 * It also means none of this reaches the editor: core rewrites a leading `html`
 * into the wrapper class, and the wrapper never carries vallem-swap-js.
 * ------------------------------------------------------------------------- */

/* Both texts in one grid cell, so the pair is as tall as the taller of them,
   which is the jargon, and the script has a real height to tween away from.
   Aligned to the start of the cell rather than centred in it, so the plain
   text's line boxes stay where they were measured while the height is moving.

   overflow hidden is what makes the shrink read as a box closing: the jargon is
   still three lines tall while the wrapper is on its way down to one, and
   clipping it is the difference between a box collapsing and text spilling out
   of the bottom of one. */
html.vallem-swap-js .vallem-about-hero .vallem-swap {
	position: relative;
	display: grid;
	align-items: start;
	overflow: hidden;
}

html.vallem-swap-js .vallem-about-hero .vallem-swap > * {
	grid-area: 1 / 1;
}

html.vallem-swap-js .vallem-about-hero .vallem-swap__before {
	display: block;
}

/* opacity and not visibility. The h1 underneath is the one thing this hero must
   be able to say at every moment of the animation, and visibility: hidden takes
   it out of the accessibility tree. */
html.vallem-swap-js .vallem-about-hero .vallem-swap__after {
	opacity: 0;
}

/* The placeholder. A span so it can sit inside anything, absolute against the
   pair's padding box, and its geometry written by the script from the measured
   line boxes rather than declared here: there is no number in this file that
   knows how many lines the text is or where they broke.

   4px is the smallest corner this theme uses. A bar is a stand-in for a line of
   text, so it wants the corner of a tag rather than the corner of a card. */
.vallem-swap__bar {
	position: absolute;
	z-index: 1;
	display: block;
	/* Belt to the braces of the margin rule above, so a bar is still a bar in
	   any container that hands its children a block gap. */
	margin: 0;
	border-radius: 4px;
	background-color: var(--vallem-swap-bar-base);
	pointer-events: none;
}

/* ---- Reduced motion ------------------------------------------------------ *
 *
 * The snippet in wp_head never adds the class, so none of the live rules above
 * match and the hero is the plain heading and the plain sentence from the first
 * paint. This is the belt to that braces, for the reader who switches the
 * setting on while the hero is still holding on the jargon.
 * ------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
	html.vallem-swap-js .vallem-about-hero .vallem-swap__before {
		display: none;
	}

	html.vallem-swap-js .vallem-about-hero .vallem-swap__after {
		opacity: 1;
	}

	.vallem-swap__bar {
		display: none;
	}
}

/* ---- Narrow -------------------------------------------------------------- *
 *
 * The jargon is nine lines on a phone against four on a laptop, so the floor
 * has to come down or the hero is a fixed 88vh box with its content overflowing
 * out of both ends of it. Measured at 390 and 500 wide: the pair plus its
 * padding is what sets the height there, not the min-height, and the swap still
 * only moves the inside of the hero because the padding is unchanged.
 * ------------------------------------------------------------------------- */

@media (width <= 781px) {
	.wp-block-group.vallem-about-hero {
		min-height: min(680px, 76vh);
	}
}


/* ---- Ons verhaal, de lichtgolf ------------------------------------------- *
 *
 * The dark middle of the Over ons page. Flat primary-darkest, one narrow
 * column, and three paragraphs that light up character by character as the
 * reader scrolls. The effect is assets/js/gradient-wave.js, the same one the
 * klantverhaal summary carries; this section opts in with the generic
 * `vallem-wave` class rather than with a copy of the script.
 *
 * Almost nothing is styled here on purpose. The measure, the type and the
 * rhythm are all block attributes so the editor owns them, which leaves this
 * block with the two things a block cannot say: the band's own padding, and the
 * custom properties the script reads.
 * ------------------------------------------------------------------------- */

/* One step more air than the other full-bleed sections, which sit on
   spacing--70. This one is the only thing on its screen and the reveal needs
   the first paragraph well clear of the fold before it starts, so the extra
   step is doing work rather than decorating.

   The inline padding is the page gutter, held here because the outer group is
   .alignfull and has had core's root padding cancelled by the negative margins
   that make it full width. Same as .vallem-feature-box. */
.vallem-story {
	padding-block: var(--wp--preset--spacing--80);
	padding-inline: var(--wp--preset--spacing--50);
}

.vallem-story__line {
	/* The crest. gradient-wave.js falls back to no crest at all when nothing
	   names one, and that silent fallback is what shipped on the klantverhaal
	   the first time: characters went from dim straight to white and the colour
	   the effect is actually about never appeared. Terminal is 9.5:1 on this
	   navy and is the one palette colour that can be read on it. */
	--gradient-wave-color-wave: var(--wp--preset--color--terminal);

	/* Where the reveal finishes.

	   The script's default is `center 40%`: it ends when the paragraph's centre
	   is 40% up the viewport. Anchoring on the centre means the finish line
	   moves with the paragraph's height, and it moves the wrong way. Written
	   out for a 900px viewport, `center 40%` is the same as ending with the
	   last line at 360px plus half the paragraph, so the four line paragraph
	   here finishes with its last line at 485px and the nine line one at 640px.
	   Two positions 155px apart for one section, and the long paragraph, the one
	   the reader needs most time on, is the one that runs furthest ahead of
	   them. Measured on the preview at 1440x900, both values screenshotted.

	   `bottom 62%` anchors on the paragraph's own last line instead, so every
	   paragraph finishes at the same place on screen whatever its length: 558px
	   of a 900px viewport, a little below the middle, which is where the eye is
	   by the time it gets there. Each paragraph then completes at the scroll
	   where its own bottom edge reaches that line, which on the preview is 289
	   for the eight line paragraph, 603 for the four line one and 1228 for the
	   nine line one. All three counted character by character out of the DOM at
	   nine scroll positions.

	   Higher than 62% and the last line lights while it is still near the foot
	   of the screen, which is the default's problem again. Lower and it sits
	   there dim past the middle of the screen, where a reader will try to read
	   it.

	   This value now carries a second job. gradient-wave.js gives a paragraph
	   whose preceding sibling carries the same class a start of "wherever that
	   sibling finished" rather than its own "top 90%", which is what stops two
	   crests running down the section at once. So this is the finish line of one
	   paragraph and the starting line of the next, and moving it moves both
	   sides of every seam together. The script reads each paragraph's own
	   property, so a per-paragraph value would still chain correctly; there is
	   just no reason to want one, and one value for all three is what keeps the
	   crest crossing the paragraph break at the same rate it crosses a line
	   break. */
	--gradient-wave-scroll-end: bottom 62%;

	/* pretty and not balance. Balance evens out the lines of a heading, which
	   is right for two or three of them and wrong for a paragraph, where it
	   fights the ragged edge the whole way down. Same call as
	   .vallem-case-wave. */
	text-wrap: pretty;
}


/* ------------------------------------------------------------------------- *
 * Leiderschap, portretkaarten. See patterns/section-founders.php
 *
 * Everything above this section is primary-darkest and this section is canvas,
 * so the page turns light here. It used to turn through a glow across the top
 * edge that started as the navy that had just ended. Nol asked for that gone:
 * the section is its own preset colour and the change of ground is a plain
 * edge. Only the card panels still carry a gradient.
 *
 * The colours the panels mix from stay palette tokens rather than flat hex, so
 * they are the brand's own violet and blue and follow either if it moves.
 * background-image paints above the block's background colour and below its
 * content, so recolouring a card in the editor still reads under the glow.
 *
 * No blur, no backdrop-filter, no image. Three reasons, all already paid for in
 * this repo: a filter makes the element a containing block for any
 * position: fixed descendant, which is the trap that cost this build more time
 * than anything else; a blurring element repaints its backdrop for an effect a
 * gradient reproduces exactly; and the reference has no blur on it either.
 *
 * The references. The 6px corner on the cards is Stripe's, measured for the
 * services bento and reused rather than reinvented. The card itself is Vercel:
 * white plate, a 1px neutral hairline, no shadow, no coloured bar anywhere on
 * it. The proportions are the ones already in this stylesheet, Resend's 1344px
 * content line and the site's own gutter.
 *
 * What this deliberately is not: four cards with a coloured top border, the one
 * thing that has been removed from this build twice.
 * ------------------------------------------------------------------------- */

.vallem-founders {
	/* The two colours the card panels are mixed from. The violet is pulled about
	   half of the way to primary because violet on its own over this navy reads
	   as the purple every gradient hero already has; halfway it reads as the blue
	   end of the brand, which is where the rest of the site lives. */
	--vallem-founders-violet: color-mix(in srgb, var(--wp--preset--color--violet) 52%, var(--wp--preset--color--primary));
	--vallem-founders-blue: var(--wp--preset--color--primary);

	/* The same two at zero alpha, which is how a gradient's far stop is written
	   here rather than as `transparent`. A gradient interpolates in premultiplied
	   alpha, so an engine that does that reaches the same pixels either way, and
	   one that does not fades through transparent black and greys the whole ramp.
	   Both forms were rendered side by side across a 900px ramp in Chrome
	   152.0.7977.82 and came out pixel identical, so this is insurance and not a
	   fix. The fallback is `transparent` so that an engine without relative
	   colour syntax drops these two declarations and keeps the glow on the cards,
	   rather than throwing away the whole background-image. */
	--vallem-founders-violet-0: transparent;
	--vallem-founders-blue-0: transparent;

	padding-block-start: var(--wp--preset--spacing--80);
	padding-block-end: var(--wp--preset--spacing--80);

}

@supports (color: rgb(from red r g b / 0)) {
	.vallem-founders {
		--vallem-founders-violet-0: rgb(from var(--vallem-founders-violet) r g b / 0);
		--vallem-founders-blue-0: rgb(from var(--vallem-founders-blue) r g b / 0);
	}
}

/* The head is centred over a four column grid. This was argued from the glow
   that used to sit behind it, and that glow has been removed, so the only thing
   holding the centring up now is the symmetry of the four cards under it. If
   the page wants one reading axis, this is the section to turn hard left, which
   is the house default and what the kernwaarden section below already does.

   The cap is on the children rather than on the header group. The section is a
   constrained layout and core gives every direct child of one
   `margin-inline: auto !important`, so a narrowed group would centre itself and
   there is no beating that at its specificity. Inside the header the layout is
   flow, which adds no margins at all, so the cap and the centring both have to
   be said here. A width set in the editor lands as an inline style and still
   wins. */
.vallem-founders .vallem-founders__header > * {
	max-width: var(--wp--style--global--content-size);
	margin-inline: auto;
}

/* Balanced, so a two line heading breaks near the middle instead of leaving one
   word on the second line under a centred first. */
.vallem-founders .vallem-founders__title {
	text-wrap: balance;
}

/* The card. A column rather than the flow group the block declares, because the
   grid stretches every card to the height of the tallest one and something in
   the card has to absorb that. Which thing absorbs it is the whole question,
   and the first version got it wrong.

   The first version pushed the panel to the foot with an auto margin. That
   aligned the bottoms and left the slack as white above each panel, so the top
   edge of the panel stepped down across the row, by 51px between the longest
   quote and the shortest. It read as a misalignment rather than as stretch,
   and it is the strongest horizontal line in the section: the photographs line
   up, the names line up, the card feet line up, and that one edge did not.

   So the panel takes the slack instead. The photograph is a fixed aspect ratio
   and the plate above it is one name line and one role line, so both are the
   same height in every card by construction; the panel is the only child that
   grows, and its top lands on the same y in all four. The panels then differ in
   height, which nothing can see because the cards are all the same height
   anyway.

   The one thing that would put the step back is a name long enough to wrap,
   which would make one plate a line taller than the rest. None of the names on
   the site do at any track width this grid produces, measured at 1440, 1000,
   820 and 390. If one ever does, a min-height of 2lh on the name buys the
   alignment back at the cost of a blank line under every short name, and that
   trade is not worth making before it is needed.

   The hairline is mixed out of the card's own colour rather than named, so it
   follows a recolour instead of staying grey on a dark plate. 6px is this
   theme's card corner, the same one the klantverhalen cards and the services
   bento use. overflow: hidden is what makes the corner apply to the photograph
   at the top and the panel at the foot, both of which run to the card edge. */
.vallem-founders .vallem-founders__card {
	display: flex;
	flex-direction: column;
	overflow: hidden;
	border: 1px solid color-mix(in srgb, currentcolor 12%, transparent);
	border-radius: 6px;
}

/* Two classes: core's own `.wp-block-image` carries a bottom margin at one
   class, which would open a white line between the photograph and the name. */
.vallem-founders .vallem-founders__portrait {
	margin: 0;
}

/* The block sets the crop through aspectRatio and scale. This is only the width
   for them to work against: an image block with no intrinsic width to measure
   collapses, which is what an empty slot would do in the editor before anybody
   has uploaded a photograph. */
.vallem-founders .vallem-founders__portrait img {
	display: block;
	width: 100%;
}

/* Secondary copy is the plate's own colour turned down, not a second colour.
   contrast-muted would hardcode dark-on-light and break the moment a card is
   recoloured; opacity follows whatever colour is inherited, and it is the same
   arrangement the statement cards use. */
.vallem-founders .vallem-founders__role,
.vallem-founders .vallem-founders__from {
	opacity: 0.66;
}

/* The panel glow, at card size. Percentages rather than lengths this time,
   because the panel is a small box whose height follows its own copy and the
   two blobs should keep their proportions inside it whatever that copy does.

   The panel is also the child of the card that grows, and its two lines are
   held apart with space-between rather than with an auto margin on the last
   one. Both pin the remit to the foot; the difference is what happens when
   there is no room to spare. An auto margin replaces the block gap outright, so
   the card with the longest quote had its remit sitting hard against the last
   line at zero, while the others had 26 and 51px. space-between distributes
   free space on top of the margins instead of in place of them, so the block
   gap set on the panel in the editor is always the minimum and still does
   something when somebody changes it. Measured after: 8, 34 and 59px.

   The blobs sit in opposite corners so they never reach peak in the same place,
   which is what caps how light this can get under white type. Measured rather
   than assumed: the panels were rendered with their text hidden and every pixel
   of all four sampled. The lightest is #1E1C97, which is 12.6:1 under white, and
   the darkest is the plate's own #071A3A at 17.2:1. The stack is primary, violet
   and primary-darkest and nothing else; accent is the palette colour that would
   break this, at 2.62:1 under white, and it is nowhere near this section. */
.vallem-founders .vallem-founders__panel {
	display: flex;
	flex: 1 1 auto;
	flex-direction: column;
	justify-content: space-between;
	background-image:
		radial-gradient(
			118% 132% at 12% 116%,
			color-mix(in srgb, var(--vallem-founders-violet) 62%, transparent) 0%,
			var(--vallem-founders-violet-0) 76%
		),
		radial-gradient(
			94% 122% at 94% -14%,
			color-mix(in srgb, var(--vallem-founders-blue) 44%, transparent) 0%,
			var(--vallem-founders-blue-0) 78%
		);
}

/* ---- Narrow -------------------------------------------------------------- */

@media (width <= 781px) {
	/* Below the breakpoint the grid is one or two columns and the section runs
	   most of the page, so it gives back a step of vertical space. */
	.vallem-founders {
		padding-block: var(--wp--preset--spacing--70);
	}
}

/* One column, so the card is the full width of the page and a 4:5 portrait is
   the tallest thing on the screen by a distance: at 390 that is a 342px card
   carrying a 428px photograph, and the name under it is pushed off the fold.
   Square gives the face the same width and 86px back.

   561px because that is where core's grid drops to one track. The row is built
   on `minimumColumnWidth: 16rem`, so two 256px tracks and the 24px gap stop
   fitting inside the page gutter just under it. Above this the cards are
   already narrow enough that 4:5 is the better crop.

   `!important` is load bearing here and is the only one in this section. The
   crop is a block attribute, so core serialises it as `aspect-ratio:4/5` in the
   img's own style attribute, and an inline style beats any selector this file
   can write. The same trap is written up under the kernwaarden rules, where the
   answer was to leave the value alone instead. Here the value has to change, so
   this is the only lever there is. Editing the crop in the editor still wins
   above this width and is overridden below it, which is worth knowing before
   somebody sets a crop here and cannot see it on a phone. */
@media (width <= 560px) {
	.vallem-founders .vallem-founders__portrait img {
		aspect-ratio: 1 / 1 !important;
	}
}


/* ------------------------------------------------------------------------- *
 * Sectie, kernwaarden. See patterns/section-values.php
 *
 * Everything else in the section is core layout and preset colours set on the
 * blocks themselves, which is what keeps it editable.
 *
 * There is no plate. `section-statement-cards` and `section-how-it-works-quad`
 * are both already a grid of filled, rounded cards, so this section has to be
 * something else or it is a third copy of a shape that ships twice. What
 * carries the structure instead is one 1px rule over each column, which is the
 * Vercel docs feature grid and Linear's feature columns: no fill, no box, no
 * radius, the divider doing all of the work.
 *
 * The rule is drawn from `currentcolor` rather than from the `border` token,
 * the same call the klantverhaal spec rows and timeline steps make and for the
 * same reason: the ground is a preset the client can change, and a hairline
 * mixed from the section's own text colour survives a recolour where #D4D4D8
 * would disappear on anything dark. 18 percent of #18181B over white is
 * #D6D6D7, the border token to within two levels, so nothing changes on the
 * ground it actually ships on. It resolves to a transparent colour rather than
 * a flattened one, so it composites over whatever is behind it.
 *
 * It is a neutral rule and it is structure. It is not, and must not become, a
 * coloured top accent bar.
 * ------------------------------------------------------------------------- */

.vallem-values {
	padding-block: var(--wp--preset--spacing--80);
}

/* The header runs to the site's prose measure against the full width row under
   it. The cap is on the children and not on the header group, because the
   section is a constrained layout and core gives every direct child of one
   `margin-inline: auto !important`. A narrowed group would centre; narrowed
   children inside a flow layout stay hard left, which is what this wants. */
.vallem-values__header > * {
	max-width: var(--wp--style--global--content-size);
}

.vallem-values__item {
	border-top: 1px solid color-mix(in srgb, currentcolor 18%, transparent);
}

/* Tabular figures so the four numerals occupy the same width and start at the
   same place under their rules. Inter's proportional 1 is narrow enough that
   01 and 04 would otherwise sit at visibly different offsets. The tracking is
   the opening a two digit label wants at 14px. */
.vallem-values__index {
	font-variant-numeric: tabular-nums;
	letter-spacing: 0.08em;
}

/* The four names in place are one word each, so this does nothing today. It is
   here for the Dutch replacements: a 312px column turns a three word value name
   into a ragged two lines often enough to be worth balancing. */
.vallem-values__item-title {
	text-wrap: balance;
}

/* Secondary copy is the item's own colour turned down, not a second colour. A
   muted grey would hardcode dark-on-light and break the moment the section is
   recoloured; opacity follows whatever is inherited. */
.vallem-values__body {
	opacity: 0.72;
	text-wrap: pretty;
}

/* ---- Where the row wraps ------------------------------------------------- *
 *
 * Core's own arithmetic gets this wrong for a set of four. The grid carries
 * `columnCount: 4` and `minimumColumnWidth: 16rem`, which core writes as
 * `repeat(auto-fill, minmax(max(min(16rem, 100%), (100% - 96px) / 4), 1fr))`.
 * auto-fill drops one track at a time, so between a 1184px and an 896px
 * viewport it fits exactly three and the fourth value is orphaned on a row of
 * its own under a 300px rule. Measured, not reasoned: at 1024px the four items
 * came back at lefts 32 / 363 / 693 / 32 and tops 395 / 395 / 395 / 619.
 * A 3 + 1 break reads as a mistake where a 2 + 2 reads as a decision, and
 * 896 to 1184 is an iPad in landscape and half of the laptop window sizes
 * anyone actually uses.
 *
 * 1184px is not a taste number: four 16rem tracks and three 32px gaps need
 * 1120px of container, and the page gutter is 32px a side above an 800px
 * viewport, so 1184 is the exact width at which four columns stop fitting.
 * The rule therefore only ever takes over below the point where core has
 * already given up on four, and never fights it above.
 *
 * This pins the WRAP, not the count. Two equal tracks hold two, three, five or
 * nine items just as well, so duplicating or deleting a value still needs no
 * change here. Two classes to beat core's single-class layout rule; the
 * specificity is what wins, not the source order, which is not guaranteed
 * because block support styles print at wp_head priority 13 and the theme
 * stylesheet at 8.
 * ------------------------------------------------------------------------- */

@media (width < 1184px) {
	.vallem-values .vallem-values__grid {
		grid-template-columns: repeat(2, minmax(0, 1fr));

		/* The column gap stays the block's own 32px. The row gap does not: with
		   the plate gone, the only thing separating a wrapped row from the one
		   above it is 32px of white and then a hairline, which reads as the
		   previous value's underline rather than as the next value's rule. */
		row-gap: var(--wp--preset--spacing--60);
	}
}

@media (width <= 781px) {
	.vallem-values .vallem-values__grid {
		grid-template-columns: minmax(0, 1fr);
	}
}

/* Nothing in here touches the space under each rule at any width, and that is
   not an oversight. That space is the item's own top padding, which is a block
   attribute and therefore an inline style, and an inline style beats anything
   this file could say. A media query written against it would silently do
   nothing. */


/* -----------------------------------------------------------------------------
 * SECTIE, PIJLERS OP ZUILKAARTEN
 *
 * Port of "De fundamenten van ons handelen" from the live vallem.com. The card
 * itself is a cover block, so the image, the overlay colour, the overlay
 * strength, the minimum height and the 25px padding are all block attributes
 * and none of them are in here. What is in here is the part the editor cannot
 * express.
 * ------------------------------------------------------------------------- */

.vallem-pillars__title,
.vallem-pillars__lead {
	/* 632px on the old site, rounded to the scale. Long enough for the heading
	   to break where it breaks there, short enough that the lead does not run
	   the full 1344px. */
	max-width: 40rem;
}

/* The old card pushes its text 200px down from the top and keeps it there, so
   all three names line up whatever the body copy does underneath. That push has
   to shrink on a narrow card, which is why it is here and not a padding set in
   the editor: a padding set there serialises inline and no media query can
   reach it. The card's own 25px stays editable. */
.vallem-pillars .vallem-pillars__card .wp-block-cover__inner-container {
	padding-block-start: 200px;
}

.vallem-pillars .vallem-pillars__card .wp-block-cover__inner-container > * + * {
	margin-block-start: 20px;
}

/* The zuil is decorative and it is already washed out to half strength by the
   overlay. Nothing here should sharpen it. */
.vallem-pillars .vallem-pillars__card .wp-block-cover__image-background {
	/* Drawn from the top right corner at any card size, same as the original's
	   background-position. The focal point attribute sets object-position on
	   the tag; this only guarantees the fit if that attribute is ever cleared. */
	object-fit: cover;
}

/* The row is a core grid at auto-fill, so it drops 3 to 2 near 1010px and 2 to
   1 near 690px on its own. The push follows the card getting shorter in
   proportion, not the breakpoint. */
@media (width <= 1100px) {
	.vallem-pillars .vallem-pillars__card .wp-block-cover__inner-container {
		padding-block-start: 150px;
	}
}

@media (width <= 781px) {
	.vallem-pillars .vallem-pillars__card .wp-block-cover__inner-container {
		padding-block-start: 96px;
	}
}

/* ------------------------------------------------------------------------- *
 * Klantverhalen op een rail. See patterns/section-case-rail.php
 *
 * A port of the customer stories rail on stripe.com/gb, measured off
 * static/css/f5d7a0708b41b5bf.css on 2026-09-07 rather than read off a
 * screenshot. Their numbers, in their own words:
 *
 *   --carousel-card-width  clamp(240px, 26.27cqi, 332px)
 *   --carousel-gap         var(--hds-space-core-200)          16px
 *   .case-study-card       flex column, gap 16, container-type inline-size
 *   .__media               aspect-ratio 332/448, radius md, overflow hidden
 *                          336/350 under 640
 *   .__logo                bottom 12, left 20, height min(60px, 20cqi)
 *   .__title               padding-inline-end 32 (16 from 640), text-wrap
 *                          pretty (balance from 640)
 *   .hds-ui-button         40x40, radius sm, 8px apart
 *   hover                  800ms cubic-bezier(0.165, 0.84, 0.44, 1)
 *
 * The 6px corner and that easing are already in this theme, from the services
 * bento, which is the same house measured in docs/stripe-bento-spec.md. That is
 * the argument for taking this section from Stripe too: it is not a second
 * reference, it is the same one.
 *
 * WHAT IS OURS RATHER THAN THEIRS
 *
 * The content line is 1344px, so the first card starts where every other
 * section's heading starts. The link is `primary`, which is 7.7:1 on white
 * where `accent` is 2.62:1 and fails outright. There is a scrim on the bottom
 * of the photograph, which Stripe has no need for: their card art is
 * commissioned dark, ours is whatever photograph the story came with, and a
 * white knocked out logo on an unknown photograph is a coin toss without one.
 *
 * And the hover is the archive's rather than theirs. Stripe grows the card
 * sideways with a scaleX on the photo and an inverse scaleX on the image inside
 * it, driven by custom properties JavaScript has to measure and write per card.
 * That machinery exists to avoid a reflow, and a fixed width flex item cannot
 * reflow. `.vallem-cases` already answered this: the photograph moves, the card
 * does not. Same duration, same easing, no script.
 * ------------------------------------------------------------------------- */

.vallem-case-rail {
	/* Where the first card's left edge lands, and where the header and the
	   arrows start. One number read by four rules, so none of them can drift.
	   Same construction as the reviews rail's inset, and no viewport unit is
	   involved, so a scrollbar cannot shift one without the others. */
	--vallem-case-rail-inset: max(
		var(--wp--preset--spacing--50),
		(100% - var(--vallem-content-line)) / 2
	);

	/* Stripe's clamp, with the cap converted rather than copied, and the
	   container query unit is why this element is a container at all.

	   Their number is 332px against a 1216px content width, which is 27.3% and
	   puts three and a half cards on screen: the fourth is cut by the viewport
	   edge, and that cut is the only thing telling anybody the row scrolls.
	   Our content line is 1344px, and 332px into 1344px goes four times with
	   16px to spare, so copying the number produced a tidy four column grid
	   that looked like it did not move. Measured, at 1440. The cap is therefore
	   27.3% of our own line, 368px, which puts the fourth card back under the
	   edge where the reference has it.

	   The fluid middle stays theirs. It resolves against this element, which is
	   the full width of the viewport rather than their content box, so it runs
	   a little wide before the cap takes over; below about 1400px the two are
	   the same number either way.

	   Inline size containment only. It does not clip, so the focus ring below
	   still paints outside the card. */
	--vallem-case-rail-card: clamp(240px, 26cqi, 368px);
	--vallem-case-rail-gap: 16px;
	--vallem-case-rail-ease: cubic-bezier(0.165, 0.84, 0.44, 1);

	container-type: inline-size;
}

/* ---- The header --------------------------------------------------------- *
 *
 * Capped on the content line with the gutter as padding, so the cap is the
 * content width and not the border box. Same as the reviews head.
 *
 * Two columns from 940px, which is Stripe's own breakpoint for this section and
 * close enough to the point where a 36px heading and a 20px lead stop fitting
 * side by side. Below it they stack and the group's own block gap does the
 * spacing; above it the grid takes over and the gap has to be zeroed, because
 * core's flow layout writes `> * + * { margin-block-start }` and that margin
 * would push the lead down inside its own grid track.
 * ------------------------------------------------------------------------- */

.vallem-case-rail .vallem-case-rail__header,
.vallem-case-rail .vallem-case-rail__nav,
.vallem-case-rail .vallem-case-rail__empty {
	max-width: calc(var(--vallem-content-line) + 2 * var(--wp--preset--spacing--50));
	margin-inline: auto;
	padding-inline: var(--wp--preset--spacing--50);
}

.vallem-case-rail .vallem-case-rail__heading {
	margin: 0;
	text-wrap: balance;
}

/* No `margin: 0` here, and that is the whole point of this rule.
 *
 * Core writes a flow layout's blockGap as `.wp-container-... > * + *
 * { margin-block-start: <gap> }`, which is (0,1,0). Any rule of ours scoped
 * `.vallem-case-rail .vallem-case-rail__lead` is (0,2,0) and silently wins, so
 * a `margin: 0` reset here does not tidy the paragraph, it deletes the gap the
 * editor thinks it is setting. Measured at 520px: computed margin-top was 0
 * where the header's gap asks for 40, and raising the gap in the pattern from
 * the 40 step to the 60 step changed nothing at all, because nothing was
 * getting through in the first place.
 *
 * The paragraph needs no reset anyway. Core's own `> *` rule zeroes both block
 * margins before it adds the gap back, and the two column grid below zeroes it
 * again from 940px where the gap becomes a grid gap instead. */
.vallem-case-rail .vallem-case-rail__lead {
	max-width: 34em;
}

@media (width >= 940px) {
	.vallem-case-rail .vallem-case-rail__header {
		display: grid;

		/* Not two equal halves. The reference runs a twelve column grid with the
		   heading on 1 to 6 and the lead on 8 to 12, so the lead starts at 59%
		   and not at 51%. Seven and five with the gutter between them lands on
		   the same 59%, and it does it without a twelve track grid nobody can
		   see. */
		grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
		gap: var(--wp--preset--spacing--50);
		align-items: start;
	}

	.vallem-case-rail .vallem-case-rail__header > * + * {
		margin-block-start: 0;
	}
}

/* ---- The arrows --------------------------------------------------------- *
 *
 * Built by assets/js/case-rail.js and inserted between the header and the rail,
 * so nothing here can be drawn on a page where the script did not run.
 *
 * 40x40 and a 4px corner are Stripe's; 4px is also this theme's chip radius, so
 * the number needed no adjusting. The colours are ours: a hairline at rest and
 * `violet-surface` under the pointer, which is the one brand tint that carries
 * violet type at full contrast.
 * ------------------------------------------------------------------------- */

.vallem-case-rail .vallem-case-rail__nav {
	display: flex;
	justify-content: flex-end;
	gap: 8px;
}

.vallem-case-rail .vallem-case-rail__nav button {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	inline-size: 40px;
	block-size: 40px;
	padding: 0;
	border: 1px solid var(--wp--preset--color--border);
	border-radius: 4px;
	background-color: transparent;
	color: var(--wp--preset--color--contrast);
	cursor: pointer;
}

.vallem-case-rail .vallem-case-rail__nav svg {
	inline-size: 20px;
	block-size: 20px;
}

/* Disabled first, so a disabled button under the pointer stays disabled looking
   rather than lighting up: the hover rule below carries :not(:disabled). */
.vallem-case-rail .vallem-case-rail__nav button:disabled {
	opacity: 0.32;
	cursor: default;
}

.vallem-case-rail .vallem-case-rail__nav button:hover:not(:disabled),
.vallem-case-rail .vallem-case-rail__nav button:focus-visible {
	border-color: transparent;
	background-color: var(--wp--preset--color--violet-surface);
	color: var(--wp--preset--color--violet);
}

/* ---- The rail ----------------------------------------------------------- *
 *
 * core/post-template renders a <ul> with one <li> per post, and the <ul> is the
 * scroll container. Its block padding is not decoration: the card's focus ring
 * sits 2px outside the card and `overflow-y` is hidden, so without it the ring
 * would be clipped top and bottom.
 * ------------------------------------------------------------------------- */

.vallem-case-rail .vallem-case-rail__track {
	display: flex;
	align-items: stretch;
	gap: var(--vallem-case-rail-gap);
	margin: 0;
	padding-block: 4px;
	padding-inline-start: var(--vallem-case-rail-inset);

	/* The same inset at the far end, which is one place this does not follow the
	   reference: theirs stops at the gutter. At 1440 the gutter is 32 and the
	   inset is 48, so copying them left the last card overhanging the content
	   line by 16px with the arrows sitting on that line directly above it.
	   Measured at the end of the scroll, where it is the only thing on screen. */
	padding-inline-end: var(--vallem-case-rail-inset);
	list-style: none;
	overflow-x: auto;
	overflow-y: hidden;

	/* Reaching the end of the rail should not start scrolling the page sideways
	   or trigger a back gesture. */
	overscroll-behavior-inline: contain;
	scroll-snap-type: x mandatory;
	scroll-padding-inline-start: var(--vallem-case-rail-inset);
}

/* Only once the arrows exist does the bar stop being the only thing saying
   there is more to the right. */
.vallem-case-rail.is-enhanced .vallem-case-rail__track {
	scrollbar-width: none;
}

.vallem-case-rail.is-enhanced .vallem-case-rail__track::-webkit-scrollbar {
	display: none;
}

.vallem-case-rail .vallem-case-rail__track > li {
	display: flex;
	flex: 0 0 var(--vallem-case-rail-card);
	inline-size: var(--vallem-case-rail-card);
	margin-block: 0;
	scroll-snap-align: start;
}

/* ---- The card ----------------------------------------------------------- *
 *
 * A flex column with no gap of its own: the group ships `blockGap` at the 20
 * step, core writes that as a margin between the children, and setting a `gap`
 * here as well would add the two together. Leaving it to the block also leaves
 * the Dimensions panel something to turn, though only the link answers it: the
 * title's own rule below outranks the gap and carries its own top margin.
 *
 * The container is what the logo sizes against. It also makes the card the
 * containing block for the stretched link below, because inline size
 * containment brings layout containment with it; `position: relative` is here
 * anyway rather than relying on that.
 * ------------------------------------------------------------------------- */

.vallem-case-rail .vallem-case-rail__card {
	position: relative;
	container-type: inline-size;
	display: flex;
	flex-direction: column;
	inline-size: 100%;
}

/* The title takes the slack so the link sits on the floor of every card and the
   row of links lines up across cards of different title lengths. Growing the
   title rather than putting `margin-block-start: auto` on the link, because
   that would replace the block gap instead of adding to it. Same reasoning as
   the reviews card's quote.

   `margin: 0` clears the heading defaults, and at (0,2,0) it also outranks
   core's gap rule, `.wp-container-... > * + *` at (0,1,0). The card's blockGap
   therefore reaches the link and not the title, and the air under the
   photograph is the title block's own top margin, set in the pattern. */
.vallem-case-rail .vallem-case-rail__story {
	flex: 1 1 auto;
	margin: 0;
	padding-inline-end: var(--wp--preset--spacing--40);
	text-wrap: pretty;
}

@media (width >= 640px) {
	.vallem-case-rail .vallem-case-rail__story {
		padding-inline-end: var(--wp--preset--spacing--20);
		text-wrap: balance;
	}
}

/* ---- The picture -------------------------------------------------------- *
 *
 * 332/448 is Stripe's, and the `surface` ground under it is what shows while
 * the photograph loads or when a story has no kaartfoto at all.
 * core/post-featured-image renders a <figure> with the image inside, so the
 * figure is what has to fill the box. Height on the img as well as the figure:
 * without it the intrinsic height wins and a landscape photo letterboxes.
 * ------------------------------------------------------------------------- */

.vallem-case-rail .vallem-case-rail__media {
	position: relative;
	aspect-ratio: 332 / 448;
	overflow: hidden;
	border-radius: 6px;
	background-color: var(--wp--preset--color--surface);
	isolation: isolate;
}

.vallem-case-rail .vallem-case-rail__photo {
	position: absolute;
	inset: 0;
	margin: 0;
	z-index: 0;
}

.vallem-case-rail .vallem-case-rail__photo img {
	display: block;
	inline-size: 100%;
	block-size: 100%;
	object-fit: cover;
	transform-origin: center;
}

/* The scrim, and the only part of this card Stripe has no equivalent of. Navy
   rather than black, same colour as the archive card's, and only at the foot:
   the logo is the one thing that needs a floor under it and the rest of the
   photograph is the point. */
.vallem-case-rail .vallem-case-rail__media::after {
	content: "";
	position: absolute;
	inset: 0;
	z-index: 1;
	pointer-events: none;
	background: linear-gradient(
		180deg,
		rgb(7 26 58 / 0) 52%,
		rgb(7 26 58 / 0.58) 100%
	);
}

/* ---- The logo ----------------------------------------------------------- *
 *
 * Knocked out white, same construction as the archive card and the dark logo
 * row, so one uploaded asset serves all three. Not quite opaque, because pure
 * white on a photograph is harsher than the mark needs to be.
 *
 * Stripe declares `height: min(60px, 20cqi)`, which sounds enormous and is not:
 * their logos are SVGs with a great deal of internal padding, and the mark
 * inside that 60px box measures about 5% of the card width on screen. Ours are
 * cropped tight, so the number that reproduces their optical size is the small
 * one. Their 20px left inset is kept as is.
 *
 * The block is bound to vallem_klant_logo, and an unfilled field is an empty
 * string rather than null, which core writes into the markup as src="".
 * vallem_hide_empty_bindings() in functions.php drops the block instead, so an
 * anonymous story shows a photograph with no logo rather than a broken one.
 * ------------------------------------------------------------------------- */

.vallem-case-rail .vallem-case-rail__logo {
	position: absolute;
	z-index: 2;
	inset-block-end: 20px;
	inset-inline-start: 20px;
	max-inline-size: calc(100% - 40px);
	margin: 0;
}

.vallem-case-rail .vallem-case-rail__logo img {
	display: block;
	inline-size: auto;
	max-inline-size: 100%;
	block-size: clamp(16px, 6cqi, 22px);
	object-fit: contain;
	object-position: left center;
	opacity: 0.92;
	filter: grayscale(1) brightness(0) invert(1);
}

/* ---- The link ----------------------------------------------------------- *
 *
 * The card's only link, and the whole card is its hit area: the anchor's own
 * ::before is stretched over the card rather than a second link being added to
 * the title. One tab stop, and its accessible name is "Lees het verhaal van
 * AVEX", which says both what happens and whose story it is. Stripe wraps the
 * whole card in one anchor and names it with every word inside it, which is
 * worse for anybody listening to it.
 *
 * The colour is on the container rather than on the button block, the same
 * arrangement is-style-subtle uses: the variation takes its colour from
 * currentcolor so one style serves a light section and a dark one, and a colour
 * set in the editor is a preset class carrying !important and still wins over
 * both.
 * ------------------------------------------------------------------------- */

.vallem-case-rail .vallem-case-rail__cta {
	color: var(--wp--preset--color--primary);
}

.vallem-case-rail .vallem-case-rail__cta .wp-block-button__link::before {
	content: "";
	position: absolute;
	inset: 0;
	z-index: 3;
}

/* The ring goes on the card, because the link's own box is a line of text at
   the bottom of it and the thing being activated is the card. */
.vallem-case-rail .vallem-case-rail__card:focus-within {
	outline: 2px solid var(--wp--preset--color--primary);
	outline-offset: 2px;
	border-radius: 6px;
}

.vallem-case-rail .vallem-case-rail__cta .wp-block-button__link:focus-visible {
	outline: none;
}

/* ---- Motion ------------------------------------------------------------- */

@media (hover: hover) and (pointer: fine) {
	.vallem-case-rail .vallem-case-rail__photo img {
		transition: transform 800ms var(--vallem-case-rail-ease);
	}

	.vallem-case-rail .vallem-case-rail__card:hover .vallem-case-rail__photo img,
	.vallem-case-rail .vallem-case-rail__card:focus-within .vallem-case-rail__photo img {
		transform: scale(1.045);
	}

	/* The arrow and the label answer the whole card, not just the line of text,
	   because the whole card is the target. */
	.vallem-case-rail .vallem-case-rail__card:hover .vallem-case-rail__cta .wp-block-button__link::after,
	.vallem-case-rail .vallem-case-rail__card:focus-within .vallem-case-rail__cta .wp-block-button__link::after {
		transform: translateX(var(--vallem-link-arrow-slide));
	}

	.vallem-case-rail .vallem-case-rail__card:hover .vallem-case-rail__cta .vallem-roll__r,
	.vallem-case-rail .vallem-case-rail__card:focus-within .vallem-case-rail__cta .vallem-roll__r {
		transform: translateY(calc(-1 * var(--vallem-roll))) rotate(0.001deg);
	}

	.vallem-case-rail .vallem-case-rail__nav button {
		transition:
			background-color 300ms cubic-bezier(0.25, 1, 0.5, 1),
			border-color 300ms cubic-bezier(0.25, 1, 0.5, 1),
			color 300ms cubic-bezier(0.25, 1, 0.5, 1);
	}
}

@media (prefers-reduced-motion: reduce) {
	.vallem-case-rail .vallem-case-rail__photo img {
		transition: none;
	}

	.vallem-case-rail .vallem-case-rail__card:hover .vallem-case-rail__photo img,
	.vallem-case-rail .vallem-case-rail__card:focus-within .vallem-case-rail__photo img {
		transform: none;
	}

	.vallem-case-rail .vallem-case-rail__card:hover .vallem-case-rail__cta .vallem-roll__r,
	.vallem-case-rail .vallem-case-rail__card:focus-within .vallem-case-rail__cta .vallem-roll__r {
		transform: none;
	}
}

/* ---- One card at a time ------------------------------------------------- *
 *
 * 640px, which is Stripe's breakpoint for this section rather than this
 * stylesheet's usual 781. It is the width at which their card goes to 100% and
 * the picture squares off to 336/350, and both of those are the section's own
 * behaviour rather than a house rule, so the reference wins.
 *
 * A gutter is left over on the right so the next card shows as a sliver. Stripe
 * fills the row exactly and leaves only the gap; the sliver is the reviews
 * rail's arrangement and it is the clearer of the two about there being more.
 * ------------------------------------------------------------------------- */

@media (width <= 639px) {
	.vallem-case-rail {
		--vallem-case-rail-card: calc(100% - var(--wp--preset--spacing--50));
	}

	.vallem-case-rail .vallem-case-rail__media {
		aspect-ratio: 336 / 350;
	}
}

/* ------------------------------------------------------------------------- *
 * De klantverhaalkaart, and the section built around it.
 *
 * Two things in one block because they ship together, but they are not the same
 * scope and the difference is the point:
 *
 * - `.vallem-case-card` is the block, vallem/klantverhaal-kaart, and every rule
 *   for it is unscoped. The block can be dropped on any page, in any column, on
 *   its own, and it has to look like itself wherever it lands.
 * - `.vallem-case-spotlight` is one arrangement of it,
 *   patterns/section-case-spotlight.php: the rail's header with one card under
 *   it. Only the header and the cap belong to the section.
 *
 * The card is the archive's featured card, lifted out of the archive's
 * arithmetic. `.vallem-cases` is scoped to that template throughout and its wide
 * variant is selected with `:nth-child(5n + 1)`, which is a claim about a
 * position in a mosaic of ten; a block that can go anywhere has no mosaic to be
 * first in. So the numbers are written plainly here: the 6px corner, the
 * clamp(380px, 44vw, 640px) box, the 26ch headline, and the attribution the
 * mosaic only shows on a wide card and which is therefore always on.
 *
 * The white pill is the exception and keeps `.vallem-cases__cta`. Those rules
 * are unscoped where they sit, which is this file saying they were a shared
 * component before this needed one; the chip, the two glyph halves, the roll and
 * the reduced-motion branch all arrive with the class. Only the hover triggers
 * are written again below, because those name the archive's card as the thing
 * being pointed at.
 * ------------------------------------------------------------------------- */

/* ---- The card ----------------------------------------------------------- *
 *
 * A grid, so the body stretches to the card on its own. The photograph and the
 * scrim are both absolutely positioned, so the body is the only child in flow
 * and there is nothing to place.
 *
 * The height is a floor and not a height, so a long headline pushes the card
 * taller instead of spilling out of it. The block's Afmetingen panel writes an
 * inline min-height that outranks this, which is what that control is for.
 * ------------------------------------------------------------------------- */

.vallem-case-card {
	position: relative;
	display: grid;
	overflow: hidden;
	min-height: clamp(380px, 44vw, 640px);
	border-radius: 6px;
	isolation: isolate;
}

/* core/post-featured-image renders a <figure> with the image inside, so the
   figure is what has to fill the card. Height on the img as well as the figure:
   without it the intrinsic height wins and a landscape photograph letterboxes.

   Both are absolute against the padding box, which is why the section caps the
   card from a wrapper rather than by padding the card itself. Padding here would
   inset the photograph and leave a frame of scrim around it. */
.vallem-case-card__photo {
	position: absolute;
	inset: 0;
	margin: 0;
	z-index: 0;
}

.vallem-case-card__photo img {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
	transform-origin: center;
}

/* Navy rather than black, and heaviest at both ends: the logo sits at the top
   and the pill at the bottom, and the middle of the photograph is the part worth
   being able to see. The archive's gradient, unchanged. */
.vallem-case-card::before {
	content: "";
	position: absolute;
	inset: 0;
	z-index: 1;
	background:
		linear-gradient(
			180deg,
			rgb(7 26 58 / 0.74) 0%,
			rgb(7 26 58 / 0.38) 42%,
			rgb(7 26 58 / 0.82) 100%
		);
}

.vallem-case-card__body {
	position: relative;
	z-index: 2;
	display: flex;
	flex-direction: column;
	gap: var(--wp--preset--spacing--30);
	box-sizing: border-box;
	padding: clamp(1.25rem, 2.2vw, 1.75rem);
	color: #fff;
}

/* Pushes the pill and the attribution to the floor of the card. */
.vallem-case-card__foot {
	margin-block-start: auto;
}

/* ---- The logo ----------------------------------------------------------- *
 *
 * Knocked out white, the same construction as the archive card, the rail card
 * and the dark logo band, so one uploaded asset serves all four. Not quite
 * opaque, because pure white on this navy is harsher than the type next to it.
 * ------------------------------------------------------------------------- */

.vallem-case-card__logo {
	margin: 0;
}

.vallem-case-card__logo img {
	display: block;
	width: auto;
	max-width: 150px;
	height: clamp(18px, 1.7vw, 24px);
	object-fit: contain;
	object-position: left center;
	opacity: 0.88;
	filter: grayscale(1) brightness(0) invert(1);
}

/* ---- The headline ------------------------------------------------------- *
 *
 * The title is the story and not the customer name. Its link is stretched over
 * the whole card, which is why the pill under it is a paragraph: one target,
 * named by the sentence.
 *
 * 26ch is the archive's measure for the wide card. The size is set on the block
 * rather than here, so the Typografie panel still turns it.
 * ------------------------------------------------------------------------- */

.vallem-case-card__title {
	max-width: 26ch;
	margin: 0;
	color: #fff;
	text-wrap: balance;
}

.vallem-case-card__title a {
	color: inherit;
	text-decoration: none;
}

.vallem-case-card__title a::after {
	content: "";
	position: absolute;
	inset: 0;
	z-index: 3;
}

.vallem-case-card:focus-within {
	outline: 2px solid var(--wp--preset--color--accent);
	outline-offset: 3px;
}

.vallem-case-card__title a:focus-visible {
	outline: none;
}

/* ---- Who said it -------------------------------------------------------- *
 *
 * Always on, unlike the archive, where it is hidden on the narrow cards because
 * a pill and a two line attribution cannot share a 507px row. This card is never
 * that narrow above the breakpoint.
 *
 * Both paragraphs are bound and vallem_hide_empty_bindings() drops an empty one,
 * so a story with no attribution leaves the pill alone rather than a blank column
 * beside it.
 * ------------------------------------------------------------------------- */

.vallem-case-card__who {
	text-align: right;
	line-height: 1.35;
	color: rgb(255 255 255 / 0.72);
}

.vallem-case-card__who p {
	margin: 0;
}

.vallem-case-card__who-name {
	color: #fff;
}

/* ---- Motion ------------------------------------------------------------- *
 *
 * The photograph moves, not the card, and the numbers are the ones every card on
 * this site uses: 800ms on Stripe's curve for the drift, the bento's faster 600
 * for the glyph. The pill's own transitions arrive with `.vallem-cases__cta`;
 * what is here is only the part naming this card as the trigger.
 * ------------------------------------------------------------------------- */

@media (hover: hover) and (pointer: fine) {
	.vallem-case-card__photo img {
		transition: transform 800ms cubic-bezier(0.165, 0.84, 0.44, 1);
	}

	.vallem-case-card:hover .vallem-case-card__photo img,
	.vallem-case-card:focus-within .vallem-case-card__photo img {
		transform: scale(1.045);
	}

	/* The two halves of the expand mark pull apart along the diagonal, one pixel
	   each way. */
	.vallem-case-card:hover .vallem-cases__cta::before,
	.vallem-case-card:focus-within .vallem-cases__cta::before {
		background-position: 1px -1px, -1px 1px;
	}

	.vallem-case-card:hover .vallem-cases__cta .vallem-roll__r,
	.vallem-case-card:focus-within .vallem-cases__cta .vallem-roll__r {
		transform: translateY(calc(-1 * var(--vallem-roll))) rotate(0.001deg);
	}
}

@media (prefers-reduced-motion: reduce) {
	.vallem-case-card__photo img {
		transition: none;
	}

	.vallem-case-card:hover .vallem-case-card__photo img,
	.vallem-case-card:focus-within .vallem-case-card__photo img {
		transform: none;
	}

	.vallem-case-card:hover .vallem-cases__cta .vallem-roll__r,
	.vallem-case-card:focus-within .vallem-cases__cta .vallem-roll__r {
		transform: none;
	}
}

/* ---- One column --------------------------------------------------------- *
 *
 * 781px, this stylesheet's breakpoint everywhere except the rail, which follows
 * its own reference instead. The card has no reference of its own to follow.
 *
 * The pill and the attribution stack, and the attribution goes back to the left
 * because right-aligned type under a left-aligned pill reads as two columns that
 * have come apart.
 * ------------------------------------------------------------------------- */

@media (width <= 781px) {
	.vallem-case-card {
		min-height: clamp(360px, 80vw, 460px);
	}

	.vallem-case-card__foot {
		flex-direction: column;
		align-items: flex-start;
		gap: var(--wp--preset--spacing--20);
	}

	.vallem-case-card__who {
		text-align: left;
	}
}

/* ---- The section -------------------------------------------------------- *
 *
 * Capped on the content line with the gutter as padding, so the cap is the
 * content width and not the border box. The stage is a wrapper around the card
 * rather than the card itself, for the reason written against the photograph
 * above: the card's padding box is what its photograph fills.
 * ------------------------------------------------------------------------- */

.vallem-case-spotlight .vallem-case-spotlight__header,
.vallem-case-spotlight .vallem-case-spotlight__stage {
	max-width: calc(var(--vallem-content-line) + 2 * var(--wp--preset--spacing--50));
	margin-inline: auto;
	padding-inline: var(--wp--preset--spacing--50);
}

.vallem-case-spotlight .vallem-case-spotlight__heading {
	margin: 0;
	text-wrap: balance;
}

/* No `margin: 0`, for the reason spelled out against the rail's lead: core
   writes a flow layout's blockGap as `> * + * { margin-block-start }` at
   (0,1,0), a rule of ours scoped like this one is (0,2,0) and silently wins, so
   a reset here would delete the gap the editor thinks it is setting rather than
   tidy the paragraph. */
.vallem-case-spotlight .vallem-case-spotlight__lead {
	max-width: 34em;
}

@media (width >= 940px) {
	.vallem-case-spotlight .vallem-case-spotlight__header {
		display: grid;

		/* Not two equal halves. Seven and five with the gutter between them puts
		   the lead's left edge at 59%, which is where the reference has it. Same
		   grid as the rail's header, and the reasoning is written there. */
		grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
		gap: var(--wp--preset--spacing--50);
		align-items: start;
	}

	.vallem-case-spotlight .vallem-case-spotlight__header > * + * {
		margin-block-start: 0;
	}
}

/* ------------------------------------------------------------------------- *
 * Button style, "Link met pijl". styles/blocks/button-link-arrow.json
 *
 * A naked link with an arrow after it: no plate, no frame, the label and one
 * glyph. is-style-subtle is the closest thing the theme already had and it is
 * not this, because it has the button's padding and no mark.
 *
 * The variation file registers the style and states the intent; the look is
 * here, for the reason written against is-style-arrow-lead. Variation CSS comes
 * out as `:root :where(.wp-block-button.is-style-link-arrow--N ...)`, which is
 * (0,1,0), and core's own button rules outweigh it. WordPress emits no pseudo
 * states for block style variations at all, so the hover has to be here too.
 *
 * THE PRESS IS TAKEN OFF, AND THAT IS NOT A TASTE DECISION
 *
 * Every button in this theme scales to 0.98 under the pointer. A transform
 * makes the element a containing block for its absolutely positioned
 * descendants, and this style is the one whose ::before is stretched over a
 * whole card. Leave the press on and hovering anywhere on the card puts the
 * pointer over that overlay, which hovers the anchor, which scales it, which
 * shrinks the overlay back to the line of text, which drops the hover, which
 * restores the overlay: a flicker loop, at whatever rate the browser can manage
 * it. Taking the press off removes the containing block and the loop with it.
 * The arrow and the label's roll are the feedback instead, which is what a text
 * link should answer with anyway.
 * ------------------------------------------------------------------------- */

.wp-block-button.is-style-link-arrow > .wp-block-button__link {
	/* 11px against a 16px label, which is the proportion Stripe's 5x8 chevron
	   holds next to theirs. Their arrow travels 6px on hover, but theirs is a
	   chevron whose shaft fades in behind it and ours is one solid glyph, so it
	   would read as the arrow coming loose. Four. */
	--vallem-link-arrow-glyph: 0.6875rem;
	--vallem-link-arrow-slide: 4px;

	display: inline-flex;
	align-items: center;
	gap: 0.4375rem;
	padding: 0;
	background-color: transparent;

	/* currentcolor rather than a preset, so one variation serves a light section
	   and a dark one. Same arrangement as is-style-subtle. */
	color: currentcolor;
	font-weight: 600;
}

.wp-block-button.is-style-link-arrow > .wp-block-button__link:hover,
.wp-block-button.is-style-link-arrow > .wp-block-button__link:focus-visible {
	background-color: transparent;
	color: currentcolor;
	transform: none;
}

/* The glyph is --vallem-arrow, declared once on :root and shared with the mega
   menu's panel titles and is-style-arrow-lead. Masked rather than drawn into
   the data URI so the colour stays a property and follows the label. */
.wp-block-button.is-style-link-arrow > .wp-block-button__link::after {
	content: "";
	flex: none;
	inline-size: var(--vallem-link-arrow-glyph);
	block-size: var(--vallem-link-arrow-glyph);
	background-color: currentcolor;
	-webkit-mask: var(--vallem-arrow) center / contain no-repeat;
	mask: var(--vallem-arrow) center / contain no-repeat;
}

@media (hover: hover) and (pointer: fine) {
	.wp-block-button.is-style-link-arrow > .wp-block-button__link::after {
		transition: transform 300ms cubic-bezier(0.25, 1, 0.5, 1);
	}

	.wp-block-button.is-style-link-arrow > .wp-block-button__link:hover::after,
	.wp-block-button.is-style-link-arrow > .wp-block-button__link:focus-visible::after {
		transform: translateX(var(--vallem-link-arrow-slide));
	}
}

@media (prefers-reduced-motion: reduce) {
	.wp-block-button.is-style-link-arrow > .wp-block-button__link::after {
		transition: none;
		transform: none;
	}
}

/* ------------------------------------------------------------------------- *
 * Sectie, demo aanvragen. See patterns/section-demo-cta.php
 *
 * Two columns on the content line, the copy left and the form card right, with
 * a logo marquee running along the bottom edge of the section.
 *
 * Ported from adaptivesecurity.com/demo/security-awareness-training, measured
 * off its own bundles. Their right column is a hard 533px against a 1fr left,
 * which is what is copied here: the form is a fixed object and the prose is
 * what should give when the window does. Their dashed side rules and their
 * yellow radial glow are both left out; this site has neither.
 *
 * The section is .alignfull, so core's negative margins have cancelled the root
 * padding. The inner group carries the content line and the page gutter back,
 * the same arrangement .vallem-partner-cards uses.
 * ------------------------------------------------------------------------- */

.vallem-demo-cta {
	/* No bottom padding. The marquee owns the space under it, and its own
	   padding is what keeps the logos off the next section. */
	padding-block-start: var(--wp--preset--spacing--70);
}

.vallem-demo-cta__inner {
	display: grid;
	grid-template-columns: minmax(0, 1fr) 33.3125rem;

	/* Stretch, so the copy column is as tall as the form beside it and has room
	   to push its checklist down. `start` here would collapse the column to its
	   own content and there would be nothing to distribute. */
	align-items: stretch;
	gap: var(--wp--preset--spacing--60);
	max-width: var(--vallem-content-line);
	margin-inline: auto;
	padding-inline: var(--wp--preset--spacing--50);
}

/* Short enough to hold the heading to two or three lines at the top of the
   scale, the same measure the partner hero's copy takes. */
.vallem-demo-cta__copy {
	display: flex;
	flex-direction: column;
	max-width: 40rem;
}

/* The checklist sits on the bottom of the column, so the air between the intro
   and the list is whatever the form beside it leaves over. Nol on 2026-09-07.
   The alternative is a fixed gap, which is only ever right at one window width
   and one length of copy.

   Two classes, not one: core writes this column's blockGap as a
   margin-block-start on every child at (0,1,0), and a one-class rule here would
   tie with it and be settled by whichever stylesheet the page printed last. */
.vallem-demo-cta__copy .vallem-demo-cta__expect {
	margin-block-start: auto;

	/* A floor, so the two never touch when the form is short. */
	padding-block-start: var(--wp--preset--spacing--40);
}

/* ---- The ratings -------------------------------------------------------- *
 *
 * Logo, five stars, score. The badges are the ones the reviews carousel already
 * ships, sized by height so a wide badge and a square one read as the same
 * weight.
 * ------------------------------------------------------------------------- */

.vallem-demo-cta__rating-logo {
	margin: 0;
}

.vallem-demo-cta__rating-logo img {
	display: block;
	width: auto;
	height: 1.375rem;
	object-fit: contain;
}

/* The stars are drawn here rather than shipped as an image, because
   assets/images/trustpilot-stars.svg is Trustpilot's own green mark and is
   wrong above a Google badge. Masked rather than filled, so the colour is
   currentcolor and the whole row recolours from the score paragraph's own text
   colour in the editor.

   The glyph is --vallem-stars-glyph, declared once on :root and shared with the
   CTA plate that carries the logo band. It used to be declared here, with a
   note to hoist it the day a second place needed stars. That day arrived.
   Only the height stays local: this row sits beside a 14px score, the other one
   sits alone above a heading. */
.vallem-demo-cta__rating-score {
	--vallem-star-height: 0.875rem;

	display: flex;
	align-items: center;
	gap: 0.5rem;
	font-variant-numeric: tabular-nums;
}

.vallem-demo-cta__rating-score::before {
	content: "";
	flex: none;
	block-size: var(--vallem-star-height);
	aspect-ratio: 100 / 24;
	background-color: currentcolor;
	-webkit-mask: var(--vallem-stars-glyph) center / contain no-repeat;
	mask: var(--vallem-stars-glyph) center / contain no-repeat;
}

/* ---- The checklist ------------------------------------------------------ *
 *
 * The mark is a paragraph, not an image, so both of its colours are on the
 * ordinary Kleur-paneel: the block's background paints the disc and its text
 * colour paints the tick. An image could be swapped but never recoloured, and
 * swapping one would mean uploading an SVG, which WordPress blocks until
 * Vallem Core ships a sanitiser.
 *
 * The character in the markup never renders. font-size: 0 is what hides it,
 * which also sidesteps the fact that U+2713 is outside the unicode-range of
 * both Inter subsets this theme ships and would otherwise arrive from the OS
 * fallback at a different weight on every platform. The tick that does show is
 * a mask of --vallem-tick-glyph, the same glyph the comparison list and the
 * intro points use, so there is one tick on this site and not two.
 * ------------------------------------------------------------------------- */

.vallem-demo-cta__mark {
	--vallem-demo-cta-mark: 1.25rem;

	flex: none;
	inline-size: var(--vallem-demo-cta-mark);
	block-size: var(--vallem-demo-cta-mark);
	border-radius: 50%;
	font-size: 0;

	/* Optically on the first line of the sentence beside it rather than centred
	   on the whole point, so a point that wraps keeps its mark next to the words
	   it belongs to. A fixed value, not the 1lh arithmetic .vallem-tick-list
	   uses, because this element has no line box of its own to measure. */
	margin-block-start: 0.125rem;
}

.vallem-demo-cta__mark::before {
	content: "";
	display: block;
	inline-size: 100%;
	block-size: 100%;
	background-color: currentcolor;
	-webkit-mask: var(--vallem-tick-glyph) center / contain no-repeat;
	mask: var(--vallem-tick-glyph) center / contain no-repeat;
}

/* ---- The card ----------------------------------------------------------- *
 *
 * No frame. It had a hairline, copied from the reference, and Nol took it off
 * on 2026-09-07: a box drawn around a form that is already a column of boxes
 * reads as a second frame inside the first. What is left is a shadow small
 * enough to seat the white plate on `canvas` without the card looking like it
 * is floating over the page.
 * ------------------------------------------------------------------------- */

.vallem-demo-cta__card {
	box-shadow: 0 1px 2px rgb(7 26 58 / 6%);
}

.vallem-demo-cta__fineprint a {
	color: inherit;
}

/* The marquee's own band. Padding rather than margin, so the mask that fades
   the ends has something to sit inside. */
.vallem-demo-cta__logos {
	padding-block: var(--wp--preset--spacing--60) var(--wp--preset--spacing--70);
}

/* Stacked, in the order the blocks are written: the pitch, then the form.
   Pulling the card up with `order` was tried and reverted. It puts a form card
   above the heading that explains it, so the first thing on a phone is six
   fields and no reason to fill them in. The reference solves this by hiding its
   paragraph on desktop and printing a second copy of it under the form, which
   costs an editor two paragraphs that have to be kept in step by hand. Not
   worth it for one screen of scrolling. */
@media (width < 1080px) {
	.vallem-demo-cta__inner {
		grid-template-columns: minmax(0, 1fr);
	}

	.vallem-demo-cta__copy {
		max-width: none;
	}
}

/* ------------------------------------------------------------------------- *
 * Sectie, contactformulier. See patterns/section-contact.php
 *
 * The demo section's grid with the selling taken out. Same 1fr against a fixed
 * 33.3125rem card, same breakpoint, same reason: the form is a fixed object and
 * the prose is what should give when the window does.
 *
 * Written out rather than folded into the .vallem-demo-cta rules it was copied
 * from. The two agree today and nothing says they have to tomorrow: the demo
 * one carries a ratings row, a checklist and a logo band that a contact page
 * will never have, and a shared block would need unpicking the first time
 * either of them moves. What genuinely is shared is the look of the form
 * fields, and that is already one block, at the top of this file.
 *
 * The section is .alignfull, so core's negative margins have cancelled the root
 * padding. The inner group carries the content line and the page gutter back.
 * ------------------------------------------------------------------------- */

.vallem-contact {
	padding-block: var(--wp--preset--spacing--70);
}

.vallem-contact__inner {
	display: grid;
	grid-template-columns: minmax(0, 1fr) 33.3125rem;

	/* Stretch, so the copy column is as tall as the card beside it and has room
	   to push the address down. `start` here would collapse the column to its
	   own content and there would be nothing to distribute. */
	align-items: stretch;
	gap: var(--wp--preset--spacing--60);
	max-width: var(--vallem-content-line);
	margin-inline: auto;
	padding-inline: var(--wp--preset--spacing--50);
}

/* Short enough to hold the heading to two or three lines at the top of the
   scale, the same measure the demo section's copy takes. */
.vallem-contact__copy {
	display: flex;
	flex-direction: column;
	max-width: 40rem;
}

/* The address sits on the bottom of the column, so the air above it is whatever
   the card beside it leaves over, which is the arrangement the demo section
   uses for its checklist.

   Two classes, not one: core writes this column's blockGap as a
   margin-block-start on every child at (0,1,0), and a one-class rule here would
   tie with it and be settled by whichever stylesheet the page printed last. */
.vallem-contact__copy .vallem-contact__direct {
	margin-block-start: auto;

	/* A floor, so the two never touch when the card is short. */
	padding-block-start: var(--wp--preset--spacing--50);
}

/* An address, not body copy that happens to be blue. Black, underlined on
   hover, the same treatment the rest of the site gives an inline link. */
.vallem-contact__email a {
	color: inherit;
	text-decoration: none;
}

.vallem-contact__email a:hover,
.vallem-contact__email a:focus-visible {
	text-decoration: underline;
	text-underline-offset: 0.15em;
}

/* No frame. A box drawn around a form that is already a column of boxes reads
   as a second frame inside the first, which is why Nol took the hairline off
   the demo card on 2026-09-07. What is left is a shadow small enough to seat
   the white plate on `canvas` without it looking like it floats over the page.

   `align-self` opts the card out of the row's stretch. The grid row is as tall
   as whichever column is taller, and when that is the prose, a stretched card
   grows a tall empty plate under the fine print. */
.vallem-contact__card {
	align-self: start;
	box-shadow: 0 1px 2px rgb(7 26 58 / 6%);
}

.vallem-contact__fineprint a {
	color: inherit;
}

/* Stacked, in the order the blocks are written: the reason, then the form. See
   the note on .vallem-demo-cta for why the card is not pulled up with `order`;
   it puts a form above the heading that explains it. */
@media (width < 1080px) {
	.vallem-contact__inner {
		grid-template-columns: minmax(0, 1fr);
	}

	.vallem-contact__copy {
		max-width: none;
	}

	/* Nothing to push against once the columns stack: the copy column is on its
	   own row and at its own height, so `auto` would resolve to zero anyway.
	   Set explicitly so the padding floor above is the only gap left. */
	.vallem-contact__copy .vallem-contact__direct {
		margin-block-start: 0;
		padding-block-start: var(--wp--preset--spacing--40);
	}
}

/* ------------------------------------------------------------------------- *
 * Logo marquee. One band, two places: the klantverhalen archive's hero and the
 * demo section. See assets/js/logo-marquee.js.
 *
 * The track's children come from `vallem/klantlogos` in Vallem Core, a dynamic
 * block, so the row is the klantlogos of the published klantverhalen plus
 * whatever loose logos were picked in its sidebar. That is why there is no
 * markup here to keep in step with the stylesheet: this band was a gallery in
 * the demo section and a hand-written row before that, and both times a class
 * rename in here left every page that had already copied the pattern rendering
 * a heap of unsized logos. Nothing copies a dynamic block.
 *
 * Two boxes, not one. `.vallem-marquee` clips and masks; `.vallem-marquee__track`
 * is what translates. An element cannot be both, it would carry its own
 * clipping window off the screen.
 *
 * The script repeats the set until one half covers the wrapper and then mirrors
 * it, so the animation can travel exactly half the track whatever the count is.
 * Nothing runs until `is-looping` is on, so a page without the script keeps a
 * static clipped row rather than a broken one.
 *
 * It does not pause on hover, deliberately: the band runs the full width and
 * the logos are not links, so there is nothing to stop for and nothing to read;
 * all a pause does is freeze the row wherever the pointer comes to rest. Nol on
 * 2026-09-06, hovering a logo should not stop the marquee.
 * ------------------------------------------------------------------------- */

.vallem-marquee {
	overflow: hidden;

	/* Faded at both ends, so logos arrive and leave rather than being cut. */
	-webkit-mask-image: linear-gradient(90deg, transparent 0%, #000 10%, #000 90%, transparent 100%);
	mask-image: linear-gradient(90deg, transparent 0%, #000 10%, #000 90%, transparent 100%);
}

/* core/group's flex layout comes from a generated
   `.wp-container-core-group-is-layout-*` class at (0,1,0), so one class here
   would only be a tie decided by stylesheet order. Two classes beat it
   outright. */
.vallem-marquee .vallem-marquee__track {
	display: flex;
	flex-wrap: nowrap;
	align-items: center;
	gap: 0;
	width: max-content;
	max-width: none;
}

/* The gap is padding on each item rather than a flex `gap`, because a real gap
   is not part of the 50% the animation travels and the loop would jump by half
   a gap every cycle. */
.vallem-marquee .vallem-marquee__item {
	flex: 0 0 auto;
	width: auto;
	max-width: none;
	margin: 0;
	padding-inline: clamp(2rem, 4.2vw, 3.75rem);
}

/* The height here is the fallback, for the moment before the script has
   measured and for the case where it never runs. logo-marquee.js overwrites
   both axes inline once the files have decoded, because equal height is not
   equal size: it is what made NORDVELD read as twice the logo MEERZICHT is. */
.vallem-marquee .vallem-marquee__item img {
	display: block;
	width: auto;
	height: 26px;
	object-fit: contain;
	opacity: 0.7;
}

/* On a dark band. Knocked out white, the same construction the dark logo row
   uses, and dimmer than the card logos: this is texture behind a headline, not
   a claim being made one logo at a time. */
.vallem-marquee--knockout .vallem-marquee__item img {
	opacity: 0.55;
	filter: grayscale(1) brightness(0) invert(1);
}

/* On a light band. `vallem/klantlogos` in its Zwart style, which Vallem Core
   writes onto every figure it renders because the block has no wrapper of its
   own to carry `is-style-zwart`.

   It exists because the logo files themselves are white: they were cut for the
   knockout band above, and on `canvas` that is white on white, which is how the
   demo section ended up looking empty. `brightness(0)` zeroes the channels and
   leaves the alpha, so a white mark comes back as its own silhouette.

   Deliberately NOT scoped under a `.vallem-marquee` ancestor. The colour is a
   property of the logo, not of the band around it, and the block does get
   dropped outside the scaffold: inserting a fresh Klantlogo-band from the
   inserter lands it as a sibling of the band rather than inside the track, which
   is exactly how it sat on /demo/. Scoped to the wrapper, Zwart silently did
   nothing there, which read as the style being broken rather than the nesting.

   Doubled class rather than one, to match `--knockout` at (0,2,1) so a band
   carrying both still ends up black on source order. */
.vallem-marquee__item.vallem-marquee__item--zwart img {
	filter: grayscale(1) brightness(0);
}

/* Editor only. `vallem/klantlogos` is rendered through ServerSideRender there,
   which puts the block's own wrapper between the track and the figures, and a
   block in normal flow stacks them into a column. Made a flex row rather than
   `display: contents`, which would render correctly and leave the block with no
   box to click. The front end never has this element: the render callback
   returns bare figures. */
.vallem-marquee__track > .wp-block-vallem-klantlogos {
	display: flex;
	flex-wrap: nowrap;
	align-items: center;
}

@media (prefers-reduced-motion: no-preference) {
	.vallem-marquee.is-looping .vallem-marquee__track {
		animation: vallem-marquee 46s linear infinite;
	}
}

@keyframes vallem-marquee {
	from {
		transform: translate3d(0, 0, 0);
	}

	to {
		transform: translate3d(-50%, 0, 0);
	}
}

/* ------------------------------------------------------------------------- *
 * Sectie, donkere uitnodiging met mediakaart.
 * See patterns/section-explore-media.php
 *
 * Copy on the left of the content line, and a card pinned past the right edge
 * of it holding a white panel beside a photo. The overhang is the whole effect,
 * so the section clips rather than the card being measured: the card starts at
 * a fixed offset past the centre line and ends wherever its contents end, and
 * whatever runs off the viewport is cut.
 *
 * Ported from adaptivesecurity.com/demo/security-awareness-training. Their
 * offset is calc(50% + 123px) inside a 1440 container; the same figure is used
 * here against the 1344 content line, which puts the card's left edge in the
 * same place relative to the paragraph beside it.
 * ------------------------------------------------------------------------- */

.vallem-explore {
	position: relative;
	overflow: hidden;
	padding-block: var(--wp--preset--spacing--80);
}

.vallem-explore__inner {
	position: relative;
	display: flex;
	align-items: center;

	/* Holds the card's shape before anyone has uploaded a photo. The reference
	   asks for the same thing with min-h-[470px]. */
	min-block-size: 29.375rem;
	max-width: var(--vallem-content-line);
	margin-inline: auto;
	padding-inline: var(--wp--preset--spacing--50);
}

.vallem-explore__copy {
	max-width: 34rem;
}

/* The reference balances this heading (`text-balance`) and it needs it: at
   34rem the placeholder breaks after the seventh word and leaves one word
   alone on line two. Balancing is the browser's job here rather than the
   copywriter's, because whoever rewrites the line should not have to count
   characters to keep it from happening again. */
.vallem-explore__copy h2 {
	text-wrap: balance;
}

/* The button's trailing arrow. Drawn rather than typed, for the reason written
   against .vallem-nav__panel-title: U+2192 is outside the unicode-range of both
   Inter subsets this theme ships, so a text arrow would come from the OS
   fallback and change shape per platform. Same glyph as the mega menu and the
   arrow-lead button.

   Scoped to the outline style, so a button set to any other style in this
   section, including the link-arrow variation that draws its own, is left
   alone. */
.vallem-explore__actions .wp-block-button.is-style-outline > .wp-block-button__link {
	display: inline-flex;
	align-items: center;
	gap: 0.5rem;
}

.vallem-explore__actions .wp-block-button.is-style-outline > .wp-block-button__link::after {
	content: "";
	flex: none;
	inline-size: 0.875rem;
	block-size: 0.875rem;
	background-color: currentcolor;
	-webkit-mask: var(--vallem-arrow) center / contain no-repeat;
	mask: var(--vallem-arrow) center / contain no-repeat;
}

/* ---- The image ---------------------------------------------------------- *
 *
 * The reference splits this card into a white panel of copy and a video. That
 * was built here as real blocks first and removed: a panel of invented product
 * chrome is a mockup, and this project does not ship those. Nol on 2026-09-07.
 * One image, and it should be a real screenshot or a real photograph.
 *
 * Absolutely placed and clipped by the section. The width is fixed rather than
 * derived, because the overhang is the effect: at the 1344 content line this
 * puts roughly 90px of the picture past the right edge, which is what the
 * reference does with its own card.
 * ------------------------------------------------------------------------- */

.vallem-explore__media {
	position: absolute;
	inset-block: 0;
	inset-inline-start: calc(50% + 7.6875rem);
	inline-size: 40rem;
	margin: 0;
}

/* Fills the card rather than sizing it. A percentage height cannot resolve
   while the row is still being measured, so an in-flow img falls back to its
   own aspect ratio and a portrait shot makes the section as tall as itself.
   The corner is the block's own border control and arrives inline, so it is not
   set here; this only has to not fight it. */
.vallem-explore__media img {
	display: block;
	inline-size: 100%;
	block-size: 100%;
	object-fit: cover;

	/* Reads as a place for a picture during the moment before it decodes. */
	background-color: var(--wp--preset--color--surface);
}

/* Below the point where the overhang has anywhere to go, the image comes back
   into the flow under the copy and runs the full width. */
@media (width < 1280px) {
	.vallem-explore__inner {
		display: block;
		min-block-size: 0;
	}

	.vallem-explore__copy {
		max-width: none;
	}

	.vallem-explore__media {
		position: static;
		inline-size: auto;
		margin-block-start: var(--wp--preset--spacing--60);
	}

	.vallem-explore__media img {
		block-size: clamp(18rem, 42vw, 30rem);
	}
}

/* ------------------------------------------------------------------------- *
 * Sectie, donkere uitnodiging met videomuur.
 * See patterns/section-academy-reel.php
 *
 * The same section as .vallem-explore above, with the one media card replaced
 * by three rows of video: top and bottom travelling right, the middle one
 * travelling left. The copy column, the content line, the min-height and the
 * button's arrow are all that section's numbers, unchanged, so the two read as
 * the same shape when they follow each other down a page.
 *
 * Three states, and the order matters:
 *
 *   plain            the album as a responsive grid under the copy. What the
 *                    editor shows, because scripts are not enqueued into the
 *                    canvas, and what a visitor without JavaScript gets.
 *   .is-reeled       assets/js/academy-reel.js has replaced the album with
 *                    three rows. Everything below the fold of this file that
 *                    positions or masks anything hangs off this class, which is
 *                    why the editor stays editable: nothing here can move a
 *                    block the editor is trying to show you.
 *   .is-looping      the rows are filled and measured, so it is safe to start
 *                    the animation. Same guard, same reason, as .vallem-marquee.
 * ------------------------------------------------------------------------- */

.vallem-academy-reel {
	position: relative;
	overflow: hidden;
	padding-block: var(--wp--preset--spacing--80);

	/* The two things the block panels cannot reach. core/video declares no
	   border support in its block.json, so there is no Randen-paneel for the
	   corner, and the tiles are built at runtime, so there is no block to hang
	   a gap on either. 6px is the card-scale corner from the ladder, the same
	   one the klantverhalen rail gives its photographs; 8px is for large media
	   sitting straight on a section, which a 130px tile is not. */
	--vallem-reel-gap: 1rem;
	--vallem-reel-radius: 6px;
	--vallem-reel-ratio: 16 / 9;
}

.vallem-academy-reel__inner {
	position: relative;
	max-width: var(--vallem-content-line);
	margin-inline: auto;
	padding-inline: var(--wp--preset--spacing--50);
}

/* Balanced for the reason written against .vallem-explore__copy h2: at this
   measure the placeholder leaves one word alone on the second line, and
   whoever rewrites it should not have to count characters to avoid that. */
.vallem-academy-reel__copy h2 {
	text-wrap: balance;
}

/* The button's trailing arrow, drawn rather than typed: U+2192 is outside the
   unicode-range of both Inter subsets this theme ships, so a text arrow would
   come from the OS fallback and change shape per platform. Scoped to the
   outline style so any other button style in this section, including the
   link-arrow variation that draws its own, is left alone. */
.vallem-academy-reel__actions .wp-block-button.is-style-outline > .wp-block-button__link {
	display: inline-flex;
	align-items: center;
	gap: 0.5rem;
}

.vallem-academy-reel__actions .wp-block-button.is-style-outline > .wp-block-button__link::after {
	content: "";
	flex: none;
	inline-size: 0.875rem;
	block-size: 0.875rem;
	background-color: currentcolor;
	-webkit-mask: var(--vallem-arrow) center / contain no-repeat;
	mask: var(--vallem-arrow) center / contain no-repeat;
}

/* Album and rows both sit under the copy when the section is stacked, and they
   want the same air above them. It is the only thing the two states share. */
.vallem-academy-reel__media {
	margin-block-start: var(--wp--preset--spacing--60);
}

/* ---- The album ---------------------------------------------------------- *
 *
 * The pattern's only media block: one group, one core/video per clip, and the
 * rows are built from it rather than written beside it. This is how it looks
 * before that happens, which is to say in the editor and on a page without
 * JavaScript. A grid, so six blocks read as a set rather than as six full-width
 * videos stacked down the page.
 *
 * The tile shape is on the figure and not on the video on purpose. An album
 * entry nobody has picked a video for yet renders as an empty
 * `<figure class="wp-block-video">`: core/video has no render callback, so
 * unlike an empty core/image it does reach the page, and it should read as a
 * place a video goes. currentcolor rather than a fixed grey, so it follows the
 * section if the section is recoloured.
 * ------------------------------------------------------------------------- */

.vallem-academy-reel__album {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(14rem, 1fr));
	gap: var(--vallem-reel-gap);
}

/* `figure` and not just the class, because the two are not the same block in
   the two places this rule has to work. On the page an empty video block is
   `<figure class="wp-block-video"></figure>` and should take the tile's shape;
   in the editor it is a `<div class="wp-block-video">` wrapped round the
   mediakiezer, and giving that a 16:9 box with overflow hidden would clip the
   Uploaden and Mediabibliotheek buttons off the bottom of the thing you are
   trying to click. A filled block is a figure in both. */
.vallem-academy-reel__album figure.wp-block-video {
	margin: 0;
	overflow: hidden;
	aspect-ratio: var(--vallem-reel-ratio);
	border-radius: var(--vallem-reel-radius);
	background-color: color-mix(in srgb, currentcolor 8%, transparent);
}

.vallem-academy-reel__album figure.wp-block-video video {
	display: block;
	inline-size: 100%;
	block-size: 100%;
	object-fit: cover;
}

/* ---- The rows ----------------------------------------------------------- *
 *
 * Built by assets/js/academy-reel.js, so none of this exists until it has run.
 * The row is the window and the track is what moves through it; a tile takes
 * its height from the row and its width from the aspect ratio, so the whole
 * wall resizes itself off one number and there is no breakpoint anywhere in it
 * that has to know how wide a video is.
 * ------------------------------------------------------------------------- */

.vallem-academy-reel__rows {
	display: flex;
	flex-direction: column;
	gap: var(--vallem-reel-gap);
}

.vallem-academy-reel__row {
	position: relative;
	flex: 1;
	min-block-size: 0;
	overflow: hidden;
}

.vallem-academy-reel__track {
	display: flex;
	inline-size: max-content;
	block-size: 100%;
}

/* The gap between tiles is this margin and deliberately not `gap` on the track.
   The loop translates the track by exactly half its width, and with `gap` a
   track of 2n tiles is half a gap wider than two laps, so it jumps by that much
   every time round. A trailing margin per tile makes the two halves exactly
   equal. */
.vallem-academy-reel__item {
	flex: none;
	margin: 0 var(--vallem-reel-gap) 0 0;
	overflow: hidden;
	block-size: 100%;
	aspect-ratio: var(--vallem-reel-ratio);
	border-radius: var(--vallem-reel-radius);
	background-color: color-mix(in srgb, currentcolor 8%, transparent);
}

.vallem-academy-reel__item video {
	display: block;
	inline-size: 100%;
	block-size: 100%;
	object-fit: cover;

	/* No controls are rendered at all, so there is nothing on the tile to
	   click. This closes the last door: a video's native context menu offers
	   "Show controls" in Firefox and picture-in-picture in Safari, and neither
	   belongs on scenery. */
	pointer-events: none;
}

/* Longhands, not the `animation` shorthand, and that is not a style
   preference. The shorthand resets every animation longhand it does not
   mention, animation-play-state included, so the pause rule below was being
   overwritten back to `running` by a selector with more classes in it: the
   video stopped off screen and the rail carried on sliding behind it. Written
   out, nothing else touches play-state and the pause rule stands on its own. */
@media (prefers-reduced-motion: no-preference) {
	.vallem-academy-reel.is-looping .vallem-academy-reel__track {
		animation-duration: var(--vallem-reel-duration, 40s);
		animation-timing-function: linear;
		animation-iteration-count: infinite;
	}

	.vallem-academy-reel.is-looping .vallem-academy-reel__row.is-right .vallem-academy-reel__track {
		animation-name: vallem-reel-right;
	}

	.vallem-academy-reel.is-looping .vallem-academy-reel__row.is-left .vallem-academy-reel__track {
		animation-name: vallem-reel-left;
	}

	/* Start the second and third rows partway through their own lap. A negative
	   delay does that without touching the loop, and it fixes two things at
	   once. The top and bottom rows travel the same way at the same speed, so
	   without it they start on the same offset and every tile edge in the wall
	   lines up in three straight columns. And in the section with quotes the
	   card falls on the same beat in both, so there are moments with no quote
	   anywhere and moments with three: the wall carries one card per six tiles
	   and shows about five, so the phase is the whole difference between a
	   quote being on screen and not. The fractions are deliberately not halves
	   or thirds, which would land back on the six-tile rhythm. */
	.vallem-academy-reel.is-looping .vallem-academy-reel__row:nth-child(2) .vallem-academy-reel__track {
		animation-delay: calc(var(--vallem-reel-duration, 40s) * -0.37);
	}

	.vallem-academy-reel.is-looping .vallem-academy-reel__row:nth-child(3) .vallem-academy-reel__track {
		animation-delay: calc(var(--vallem-reel-duration, 40s) * -0.71);
	}

	/* Off screen is off. The script pauses the video on the same signal; this
	   is the animation half of it. */
	.vallem-academy-reel:not(.is-onscreen) .vallem-academy-reel__track {
		animation-play-state: paused;
	}
}

@keyframes vallem-reel-left {
	from {
		transform: translate3d(0, 0, 0);
	}

	to {
		transform: translate3d(-50%, 0, 0);
	}
}

@keyframes vallem-reel-right {
	from {
		transform: translate3d(-50%, 0, 0);
	}

	to {
		transform: translate3d(0, 0, 0);
	}
}

/* ---- Beside the copy ---------------------------------------------------- *
 *
 * The same 1280 breakpoint .vallem-explore uses, and for the same reason: below
 * it the overhang has nowhere to go. Above it the wall is absolutely placed and
 * the section clips it, so it runs off the right edge of the screen exactly the
 * way that section's card does.
 *
 * inset-inline-end: calc(50% - 50vw - 4rem) puts the right edge four rem past
 * the viewport. The percentage resolves against the centred container, so the
 * arithmetic lands on the viewport edge at any width without a media query
 * knowing the container's number.
 * ------------------------------------------------------------------------- */

@media (width >= 1280px) {
	.vallem-academy-reel.is-reeled .vallem-academy-reel__inner {
		display: flex;
		align-items: center;

		/* .vallem-explore's number. Three rows of 16:9 at this height come out
		   around 150px each, which is the size the wall was drawn at. */
		min-block-size: 29.375rem;
	}

	.vallem-academy-reel.is-reeled .vallem-academy-reel__copy {
		position: relative;
		z-index: 1;
		max-width: 34rem;
	}

	.vallem-academy-reel.is-reeled .vallem-academy-reel__rows {
		position: absolute;
		z-index: 0;
		inset-block: 0;

		/* Starts under the copy, not beside it. The mask below is what decides
		   where the wall becomes visible, so the row can begin early and the
		   tiles can dissolve on their way in rather than appear at an edge. */
		inset-inline-start: 20rem;
		inset-inline-end: calc(50% - 50vw - 4rem);
		margin-block-start: 0;

		/* The dissolve. A mask rather than a gradient painted over the top,
		   because the section keeps its own background attribute and can be
		   recoloured from the Kleur-paneel: a mask fades to whatever colour is
		   behind it, a painted gradient fades to a black that stops being right
		   the moment somebody changes the section.
		   The stops are measured from the row's own left edge, and 16rem is not
		   a guess: the copy is capped at 34rem from the container's content
		   edge and the row starts 20rem from its padding edge, which puts the
		   last character the copy can reach exactly 16rem into the row at every
		   width in this query. So a tile is still fully invisible where the
		   text ends, and only fully itself a further 16rem later. */
		-webkit-mask-image: linear-gradient(to right, transparent 0, transparent 16rem, #000 32rem);
		mask-image: linear-gradient(to right, transparent 0, transparent 16rem, #000 32rem);
	}
}

/* Below that the wall goes under the copy and runs the full width of the
   screen, cancelling the container's gutter to reach both edges. No mask: there
   is no text beside it any more, so there is nothing for it to dissolve
   against, which is the whole reason the mask lives in the query above rather
   than in the base rule. */
@media (width < 1280px) {
	.vallem-academy-reel.is-reeled .vallem-academy-reel__rows {
		margin-inline: calc(-1 * var(--wp--preset--spacing--50));

		/* Three rows share this, so it is the tile size in disguise: a third of
		   it less the gaps, and the width follows from the ratio. The vw term
		   does the work on a phone, where 15rem would have left 69px tiles that
		   read as a texture rather than as video, and the 26rem cap is what
		   stops a tablet in landscape from giving the wall the whole screen. */
		block-size: clamp(19rem, 78vw, 26rem);
	}
}

/* On a phone the gap is worth more as picture than as air: half a rem across
   three rows and their gaps is another ten pixels of height per tile, and a
   tighter weave is the right read for a wall at this size anyway. */
@media (width < 600px) {
	.vallem-academy-reel {
		--vallem-reel-gap: 0.5rem;
	}
}

/* ---- The quote cards ---------------------------------------------------- *
 *
 * See patterns/section-academy-reel-quotes.php. The cards come from the
 * vallem/videomuur block; this is only their look.
 *
 * A wider tile than a video, 2.4, which is the ratio the feature tabs already
 * use for their stage. Wide enough for a sentence at a readable size and
 * different enough from 16:9 that a card reads as a break in the rhythm rather
 * than as a video that failed to load.
 *
 * THE CONTAINER IS THE ROW, NOT THE CARD
 *
 * The type in the wall is in `cqh` so that a card at 146px on a desktop and at
 * 106px on a phone is the same card rather than the same card with the text
 * falling out of it. The query container is `__row`, and putting it on the card
 * instead is a trap worth writing down: container query units used *on* the
 * container resolve against the nearest ancestor container, not against the
 * element itself, and with no ancestor they silently fall back to the small
 * viewport. `padding: 7cqh` on the card came out as 7svh, 66px on a 940px
 * window, which left the card no content box at all and then fed back into
 * every `cqh` inside it, because size containment measures the content box.
 * Measured in Chrome on 2026-09-07, not reasoned about.
 *
 * The album has no row, so it does not use container units at all: it is the
 * editor's view and a fallback, both at a comfortable size, and fixed rem is
 * the right answer there.
 * ------------------------------------------------------------------------- */

.vallem-academy-reel {
	--vallem-reel-quote-ratio: 12 / 5;

	/* currentcolor rather than a grey, for the reason the empty tile gives:
	   the section keeps its background attribute and can be recoloured, and
	   10% of the text colour over the ground is the charcoal Nol asked for at
	   black and the same relationship at anything else. */
	--vallem-reel-quote-plate: color-mix(in srgb, currentcolor 10%, transparent);

	/* `accent` lightened towards white. Nol on 2026-09-08: the quote wanted a
	   lighter blue. Mixed off the preset rather than written as a hex, so it
	   still follows the palette if `accent` is ever retuned, and it lands about
	   #9EC1FF, which is 6.9:1 on the plate against the 4.8:1 accent gave. */
	--vallem-reel-quote-ink: color-mix(in srgb, var(--wp--preset--color--accent) 65%, #fff);
}

/* The album before the script runs, which is also the editor's view of it: six
   cards in a grid you can type into. Wider cells than the video album's,
   because a quote needs the measure. */
.vallem-academy-reel__quotes {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(17rem, 1fr));
	gap: var(--vallem-reel-gap);
}

.vallem-academy-reel__quote {
	display: flex;
	flex-direction: column;
	justify-content: center;
	border-radius: var(--vallem-reel-radius);
	background-color: var(--vallem-reel-quote-plate);
}

.vallem-academy-reel__quote p {
	margin: 0;
}

/* The clamp is the backstop, not the mechanism. The size band below is what
   actually keeps a long quote inside the card; this catches the one that is
   long even at the smallest band. */
.vallem-academy-reel__quote-text {
	display: -webkit-box;
	overflow: hidden;
	-webkit-box-orient: vertical;
	-webkit-line-clamp: 3;
	line-clamp: 3;
	color: var(--vallem-reel-quote-ink);
	line-height: 1.35;
	text-wrap: pretty;
}

/* Naam, rol and bedrijf on one line and small, per Nol on 2026-09-08. They are
   three spans in one paragraph rather than three blocks, so they wrap as a
   sentence does and the separator can be pure adjacency. */
.vallem-academy-reel__quote-meta {
	display: flex;
	flex-wrap: wrap;
	align-items: baseline;
	color: var(--wp--preset--color--base-muted);
	line-height: 1.35;
}

/* Drawn between spans rather than before a named one, so an unfilled rol or
   bedrijf takes its separator with it. The block only emits the spans that
   have something in them, which is what makes adjacency enough. */
.vallem-academy-reel__quote-meta > span + span::before {
	content: "\00b7";
	margin-inline: 0.45em;
	opacity: 0.7;
}

.vallem-academy-reel__quote-name {
	color: var(--wp--preset--color--base);
	font-weight: 600;
}

/* ---- In the album ------------------------------------------------------- */

.vallem-academy-reel__quotes .vallem-academy-reel__quote {
	gap: 0.45rem;
	padding: 1rem 1.25rem;
	aspect-ratio: var(--vallem-reel-quote-ratio);
}

.vallem-academy-reel__quotes .vallem-academy-reel__quote-text {
	font-size: 1.0625rem;
}

.vallem-academy-reel__quotes .vallem-academy-reel__quote-meta {
	font-size: 0.75rem;
}

/* ---- In the wall -------------------------------------------------------- *
 *
 * The row is the query container, so every size below is a fraction of the
 * tile height and the card holds together at any of them. The clamps are the
 * floor and the ceiling: a container query will happily scale text to six
 * pixels, and on a very tall section it would run past the size the copy
 * column is set in.
 *
 * The horizontal padding is wider than the vertical on purpose. The card
 * centres its contents, so short copy leaves more air above and below than a
 * symmetric inset does at the sides, and matching the numbers made the card
 * look narrow-margined rather than even. Nol asked for even; these are the
 * numbers that read that way.
 * ------------------------------------------------------------------------- */

.vallem-academy-reel__row {
	container-type: size;
}

.vallem-academy-reel__item.is-quote {
	gap: 4cqh;
	padding: 9cqh 13cqh;
	aspect-ratio: var(--vallem-reel-quote-ratio);
	background-color: var(--vallem-reel-quote-plate);
}

/* ---- How long is the quote --------------------------------------------- *
 *
 * The card cannot grow, so something has to give when the copy is long. The
 * block counts the characters and puts a band on the card, and the band picks
 * the size: a short quote gets to be big, a long one steps down until it fits.
 * That is the whole trick, and it beats one size with an ellipsis because
 * nothing is ever cut off mid-sentence at the sizes people actually write at.
 *
 * The bands are 60 and 120 characters, measured against this card: about 30
 * characters to a line at the large size and about 42 at the small one, over
 * three lines and four.
 * ------------------------------------------------------------------------- */

.is-quote .vallem-academy-reel__quote-text {
	font-size: clamp(0.8125rem, 13.5cqh, 1.25rem);
}

.is-quote.is-short .vallem-academy-reel__quote-text {
	font-size: clamp(0.9375rem, 16cqh, 1.4375rem);
}

.is-quote.is-long .vallem-academy-reel__quote-text {
	-webkit-line-clamp: 4;
	line-clamp: 4;
	font-size: clamp(0.75rem, 10.5cqh, 1rem);
	line-height: 1.3;
}

.is-quote .vallem-academy-reel__quote-meta {
	font-size: clamp(0.5625rem, 7.5cqh, 0.75rem);
}

/* ---- Hoe aanvallers binnenkomen ------------------------------------------ *
 *
 * Three numbers about how an attack starts, each with its own chart, rotating
 * on a dwell. patterns/section-risk-stats.php ships the markup and
 * assets/js/risk-stats.js says which one is current; everything else is here.
 *
 * The ground is `contrast` (#18181B), set as a block attribute on the pattern's
 * group so it stays a palette choice rather than a number in this file. That is
 * also the colour CLAUDE.md pairs `accent` against at 6.76:1, so the one accent
 * in the section came free.
 *
 * The ink ladder below is neutral rather than the site's blue-greys, because
 * against a true near-black those read cold. None of the four are palette
 * colours: they are Zinc steps chosen for their contrast on this ground and
 * checked rather than eyeballed, 17.0 / 14.0 / 6.9 / 5.2 to one.
 * ------------------------------------------------------------------------- */

.vallem-risk {
	/* Zinc, measured on #18181B. ink-4 is the floor for small text at 5.18:1;
	   an earlier draft had it at 2.41 and was carrying captions and axis
	   labels. */
	--vallem-risk-ink: #FAFAFA;
	--vallem-risk-ink-2: #E4E4E7;
	--vallem-risk-ink-3: #A1A1AA;
	--vallem-risk-ink-4: #8A8A94;
	--vallem-risk-border: #3F3F46;
	--vallem-risk-field: #27272A;

	--vallem-risk-accent: var(--wp--preset--color--accent);

	/* Sequential ramp for the hourly chart, and the low step doubles as the
	   quieter series in the bar charts. One hue (219) rising in lightness with
	   the value, every step at or over 3:1 on the ground: 3.30 / 4.73 / 6.77.
	   Run through the palette validator, not judged by eye. */
	--vallem-risk-seq-1: #5A6B8C;
	--vallem-risk-seq-2: #5883D3;
	--vallem-risk-seq-3: #6B9FFF;

	/* One rule plus its gap, and the plot is exactly 50 of them, so every bar
	   height is a whole number of rules and the ruling never breaks phase at a
	   boundary.

	   This was a 1px rule on a 3px pitch, a hundred lines to the panel. At that
	   spatial frequency the field crawled while the page scrolled: smooth
	   scrolling moves by fractional device pixels, and every fraction shifts the
	   pattern's phase, so a hundred hairlines shimmer together. Halving the
	   frequency roughly halves that. The ink ratio is held at a third by
	   doubling the line along with the pitch, so the weight is unchanged and
	   only the count drops.

	   Do not thin the line back to 1px here: against a 5px gap it is too light
	   to carry the accent. And never halve the pitch instead, because a 1px rule
	   against a 1px gap is a half tone that averages the accent into the ground
	   at 1x, which once shipped a lit bar the same grey as the one beside it. */
	--vallem-risk-rule: 6px;
	--vallem-risk-ink-w: 2px;
	--vallem-risk-plot: 300px;

	/* Clear space above the ruled field for a bar's value label. Without it a
	   tall bar has only the rules it does not use to put its label in, and at 82
	   of 100 that is 54px for a label that needs 44 plus its gap. The 82% sat on
	   the top rules and read as if it had escaped the chart. Headroom rather
	   than a shorter bar, because a rule has to stay worth a percent. */
	--vallem-risk-head: 3rem;

	--vallem-risk-dwell: 8000ms;

	padding-block: var(--wp--preset--spacing--70);
	padding-inline: var(--wp--preset--spacing--50);
	color: var(--vallem-risk-ink);
	overflow: hidden;
}

/* ---- Heading ---- */

/* One h2 for the whole turn. The <strong> around the second sentence is a real
   rich text format, so it survives editing, and the colour follows the emphasis
   instead of being painted on a span the editor cannot see. */
.vallem-risk__title {
	margin: 0 0 var(--wp--preset--spacing--60);
	line-height: 1.1;
	letter-spacing: -0.0223em;
	font-weight: 400;
	color: var(--vallem-risk-ink-3);
}

.vallem-risk__title strong {
	font-weight: 500;
	color: var(--vallem-risk-ink);
}

/* ---- The dwell bar ---- */

.vallem-risk__track {
	position: relative;
	height: 1px;
	background: var(--vallem-risk-field);
	margin-bottom: clamp(1.5rem, 2.5vw, 1.75rem);
}

/* The segment is always visible so the row always says which stat is live, and
   the bright fill on top of it is the dwell running down. One element would
   have had to do both jobs and would have done neither at rest. */
.vallem-risk__seg {
	position: absolute;
	top: -1px;
	left: 0;
	height: 2px;
	width: calc(100% / 3);
	background: var(--vallem-risk-border);
	transition: left 420ms cubic-bezier(.4, 0, .2, 1);
}

.vallem-risk__fill {
	display: block;
	height: 100%;
	background: var(--vallem-risk-ink);
	transform: scaleX(0);
	transform-origin: left center;
}

.vallem-risk.is-enhanced .vallem-risk__fill {
	animation: vallem-risk-fill var(--vallem-risk-dwell) linear forwards;
}

/* Off screen only. Hover and focus are handled below, in CSS, because they are
   states of an element rather than facts the script has to keep a record of. */
.vallem-risk.is-paused .vallem-risk__fill { animation-play-state: paused; }

/* Pausing is scoped to the stat row, not to the section.
   
   The section is full bleed and taller than the viewport, so hovering it was
   not a signal of anything: crossing the band on the way to the navigation
   stopped the rotation, and the mouseenter/mouseleave pair that tracked it
   could be left holding a stale `true` when a leave went missing, which is what
   made resuming feel unreliable. The stat row is the control; resting on it is
   the only pointer gesture that means "hold this one".

   In CSS rather than in the script for the same reason: :hover and
   :focus-visible cannot get out of step with themselves. The theme already
   leans on :has() in the forms and the header for exactly this. */
.vallem-risk:has(.vallem-risk__stats:hover) .vallem-risk__fill,
.vallem-risk:has(.vallem-risk__stat:focus-visible) .vallem-risk__fill {
	animation-play-state: paused;
}

@keyframes vallem-risk-fill {
	from { transform: scaleX(0); }
	to { transform: scaleX(1); }
}

/* ---- The stat row ---- */

.vallem-risk__stats {
	display: grid;
	grid-template-columns: repeat(3, 1fr);
	gap: clamp(1.5rem, 3vw, 2.5rem);
	margin-bottom: var(--wp--preset--spacing--60);
}

/* A button, so the stat is reachable and pressable without the script having to
   invent an affordance for it. Stripped back to nothing visually. */
.vallem-risk__stat {
	display: grid;
	grid-template-columns: auto 1fr;
	column-gap: 0.9rem;
	row-gap: 1rem;
	align-items: start;
	appearance: none;
	background: none;
	border: 0;
	padding: 0;
	margin: 0;
	font: inherit;
	text-align: left;
	color: inherit;
	cursor: pointer;
}

.vallem-risk__figure {
	grid-column: 1;
	display: flex;
	align-items: flex-start;
	gap: 0.26em;
	font-size: clamp(2.375rem, 4vw, 3.25rem);
	line-height: 0.88;
	font-weight: 400;
	letter-spacing: -0.0223em;
	/* Tabular so 82, 62 and 88 take identical width and the row does not shuffle
	   as the active one changes. */
	font-variant-numeric: tabular-nums;
	color: var(--vallem-risk-ink-4);
	transition: color 320ms ease;
}

.vallem-risk__stat.is-active .vallem-risk__figure { color: var(--vallem-risk-ink); }

.vallem-risk__sup {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 0.34em;
	font-size: 0.34em;
	line-height: 1;
	padding-top: 0.2em;
}

.vallem-risk__tri {
	width: 0;
	height: 0;
	border-left: 0.3em solid transparent;
	border-right: 0.3em solid transparent;
	border-bottom: 0.5em solid var(--vallem-risk-border);
	transition: border-bottom-color 320ms ease;
}

.vallem-risk__stat.is-active .vallem-risk__tri { border-bottom-color: var(--vallem-risk-accent); }

.vallem-risk__label {
	grid-column: 2;
	font-size: clamp(0.875rem, 1.05vw, 0.9375rem);
	line-height: 1.45;
	letter-spacing: -0.008em;
	color: var(--vallem-risk-ink-3);
	max-width: 24ch;
	padding-top: 0.1em;
	transition: color 320ms ease;
}

.vallem-risk__stat.is-active .vallem-risk__label { color: var(--vallem-risk-ink-2); }

/* A source chip in Inter at a real weight and in normal case. Thin letterspaced
   uppercase reads as decoration and is the first thing to go illegible at 11px
   on a dark ground. 4px is the bottom of the theme's corner ladder and keeps
   the row reading as instrument rather than as UI. */
.vallem-risk__src {
	grid-column: 1 / -1;
	justify-self: start;
	font-size: 0.8125rem;
	font-weight: 500;
	letter-spacing: -0.006em;
	color: var(--vallem-risk-ink-4);
	border: 1px solid var(--vallem-risk-field);
	border-radius: 4px;
	padding: 0.3em 0.6em;
	transition: color 320ms ease, border-color 320ms ease;
}

.vallem-risk__stat.is-active .vallem-risk__src {
	color: var(--vallem-risk-ink-2);
	border-color: var(--vallem-risk-border);
}

.vallem-risk__stat:focus-visible {
	outline: 2px solid var(--vallem-risk-accent);
	outline-offset: 6px;
}

/* ---- The panel ---- */

/* The three charts stack in one grid cell. All three are laid out, so the row
   takes the height of the tallest and nothing can spill past it.

   This was a fixed height and an absolutely positioned stack, and the height
   was a guess: plot, plus the headroom, plus six rems for whatever came under
   it. The middle chart carries a names row the other two do not, and a caption
   wraps to three lines once the column is narrow enough, so at editor widths
   the caption walked straight out of the box and over the closing line.
   Guessing a height for the tallest of three variable things is not something
   to get right once; a grid measures it. */
.vallem-risk__stage { display: grid; }

.vallem-risk__chart {
	grid-area: 1 / 1;
	margin: 0;
	opacity: 0;
	visibility: hidden;
	transition: opacity 420ms ease, visibility 0s linear 420ms;
}

.vallem-risk__chart.is-current {
	opacity: 1;
	visibility: visible;
	transition: opacity 420ms ease, visibility 0s;
}

/* With no script every chart would stack on the others, so the first one shows
   and the section reads as an ordinary static three up. */
.vallem-risk:not(.is-enhanced) .vallem-risk__chart { opacity: 0; visibility: hidden; }
.vallem-risk:not(.is-enhanced) .vallem-risk__chart:first-of-type { opacity: 1; visibility: visible; }

/* Border box, so the padding is the headroom and the content box stays exactly
   the hundred rules every bar height is counted in. */
.vallem-risk__plot {
	position: relative;
	height: calc(var(--vallem-risk-plot) + var(--vallem-risk-head));
	padding-top: var(--vallem-risk-head);
}

.vallem-risk__caption {
	display: flex;
	justify-content: space-between;
	align-items: flex-start;
	gap: 2.5rem;
	margin-top: 1.1rem;
	padding-top: 0.85rem;
	border-top: 1px solid var(--vallem-risk-field);
	font-size: 0.8125rem;
	line-height: 1.45;
	letter-spacing: -0.006em;
	color: var(--vallem-risk-ink-3);
}

.vallem-risk__caption > :first-child { max-width: 62ch; }

/* Only when there actually is a second one. Two of the three captions are a
   single sentence, and an only child is its own :last-child, so an unscoped
   rule right-aligns the one thing on the line. */
.vallem-risk__caption > :last-child:not(:first-child) {
	text-align: right;
	color: var(--vallem-risk-ink-4);
	flex: none;
}

.vallem-risk__caption strong { font-weight: 500; color: var(--vallem-risk-ink-2); }

/* One ruling under every chart: 1px on a 3px pitch, 100 of them to the plot. */
.vallem-risk__ruled {
	background-image: repeating-linear-gradient(
		to bottom,
		var(--vallem-risk-c) 0 var(--vallem-risk-ink-w),
		transparent var(--vallem-risk-ink-w) var(--vallem-risk-rule)
	);
}

/* ---- The entrance ---- */

/* Each chart draws itself every time it is opened, by the dwell running out or
   by somebody clicking a stat. The first build did this once per chart and then
   left them static, which made a clicked chart feel like it had already
   happened without you.

   clip-path rather than a scale or a height. Scaling squashes the ruling with
   the bar, and growing the height drags the gradient's origin along with the
   top edge so the rules crawl while it fills. An inset clip leaves every rule
   exactly where it will end up and uncovers them from the baseline, which is
   also the only one of the three that costs no layout.

   The clipped state hangs off .is-enhanced, so with the script missing the
   charts are simply drawn, which is the same no-JS contract as everything else
   here. */
.vallem-risk.is-enhanced :is(.vallem-risk__bar, .vallem-risk__hour) > i {
	clip-path: inset(100% 0 0 0);
	transition: clip-path 760ms cubic-bezier(.22, .68, .28, 1);
	transition-delay: calc(var(--i, 0) * var(--vallem-risk-stagger, 80ms));
}

.vallem-risk.is-enhanced .vallem-risk__chart.is-revealed :is(.vallem-risk__bar, .vallem-risk__hour) > i {
	clip-path: inset(0 0 0 0);
}

/* The value sits at the bar's finished height, so without this it hangs over a
   bar that has not arrived yet. Held back a beat so the bar is under it. */
.vallem-risk.is-enhanced .vallem-risk__bar b {
	opacity: 0;
	transition: opacity 420ms ease;
	transition-delay: calc(var(--i, 0) * var(--vallem-risk-stagger, 80ms) + 260ms);
}

.vallem-risk.is-enhanced .vallem-risk__chart.is-revealed .vallem-risk__bar b { opacity: 1; }

/* Twenty four candles want a quicker beat than two bars, or the sweep outlasts
   its welcome. Twenty two milliseconds puts the last one just past half a
   second. */
.vallem-risk__hours { --vallem-risk-stagger: 22ms; }

/* ---- Charts one and two, bars ---- */

/* The ruled field runs behind the bars the way gridlines would. Without it an
   18 against an 82 leaves most of the panel empty, and the empty part reads as
   a mistake rather than as the point being made. */
.vallem-risk__bars {
	display: flex;
	align-items: flex-end;
	gap: clamp(1.25rem, 3vw, 2.5rem);
	height: 100%;
	--vallem-risk-c: var(--vallem-risk-field);
}

.vallem-risk__bar {
	position: relative;
	flex: 1 1 0;
	height: 100%;
	display: flex;
	align-items: flex-end;
}

/* The quieter series sits a step down the same ramp rather than going neutral.
   A grey bar beside three accent ones reads as one that is switched off. */
.vallem-risk__bar i {
	display: block;
	width: 100%;
	height: calc(var(--n) * var(--vallem-risk-rule));
	--vallem-risk-c: var(--vallem-risk-seq-1);
}

.vallem-risk__bar.is-lit i { --vallem-risk-c: var(--vallem-risk-accent); }

/* Value and category on one line, anchored off the same rule count as the bar
   so the label always sits one gap above the top rule whatever the number
   becomes. Reading "82% zonder malware" in place beats reading "82%" and
   hunting for a key underneath. */
.vallem-risk__bar b {
	position: absolute;
	left: 0;
	right: 0;
	bottom: calc(var(--n) * var(--vallem-risk-rule) + 0.7rem);
	display: flex;
	align-items: baseline;
	gap: 0.5rem;
	font-weight: 400;
	white-space: nowrap;
}

.vallem-risk__bar b em {
	font-style: normal;
	font-size: clamp(1.625rem, 2.6vw, 2.25rem);
	font-weight: 500;
	letter-spacing: -0.0223em;
	font-variant-numeric: tabular-nums;
	color: var(--vallem-risk-ink-4);
}

.vallem-risk__bar b span {
	font-size: clamp(0.9375rem, 1.2vw, 1.0625rem);
	letter-spacing: -0.01em;
	color: var(--vallem-risk-ink-3);
}

.vallem-risk__bar.is-lit b em { color: var(--vallem-risk-ink); }
.vallem-risk__bar.is-lit b span { color: var(--vallem-risk-ink-2); }

/* Four categories need the name under the bar. Two do not, because two fit on
   the value line itself. */
.vallem-risk__names {
	display: flex;
	gap: clamp(1.25rem, 3vw, 2.5rem);
	margin-top: 0.8rem;
}

.vallem-risk__names span {
	flex: 1 1 0;
	font-size: 0.875rem;
	line-height: 1.35;
	letter-spacing: -0.008em;
	color: var(--vallem-risk-ink-4);
}

.vallem-risk__names span.is-lit { color: var(--vallem-risk-ink-2); }

/* ---- Chart three, the clock ---- */

.vallem-risk__hours {
	display: flex;
	align-items: flex-end;
	gap: 2px;
	height: 100%;
}

.vallem-risk__hour { flex: 1 1 0; display: flex; align-items: flex-end; }

/* Twice the ink of the other charts. This is the one where colour carries the
   value, and a third of coverage washed the ramp out until the peak and the
   shoulder read alike. Two thirds here against the section's one third. */
.vallem-risk__hour i {
	display: block;
	width: 100%;
	height: calc(var(--n) * var(--vallem-risk-rule));
	--vallem-risk-ink-w: 4px;
	--vallem-risk-c: var(--vallem-risk-seq-1);
}

.vallem-risk__hour.lvl-2 i { --vallem-risk-c: var(--vallem-risk-seq-2); }
.vallem-risk__hour.lvl-3 i { --vallem-risk-c: var(--vallem-risk-seq-3); }

.vallem-risk__axis { position: relative; height: 1.15rem; margin-top: 0.65rem; }

.vallem-risk__axis i {
	position: absolute;
	top: 0;
	font-size: 0.8125rem;
	font-weight: 500;
	font-style: normal;
	font-variant-numeric: tabular-nums;
	color: var(--vallem-risk-ink-4);
	transform: translateX(-50%);
}

.vallem-risk__axis i:first-child { transform: none; }
.vallem-risk__axis i:last-child { transform: translateX(-100%); }

/* Direct labels rather than a legend box. Three steps of one ramp need no key
   when the two bands that matter are named where they sit. */
.vallem-risk__mark {
	position: absolute;
	top: 0;
	font-size: 0.875rem;
	line-height: 1.35;
	letter-spacing: -0.008em;
	color: var(--vallem-risk-ink-4);
	pointer-events: none;
}

.vallem-risk__mark b { display: block; font-weight: 500; color: var(--vallem-risk-ink-2); }
.vallem-risk__mark--peak { left: 0; color: var(--vallem-risk-ink-3); }
.vallem-risk__mark--work { left: 34.5%; }

/* ---- The closing line ---- */

/* The same reveal the Over ons page and the klantverhalen carry, opted into
   with the generic `vallem-wave` class that gradient-wave.js matches on.
   Nothing here reimplements it; this only sets the measure and the two custom
   properties the script reads. Size is a block attribute, a step below the Over
   ons treatment, because this one closes a section rather than owning a
   screen. */
/* The cap is on the child and not on the paragraph itself, for the reason
   .vallem-statement__header already carries: core gives every direct child of a
   constrained layout `margin-left: auto !important; margin-right: auto
   !important`, and there is no beating an !important at that specificity
   without a fight. Inside this group the layout is flow, which adds no margins
   at all, so a plain max-width leaves the line hard left and lined up with the
   charts rather than centred against them. */
.vallem-risk__outro { margin-top: var(--wp--preset--spacing--70); }

/* Narrower than the rest of the section, because this is the one thing here
   that is read as prose rather than scanned. */
.vallem-risk__outro > * { max-width: 900px; }

.vallem-risk__wave {
	color: var(--vallem-risk-ink);

	/* The crest. gradient-wave.js falls back to no crest at all when nothing
	   names one, and that silent fallback is what shipped on the klantverhaal
	   the first time: characters went from dim straight to white and the colour
	   the effect is about never appeared. */
	--gradient-wave-color-wave: var(--vallem-risk-accent);
	--gradient-wave-color-start: rgb(250 250 250 / 0.18);

	/* Anchor the finish on the paragraph's own last line, so it completes at the
	   same place on screen whatever length the copy becomes. Same reasoning as
	   .vallem-story__line. */
	--gradient-wave-scroll-end: bottom 62%;
}

/* ---- Narrow ---- */

@media (max-width: 900px) {
	.vallem-risk__stats { grid-template-columns: 1fr; gap: 2rem; }
	.vallem-risk__seg { width: 100%; }
	.vallem-risk__label { max-width: none; }
	.vallem-risk__bar b { white-space: normal; display: block; }
	.vallem-risk__caption { flex-direction: column; gap: 0.5rem; }
	.vallem-risk__caption > :last-child:not(:first-child) { text-align: left; }
	.vallem-risk__mark--work { display: none; }
}

@media (prefers-reduced-motion: reduce) {
	/* No animation means no animationend, so the section stops advancing on its
	   own and only ever moves on a real click. Nothing needs a branch in JS. */
	.vallem-risk.is-enhanced .vallem-risk__fill { animation: none; transform: scaleX(1); }

	.vallem-risk__chart,
	.vallem-risk__figure,
	.vallem-risk__tri,
	.vallem-risk__src,
	.vallem-risk__seg,
	.vallem-risk__label { transition: none; }

	/* Drawn, not revealed. */
	.vallem-risk.is-enhanced :is(.vallem-risk__bar, .vallem-risk__hour) > i {
		clip-path: none;
		transition: none;
	}

	.vallem-risk.is-enhanced .vallem-risk__bar b { opacity: 1; transition: none; }
}

/* ------------------------------------------------------------------------- *
 * Prijskaarten. See patterns/section-pricing-tiers.php
 *
 * Ported from aikido.dev/pricing, the pentest row at the foot of that page,
 * measured in their own stylesheets on 2026-09-08 rather than estimated off a
 * screenshot. Their numbers, for anyone checking this against the source:
 * card radius 1rem, 1px #e5e5e9 frame, padding 1.5rem/1rem; highlighted card
 * 4px frame in the accent; ribbon 4rem tall at top:-3rem, width 100% + 8px;
 * price 2rem/500; everything else .875rem; list rows 6px apart with 8px beside
 * the tick; 16px between cards.
 *
 * Two of those did not come across. The corner is 8px, which is this theme's
 * plate radius (feature-duo, statement-cards, the feature-tabs stage), not
 * 16px. And the button is this theme's button, so a 0.25em corner rather than
 * their pill.
 *
 * WHY THE CARD IS A SUBGRID
 *
 * The one detail worth stealing outright: the hairline under the description
 * sits at the same height in all three cards even though the middle card's
 * description is two lines longer, and so do the label rows, the tick list and
 * the button. That is the card being a grid item that spans four of the outer
 * grid's rows with `grid-template-rows: subgrid`, so its four bands are placed
 * in tracks that every card shares.
 *
 * It buys the alignment at the cost of a fixed shape: the card has to have
 * exactly four children. A fifth is clamped into the last track and lands on
 * top of the button; a fourth removed leaves a gap. Neither loses a block, and
 * the pattern's four bands are groups precisely so that adding a row means
 * adding it inside one of them, where the count does not matter.
 *
 * The fallback below the @supports is an ordinary flex column with the foot on
 * `margin-block-start: auto`. Only the button still aligns, which is what every
 * pricing table did before subgrid, and it is also what the narrow breakpoint
 * uses deliberately. Chrome 117, Safari 16, Firefox 71.
 *
 * WHAT IS NOT HERE
 *
 * No tooltips. The reference hangs an (i) after eight of its rows; the text
 * would have to live inside a core/list-item, which is the same RichText field
 * that strips the tick's SVG the first time anyone edits the row. See
 * lessons.md. An explanation worth having belongs in the row or in the FAQ.
 *
 * The colours are one blue family: frame and ribbon `accent`, featured button
 * `primary`. The tick is deliberately neutral, currentcolor at 80%,
 * exactly as the reference draws it, because three blues in one card reads as
 * one blue. Green appears once, on the line under the button, and it is a
 * preset on that block rather than anything here.
 * ------------------------------------------------------------------------- */

.vallem-pricing {
	/* ---- The knobs -----------------------------------------------------
	 * --vallem-pricing-pad     the card's own padding. Spelled twice, here
	 *                          and as spacing|30 on the block, because the
	 *                          ribbon has to bleed out of it by exactly this
	 *                          much when it stops being absolute and a
	 *                          var() cannot go in a block attribute. Change
	 *                          one and change the other.
	 * --vallem-pricing-frame   the highlighted card's border width. Same
	 *                          arrangement: it is on the block so the editor
	 *                          owns it, and repeated here because the ribbon
	 *                          is positioned against the outside of it.
	 * --vallem-pricing-radius  the plate corner, matching the block.
	 * --vallem-pricing-ribbon  height of the lint above the featured card.
	 * --vallem-pricing-rule    the inner hairlines. Derived from the `border`
	 *                          slug rather than invented: #D4D4D8 straight is
	 *                          heavier than the reference's #e5e5e9, and 55%
	 *                          of it on white lands on that almost exactly.
	 * --vallem-pricing-bands   how many rows a card spans. It is the number of
	 *                          groups inside the card and nothing else.
	 * ------------------------------------------------------------------- */
	--vallem-pricing-pad: var(--wp--preset--spacing--30);
	--vallem-pricing-frame: 4px;
	--vallem-pricing-radius: 8px;
	--vallem-pricing-ribbon: 44px;
	--vallem-pricing-rule: color-mix(in srgb, var(--wp--preset--color--border) 55%, transparent);
	--vallem-pricing-bands: 4;
}

/* ---- The section head --------------------------------------------------- */

/* Capped short of the content line so the heading does not run the full 1344px
   of the card row underneath it. Same measure the other section heads use. */
.vallem-pricing .vallem-pricing__title {
	max-width: 20ch;
}

.vallem-pricing .vallem-pricing__lead {
	max-width: 62ch;
}

/* ---- The row ------------------------------------------------------------ */

/* Room for the ribbon, which is out of flow and therefore cannot make its own.
   The frame is in the sum because the ribbon sits against the outside of it. */
.vallem-pricing .vallem-pricing__row {
	padding-block-start: calc(var(--vallem-pricing-ribbon) + var(--vallem-pricing-frame));
}

/* ---- The card ----------------------------------------------------------- */

/* position: relative for the ribbon, and the flex fallback for the four bands.
   Nothing clips: the ribbon hangs outside the box, so an overflow rule here
   would cut it off. */
.vallem-pricing .vallem-pricing__card {
	position: relative;
	display: flex;
	flex-direction: column;
}

.vallem-pricing .vallem-pricing__body {
	flex: 1 1 auto;
}

.vallem-pricing .vallem-pricing__foot {
	margin-block-start: auto;
}

@supports (grid-template-rows: subgrid) {
	.vallem-pricing .vallem-pricing__card {
		display: grid;
		grid-row: span var(--vallem-pricing-bands);
		grid-template-rows: subgrid;

		/* The outer grid's 16px is the gap between cards. Inside the card the
		   bands meet on their own padding and their hairlines, so the gutter
		   is overridden rather than inherited. */
		row-gap: 0;
	}

	/* Both are the flex fallback's job and would fight the tracks here: a band
	   stretches to fill its row, which is what puts every hairline on the same
	   line, and an auto margin would pull the foot to the bottom of its row
	   instead of the top, so the third card's button would drop below the other
	   two the moment its reassurance line is deleted. */
	.vallem-pricing .vallem-pricing__body {
		flex: none;
	}

	.vallem-pricing .vallem-pricing__foot {
		margin-block-start: 0;
	}
}

/* ---- The ribbon --------------------------------------------------------- */

/* The ribbon overlaps the top of the card by exactly the corner radius, and
   that number is the whole rule.

   It first sat flush, bottom edge against the card's top edge, on the reasoning
   that the ribbon and the frame are the same colour so there was no seam to
   hide. That is wrong at the corners. The card's top corners are round and the
   ribbon's bottom corners are square, so for the first 8px of the card's height
   its edges curve inward away from the ribbon's, and the section background
   shows through as a crescent bitten out of each side of the join. Invisible on
   the light ground it was designed against, obvious the moment the section is
   recoloured dark. Found from a screenshot, then measured: 7 device pixels of
   #1D2228 between the ribbon and the frame at 1px in from the card's edge.

   The corner curve occupies exactly the first `radius` of the card's height, so
   a ribbon that reaches `radius` past the card's top edge covers both curves
   completely and meets the card where its sides have gone vertical. Below that
   there is nothing to mismatch.

   That is why the height is ribbon + radius while the block inset is unchanged:
   the extra length is spent downward, so the ribbon still stands exactly
   `--vallem-pricing-ribbon` proud of the card and the row's top padding still
   matches. The padding-block-end takes the overlap back out of the content box
   so the label stays centred in the part of the ribbon that is actually
   visible.

   No z-index anywhere. The reference hides its own join by putting the ribbon
   at `z-index: -1` and painting the card over it, which only works while no
   ancestor has made a stacking context and would put the ribbon behind the
   section's own background here. An absolutely positioned child already paints
   above its parent's background and border, so overlapping is enough.

   The containing block of an absolutely positioned child is the padding box, so
   the inline inset is the frame width and the block inset is the ribbon plus
   the frame. Out of flow also means it is not a grid item, which is what keeps
   the featured card at four bands like its neighbours. */
.vallem-pricing .vallem-pricing__ribbon {
	position: absolute;
	inset-block-start: calc(-1 * (var(--vallem-pricing-ribbon) + var(--vallem-pricing-frame)));
	inset-inline: calc(-1 * var(--vallem-pricing-frame));
	box-sizing: border-box;
	block-size: calc(var(--vallem-pricing-ribbon) + var(--vallem-pricing-radius));
	padding-block-end: var(--vallem-pricing-radius);
	display: flex;
	align-items: center;
	justify-content: center;
	border-start-start-radius: var(--vallem-pricing-radius);
	border-start-end-radius: var(--vallem-pricing-radius);
}

/* ---- The price ---------------------------------------------------------- */

/* blockGap is 0 on all four bands, so every gap inside a card is one of these.
   Written as margin-block-start to stay on the axis core's flow layout uses, at
   two classes so it clears that layout's generated rule whichever stylesheet
   the page prints last. */
.vallem-pricing .vallem-pricing__amount {
	margin-block-start: 14px;

	/* The heading ladder deliberately only reaches headings, and the amount is
	   a paragraph because a price is not a section of the document. These are
	   the ladder's display-small values, copied so the amount is set like the
	   display type it looks like. */
	font-weight: 500;
	letter-spacing: -0.025em;
	line-height: 1.05;
}

.vallem-pricing .vallem-pricing__unit {
	margin-block-start: 6px;
}

/* The one link in the card. It reads as part of the line rather than as a
   button: the colour comes from the sentence it sits in and only the rule under
   it says it is a link, which is what the reference does. Both are states, so
   neither can come off the block. */
.vallem-pricing .vallem-pricing__unit a {
	color: inherit;
	text-decoration-color: color-mix(in srgb, currentcolor 45%, transparent);
	text-underline-offset: 0.2em;
	transition: color 200ms ease, text-decoration-color 200ms ease;
}

.vallem-pricing .vallem-pricing__unit a:hover,
.vallem-pricing .vallem-pricing__unit a:focus-visible {
	color: var(--wp--preset--color--primary);
	text-decoration-color: currentcolor;
}

/* ---- The bands ---------------------------------------------------------- */

.vallem-pricing .vallem-pricing__pitch {
	padding-block: 16px var(--vallem-pricing-pad);
	border-block-end: 1px solid var(--vallem-pricing-rule);
}

.vallem-pricing .vallem-pricing__body {
	padding-block-start: var(--vallem-pricing-pad);
}

.vallem-pricing .vallem-pricing__spec-value {
	margin-block-start: 8px;
}

.vallem-pricing .vallem-pricing__spec-value + .vallem-pricing__spec-label {
	margin-block-start: 16px;
}

.vallem-pricing .vallem-pricing__foot {
	padding-block-start: 16px;
	border-block-start: 1px solid var(--vallem-pricing-rule);
}

/* ---- The tick list ------------------------------------------------------ */

/* Drawn from the marker side, the same construction as the comparison list and
   for the same reason: a core/list-item is a RichText field, so an inline SVG
   inside one is stripped the first time anybody edits that row. From here,
   pressing Enter adds a seventh row and the tick arrives with it.

   A mask over a painted colour rather than a coloured image, so the colour is a
   property. currentcolor at 80% is the reference's own `stroke-opacity: 0.8` on
   the text colour, which keeps the list neutral against a card that is already
   carrying a blue frame and a blue button. */
.vallem-pricing .vallem-pricing__list {
	--vallem-pricing-tick-size: 18px;
	--vallem-pricing-tick: color-mix(in srgb, currentcolor 80%, transparent);

	margin-block-start: 20px;
	margin-inline: 0;
	padding-inline-start: 0;
	list-style: none;
}

.vallem-pricing .vallem-pricing__list li {
	display: flex;
	align-items: flex-start;
	gap: 10px;
	margin: 0;
}

.vallem-pricing .vallem-pricing__list li + li {
	margin-block-start: 8px;
}

.vallem-pricing .vallem-pricing__list li::before {
	content: "";
	flex: none;
	inline-size: var(--vallem-pricing-tick-size);
	block-size: var(--vallem-pricing-tick-size);
	background-color: var(--vallem-pricing-tick);
	-webkit-mask: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M6.5 12.4L10 15.9L17.5 8.4' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='square' stroke-linejoin='round'/></svg>") center / contain no-repeat;
	mask: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M6.5 12.4L10 15.9L17.5 8.4' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='square' stroke-linejoin='round'/></svg>") center / contain no-repeat;

	/* Centred on the first line rather than on the whole row, so a row that
	   wraps keeps its mark beside the words it belongs to. The em value is the
	   fallback for browsers without lh and matches the 1.5 the block carries at
	   14px. */
	margin-block-start: 0.13em;
}

@supports (height: 1lh) {
	.vallem-pricing .vallem-pricing__list li::before {
		margin-block-start: calc((1lh - var(--vallem-pricing-tick-size)) / 2);
	}
}

/* ---- The line under the button ------------------------------------------ */

/* Its colour is the `success` preset on the block, so the mark takes
   currentcolor and the whole line recolours from the editor as one thing. */
.vallem-pricing .vallem-pricing__note {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 6px;
	text-align: center;
}

.vallem-pricing .vallem-pricing__note::before {
	content: "";
	flex: none;
	inline-size: 16px;
	block-size: 16px;
	background-color: currentcolor;
	-webkit-mask: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M7 2h10a5 5 0 0 1 5 5v10a5 5 0 0 1-5 5H7a5 5 0 0 1-5-5V7a5 5 0 0 1 5-5Zm9.73 8.684a1 1 0 0 0-1.46-1.368l-3.083 3.29-1.523-1.353a1 1 0 1 0-1.328 1.494l2.25 2a1 1 0 0 0 1.393-.063l3.751-4Z'/></svg>") center / contain no-repeat;
	mask: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M7 2h10a5 5 0 0 1 5 5v10a5 5 0 0 1-5 5H7a5 5 0 0 1-5-5V7a5 5 0 0 1 5-5Zm9.73 8.684a1 1 0 0 0-1.46-1.368l-3.083 3.29-1.523-1.353a1 1 0 1 0-1.328 1.494l2.25 2a1 1 0 0 0 1.393-.063l3.751-4Z'/></svg>") center / contain no-repeat;
}

/* ---- Narrow ------------------------------------------------------------- *
 *
 * The outer grid drops to two columns and then to one at its own widths, which
 * core computes from `max(min(20rem, 100%), (100% - 2 * gap) / 3)`. Three columns
 * need (W - 2 * 32 - 32) / 3 >= 320, so W >= 1056, and it
 * is where the subgrid has to go: the ribbon becomes an ordinary block at this
 * width, which would make the featured card a five-item subgrid with four
 * tracks, and the fifth item is clamped on top of the fourth.
 *
 * So below this the card is the flex fallback, the ribbon is a band across the
 * top of the card, and only the button still lines up. With two cards side by
 * side and then one on its own there is much less to line up anyway.
 * ------------------------------------------------------------------------- */

@media (max-width: 1055px) {
	.vallem-pricing .vallem-pricing__card {
		display: flex;
		grid-row: auto;
	}

	.vallem-pricing .vallem-pricing__body {
		flex: 1 1 auto;
	}

	.vallem-pricing .vallem-pricing__foot {
		margin-block-start: auto;
	}

	.vallem-pricing .vallem-pricing__row {
		padding-block-start: 0;
	}

	/* Out of the card's padding on three sides and back again underneath. The
	   card's own padding is an inline style set on the block, so it cannot be
	   overridden from here; bleeding out of it with a negative margin is the
	   move that does not need to. The corner is the plate's 8px less the 4px
	   frame it sits inside. */
	.vallem-pricing .vallem-pricing__ribbon {
		position: static;
		margin-block: calc(-1 * var(--vallem-pricing-pad)) var(--vallem-pricing-pad);
		margin-inline: calc(-1 * var(--vallem-pricing-pad));
		block-size: auto;
		padding-block: 10px;
		border-start-start-radius: calc(var(--vallem-pricing-radius) - var(--vallem-pricing-frame));
		border-start-end-radius: calc(var(--vallem-pricing-radius) - var(--vallem-pricing-frame));
	}
}

/* ------------------------------------------------------------------------- *
 * Oproep met logobalk. See patterns/section-cta-logos.php
 *
 * A coloured plate at wide width with the copy pinned left and a picture the
 * plate clips out of its own bottom right corner, then a wrapped band of client
 * logos underneath. Ported from aikido.dev/attack/infinite, measured off their
 * stylesheet rather than off a screenshot.
 *
 * Everything worth retuning is a custom property on the section, so none of it
 * needs a selector rewritten:
 *
 *   --vallem-cta-logos-copy    how wide the copy column runs
 *   --vallem-cta-logos-media   how much of the plate width the picture takes
 *   --vallem-cta-logos-fill    how tall it stands in the plate
 *   --vallem-cta-logos-logo    the logo height in the band
 *   --vallem-cta-logos-dots    the pitch of the dot texture
 *   --vallem-cta-logos-pad     the padding written on the plate in the pattern
 * ------------------------------------------------------------------------- */

.vallem-cta-logos {
	--vallem-cta-logos-copy: 55ch;
	--vallem-cta-logos-media: 40%;
	--vallem-cta-logos-fill: 90%;
	--vallem-cta-logos-logo: 1.55rem;
	--vallem-cta-logos-dots: 15px;

	/* The plate's own padding, which the picture bleeds past below 768px. Out of
	   step with the pattern it only falls short and leaves a strip of plate; the
	   plate's overflow: hidden means it can never push the picture out. */
	--vallem-cta-logos-pad: var(--wp--preset--spacing--60);
}

/* The corner is 12px, not the 8px the ladder gives a card plate. That rule was
   written for cards a few hundred pixels wide; on a 1344 by 400 slab 8px reads
   as a mistake. 12px is the top of the ladder, already in use on the nav, and
   the nearest step to the source's 16px.

   The dot texture is --vallem-dot-field, declared once on :root and shared with
   the quote plate, which is where the reasoning now lives: mixed out of
   currentcolor so it recolours with the plate, and set as the element's own
   background-image so an image chosen in the Achtergrond-paneel replaces it
   outright. The dots are the default, not a floor. The pitch stays here,
   because it is the one number the two plates set differently. */
.vallem-cta-logos .vallem-cta-logos__card {
	position: relative;
	overflow: hidden;
	background-image: var(--vallem-dot-field);
	background-size: var(--vallem-cta-logos-dots) var(--vallem-cta-logos-dots);
}

/* The picture is behind the copy, so the column takes a stacking context of its
   own and nothing can land on top of the buttons.

   The width is a min() and not a breakpoint. The source clamps the column to a
   measure on desktop and to a percentage on tablet, and a straight port of that
   put the copy under the picture at exactly 1024px, where neither rule owns it.
   min() has no such seam: 55ch is the measure, 53% is the room left beside a
   picture that takes 40% of the plate, and whichever is smaller wins at every
   width. The real limit is about 61% at any plate size, so 53% has margin. */
.vallem-cta-logos .vallem-cta-logos__copy {
	position: relative;
	z-index: 2;
	max-width: min(var(--vallem-cta-logos-copy), 53%);
}

/* The source breaks its heading with a literal <br>. This does the same job
   without freezing a line ending that will be wrong for whatever sentence
   replaces the placeholder: balance evens the lines out, so a two sentence
   heading breaks near its own middle instead of orphaning a word. Written on
   the level rather than on a class, so it survives the heading being changed
   from an h2. */
.vallem-cta-logos .vallem-cta-logos__copy :is(h1, h2, h3, h4, h5, h6) {
	text-wrap: balance;
}

/* The stars are a mask of --vallem-stars-glyph, declared on :root and shared
   with the demo CTA's rating row, so there is one star shape on this site and
   not two. A mask takes currentcolor, which is what puts the star colour on
   this paragraph's own Kleur-paneel with nothing extra to expose.

   Drawn as an inline-block and not as a flex item, unlike the demo CTA's. The
   score here holds a <strong> around the 5, and a flex container would make the
   plain text and the strong two separate items with the gap falling between
   them, so "0,0/" and "5" would drift apart. */
.vallem-cta-logos .vallem-cta-logos__rating {
	--vallem-star-height: 1rem;

	font-variant-numeric: tabular-nums;
}

.vallem-cta-logos .vallem-cta-logos__rating::before {
	content: "";
	display: inline-block;
	vertical-align: -0.16em;
	margin-right: 0.5rem;
	height: var(--vallem-star-height);
	width: calc(var(--vallem-star-height) * 100 / 24);
	background-color: currentcolor;
	-webkit-mask: var(--vallem-stars-glyph) center / contain no-repeat;
	mask: var(--vallem-stars-glyph) center / contain no-repeat;
}

/* Quieter than the heading through opacity, not through a second colour slug. A
   fixed grey would pin dark text to a light plate and be wrong the moment the
   plate is recoloured. On the accent-surface plate the intro composites to
   #3A3E44 at 8.71:1 and the small line to #585F69 at 5.23:1, so both clear AA
   with room to spare. */
.vallem-cta-logos .vallem-cta-logos__intro {
	opacity: 0.82;
}

.vallem-cta-logos .vallem-cta-logos__note {
	opacity: 0.66;
}

/* The plate clips the picture, and that is the whole construction: no negative
   insets and nothing to measure, unlike hero-split's bleed. It sits 1px past
   the right edge so no hairline of plate survives subpixel rounding at the
   corner. object-position is at the left, so the crop eats the right of the
   picture rather than its middle.

   The source writes these as min-height and max-width. Here they are outright
   sizes, because the figure has no intrinsic size of its own to fall back on:
   the img inside it is 100% of the figure both ways. */
.vallem-cta-logos .vallem-cta-logos__media {
	position: absolute;
	inset: auto -1px 0 auto;
	z-index: 1;
	width: var(--vallem-cta-logos-media);
	height: var(--vallem-cta-logos-fill);
	margin: 0;
}

.vallem-cta-logos .vallem-cta-logos__media img {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
	object-position: 0% 50%;

	/* Left corners only. The right edge runs off the plate at every width and
	   the plate clips it, so a radius there would read as a nick in the crop.
	   6px is the ladder's value for a picture a card encloses. */
	border-radius: 6px 0 0 6px;
}

/* --- The logo band ------------------------------------------------------- *
 *
 * Selectors here carry the section class on top of the block's own, because
 * core sizes a gallery item with
 *
 *   .wp-block-gallery.has-nested-images.columns-8
 *     figure.wp-block-image:not(#individual-image)
 *
 * and that :not() exists to buy id-level specificity. A two-class override
 * loses. Read off wp-includes/blocks/gallery/style.css in 7.0.4, not assumed.
 *
 * Core owns the item width from 1024px up, which is deliberate: the Kolommen
 * slider in the sidebar keeps working where there is room for it. Below 1024px
 * the row is clamped by hand, because eight logos across a tablet is 75px each.
 * ------------------------------------------------------------------------- */

/* The gap above the band is a margin and not the section's blockGap. What used
   to sit here was `margin: 0`, at (0,2,0), which silently beat the blockGap core
   writes at (0,1,0): the band sat hard against the plate and nothing in the
   editor could move it. preset 70 is clamp(4rem, 8vw, 6rem), against the
   source's 5rem either side of its own logo strip. */
.vallem-cta-logos .vallem-cta-logos__album {
	justify-content: center;
	margin: var(--wp--preset--spacing--70) 0 0;
}

/* The band ships empty, so until someone fills it the figure is a zero-height
   box carrying that margin, which reads as a hole under the plate. The gap
   arrives with the logos. */
.vallem-cta-logos .vallem-cta-logos__album:empty {
	margin-block-start: 0;
}

/* flex-grow is core's, and with a part-filled row it stretches the logos to
   span the full width instead of leaving the row centred. The source centres,
   so it goes. Spacing between logos is padding on each item rather than a gap,
   which is also how the source does it and is why blockGap is 0 in the pattern:
   core sizes an item at calc(12.5% - gap * 0.875), and eight only fit at 0. */
.vallem-cta-logos .vallem-cta-logos__album.wp-block-gallery.has-nested-images figure.wp-block-image:not(#individual-image) {
	flex-grow: 0;
	align-items: center;
	justify-content: center;
	padding: 1.5rem 1rem;
	margin: 0;
}

/* One ink for the whole band, the treatment the logo row already uses, so a set
   of mixed-brand files reads as one material and no second set is needed for a
   dark scheme. Knockouts have to be transparent holes: a white shape flattens
   to black here and the mark becomes a solid block.

   A height with core's max-width: 100% squashes a wide wordmark rather than
   scaling it, which is the trap the logo row documents; object-fit is what
   stops that. No optical normalisation here, unlike assets/js/logo-row.js: that
   script also owns a hover caption this band does not have, and a static row
   under a CTA does not earn a script. */
.vallem-cta-logos .vallem-cta-logos__album.wp-block-gallery.has-nested-images figure.wp-block-image img {
	width: auto;
	height: var(--vallem-cta-logos-logo);
	object-fit: contain;
	filter: grayscale(1) brightness(0);
	opacity: 0.6;
}

/* Five across. The source's tablet step, and the point where eight stops being
   a row and starts being a strip. */
@media (max-width: 1023px) {
	.vallem-cta-logos .vallem-cta-logos__album.wp-block-gallery.has-nested-images figure.wp-block-image:not(#individual-image) {
		width: 20%;
		padding: 1rem;
	}
}

@media (max-width: 767px) {
	.vallem-cta-logos .vallem-cta-logos__copy {
		max-width: none;
	}

	/* In flow under the copy, still running off the plate's right edge, so the
	   crop reads the same as it does on desktop.

	   Nothing negative on the block axis any more. The margin that was here
	   cancelled the plate's bottom padding exactly, which left the picture
	   sitting on the plate's own edge with square corners against a 12px radius,
	   and it was a hardcoded copy of a value the Afmetingen-paneel owns: turn
	   that padding up and the picture got cropped. The bottom padding is the
	   plate's to keep.

	   Not hidden below 480px the way the source hides it: an element that
	   vanishes at a width is a surprise in the editor. */
	.vallem-cta-logos .vallem-cta-logos__media {
		position: static;
		width: auto;
		height: auto;

		/* The extra pixel buys what desktop's right: -1px buys: no hairline of
		   plate survives subpixel rounding at the crop. */
		margin: var(--wp--preset--spacing--50) calc(-1 * var(--vallem-cta-logos-pad) - 1px) 0 0;
	}

	.vallem-cta-logos .vallem-cta-logos__media img {
		height: auto;
		object-fit: contain;
		object-position: center;
	}

	/* Four across. */
	.vallem-cta-logos .vallem-cta-logos__album.wp-block-gallery.has-nested-images figure.wp-block-image:not(#individual-image) {
		width: 25%;
	}
}

/* Two across, which is where core's own untouched default already lands. Spelled
   out anyway, because the rule above it is not core's and would otherwise carry
   four logos down to a phone. */
@media (max-width: 479px) {
	.vallem-cta-logos .vallem-cta-logos__album.wp-block-gallery.has-nested-images figure.wp-block-image:not(#individual-image) {
		width: 50%;
	}
}

/* ------------------------------------------------------------------------- *
 * Tekst naast beeldvlak. See patterns/section-split-panel.php and
 * patterns/section-split-panel-flipped.php
 *
 * Two equal columns on a plain section ground: copy on one side, a picture
 * filling the other edge to edge. Ported from aikido.dev's
 * `.split-grid.content-block`, measured off their stylesheet.
 *
 * The source insets its screenshot 2rem inside a tinted well and lets it bleed
 * out of the fourth side. Nol on 2026-09-08: the picture fills the whole side,
 * no frame. Both went, and the well is now shape and clip only. What is left
 * are two custom properties on the section, so neither needs a selector
 * rewritten:
 *
 *   --vallem-split-panel-gap     the gutter between the two columns
 *   --vallem-split-panel-ratio   the shape of the well, which is what holds the
 *                                section together while a picture is being
 *                                swapped, and what the picture is cropped to
 * ------------------------------------------------------------------------- */

.vallem-split-panel {
	/* 4rem, which is the source's gutter exactly: preset 60 tops out there.
	   Not a new number. */
	--vallem-split-panel-gap: var(--wp--preset--spacing--60);
	--vallem-split-panel-ratio: 5 / 4;
}

/* The row is a plain two-track grid and not core's grid layout. Core's writes
   `repeat(auto-fill, minmax(N, 1fr))`, which is three tracks at 1344px unless
   the minimum is tuned to the viewport, and this row is two halves at every
   width above the stacking point. It is the same construction card-media-split
   uses, for the same reason. */
.vallem-split-panel .vallem-split-panel__row {
	display: grid;
	grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
	gap: var(--vallem-split-panel-gap);
	align-items: center;
}

/* Three classes, and measured rather than defensive. Core's flow layout puts a
   block gap on every child of a group and zeroes it on the last one with
   `:root :where(.is-layout-flow) > :last-child`. The `:last-child` sits outside
   the `:where()`, so that is (0,2,0) and a two-class selector only ties it. On
   a grid those margins are real space inside the row. Same note as
   .vallem-media-card. */
.wp-block-group.vallem-split-panel__row > .vallem-split-panel__copy,
.wp-block-group.vallem-split-panel__row > .vallem-split-panel__media {
	margin-block: 0;
}

/* ---- Left or right ------------------------------------------------------ *
 *
 * The whole of the flip: two grid areas swapped, and which side of the well the
 * picture leaves by. Nothing else. That is why it is one click in the Stijlen
 * tab rather than a second stylesheet, and why the mirrored pattern is a
 * require of the first rather than a copy.
 *
 * Beeld links and Beeld rechts are registered for core/group in
 * styles/blocks/media-left.json and media-right.json. They were written for
 * card-media-split and they carry an 8px radius, because a variation with no
 * styles at all fails to register. That radius lands on this row, which has no
 * background and clips nothing, so it draws nothing.
 * ------------------------------------------------------------------------- */

.vallem-split-panel .is-style-media-right > .vallem-split-panel__copy,
.vallem-split-panel .is-style-media-left > .vallem-split-panel__media {
	grid-area: 1 / 1 / 2 / 2;
}

.vallem-split-panel .is-style-media-right > .vallem-split-panel__media,
.vallem-split-panel .is-style-media-left > .vallem-split-panel__copy {
	grid-area: 1 / 2 / 2 / 3;
}

/* The flip is those two rules and nothing else. There used to be four more
   here, dropping the well's padding on one side and pulling the picture 10%
   past that edge. Both are gone: with the picture edge to edge on all four
   sides there is no closed edge for an overhang to read against. */

/* ---- The well ----------------------------------------------------------- *
 *
 * A shape and a clip, and nothing else. No padding, no absolute positioning,
 * nothing to measure. Unlike hero-split's bleed, which has to escape a plate it
 * is not inside.
 *
 * The ratio is on the well and not on the picture. A picture with an aspect
 * ratio in its block markup is three attributes that have to match what save()
 * writes or the block goes invalid, and it would put the section's shape in the
 * hands of whatever screenshot is dropped in next. Here the well keeps its
 * shape and the picture is fitted to it, so a 3:2 screenshot and a 16:9 one
 * land on exactly the same edges.
 *
 * 5/4 and not the source's square. On the 1344px content line a column is
 * 640px, so a square well stands 640px tall against roughly 220px of copy, and
 * the section reads as a picture with a caption beside it. 5/4 puts it at
 * 512px, which is still twice the copy and still the dominant half. Measured on
 * a rendered page at 1440px: 639 by 511.
 *
 * grid-auto-rows is what makes the picture fill. Every row here is implicit,
 * so sizing them all `minmax(0, 1fr)` gives the one figure the well's whole
 * height and it stretches into it. Without it the row is sized to the picture,
 * which is what left air above and below before.
 *
 * auto-rows rather than a single explicit row, because this container stays
 * editable and someone will eventually drop a second block in it. Two children
 * then take half the well each and both still fill; one explicit row would put
 * the second child in an auto row past the bottom edge, where overflow: hidden
 * would eat it.
 * ------------------------------------------------------------------------- */

.vallem-split-panel .vallem-split-panel__media {
	position: relative;
	display: grid;
	grid-template-columns: minmax(0, 1fr);
	grid-auto-rows: minmax(0, 1fr);
	overflow: hidden;
	aspect-ratio: var(--vallem-split-panel-ratio);
}

.vallem-split-panel .vallem-split-panel__shot {
	margin-block: 0;
	height: 100%;
}

/* cover rather than contain, and the reason is that this is the only pair that
   fills without distorting: contain would put the air back, and a bare
   width/height 100% would stretch a screenshot to 5/4. The crop is even,
   because object-position is centre. A picture wider or taller than 5/4 loses
   the same amount off both opposite edges rather than all of it off one.

   width and height are both pinned so the figure's own box, not the picture's
   intrinsic ratio, decides the frame. That is what makes the well authoritative
   about the section's shape. */
.vallem-split-panel .vallem-split-panel__shot img {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
	object-position: center;
}

/* ---- The copy ----------------------------------------------------------- */

/* Written on the levels rather than on a class, so it survives the heading
   being changed from an h2. Same rule as the CTA plate's. */
.vallem-split-panel .vallem-split-panel__copy :is(h1, h2, h3, h4, h5, h6) {
	text-wrap: balance;
}

/* Quieter than the heading through opacity, not through a second colour slug. A
   fixed grey would pin dark text to a light ground and be wrong the moment the
   section is recoloured. 0.78 of #18181B on white is 12.6:1. */
.vallem-split-panel .vallem-split-panel__intro {
	opacity: 0.78;
}

/* ---- Stacked ------------------------------------------------------------ *
 *
 * 940px rather than the 781px the rest of the theme stacks at. Two 36px
 * headings and a 4rem gutter run out of room well before a phone: at 781px a
 * column is 350px, which is four words a line. 940 is already a breakpoint in
 * this stylesheet, on the case spotlight's header.
 *
 * The picture goes on top whichever way the row is aligned, which is Aikido's
 * mobile-flip and needs no class of its own here: the copy is first in the
 * source, so both alignments are one pair of grid areas.
 *
 * Nothing else changes below the breakpoint. The picture already fills its
 * well at every width, the well keeps its 5/4 shape, and the section is not
 * full bleed, so nothing overflows the page.
 * ------------------------------------------------------------------------- */

@media (width <= 940px) {
	.vallem-split-panel {
		--vallem-split-panel-gap: var(--wp--preset--spacing--50);
	}

	.vallem-split-panel .vallem-split-panel__row {
		grid-template-columns: minmax(0, 1fr);
	}

	.vallem-split-panel .is-style-media-right > .vallem-split-panel__media,
	.vallem-split-panel .is-style-media-left > .vallem-split-panel__media {
		grid-area: 1 / 1 / 2 / 2;
	}

	.vallem-split-panel .is-style-media-right > .vallem-split-panel__copy,
	.vallem-split-panel .is-style-media-left > .vallem-split-panel__copy {
		grid-area: 2 / 1 / 3 / 2;
	}
}

/* ------------------------------------------------------------------------- *
 * Beeld naast stappen. See patterns/section-steps-media.php
 *
 * A near-black band with a 3:2 picture in the wide column and a connected step
 * list in the narrow one. Ported from aikido.dev's `.infinite_boxes_wrap.dark`,
 * measured off their stylesheet: `1fr .6fr` at a 3vw gutter, a 1rem dot, a
 * 1.5px line, 2rem of text indent and 2.5rem under a step.
 *
 * The dot ramp is the character of the section, so it is three custom
 * properties rather than three colours buried in rules:
 *
 *   --vallem-steps-media-ramp-start  the first dot, lightest
 *   --vallem-steps-media-ramp-mid    every dot between the two ends
 *   --vallem-steps-media-ramp-end    the last dot, most saturated
 *
 * and the rest of the geometry is knobs on the same element:
 *
 *   --vallem-steps-media-gap    the gutter between picture and copy
 *   --vallem-steps-media-copy   the narrow column's share of the row
 *   --vallem-steps-media-ratio  the crop the picture is fitted to
 *   --vallem-steps-media-dot    diameter of a dot
 *   --vallem-steps-media-line   thickness of the connecting line, 1.5px and
 *                               not 1px, which is the source
 *   --vallem-steps-media-air    the dot's drop from the top of its step, and
 *                               the same air again above and below the line
 *   --vallem-steps-media-step   the space under a step
 * ------------------------------------------------------------------------- */

.vallem-steps-media {
	/* 4rem against the source's 3vw, which is 43px at 1440. Preset 60 tops out
	   at 4rem and is the gutter the other split sections use, so it is not a
	   new number. */
	--vallem-steps-media-gap: var(--wp--preset--spacing--60);
	--vallem-steps-media-copy: 0.6fr;
	--vallem-steps-media-ratio: 3 / 2;

	--vallem-steps-media-dot: 1rem;
	--vallem-steps-media-line: 1.5px;
	--vallem-steps-media-air: 0.5rem;
	--vallem-steps-media-step: 2.5rem;

	/* Light to saturated. violet-surface 11.40:1 on this plate, accent 6.76:1,
	   primary 2.30:1. The last one is the one to watch if the ramp is ever
	   retuned: a dot is decoration and the step is named by its title, but
	   below roughly 2:1 it stops reading as the end of the line. */
	--vallem-steps-media-ramp-start: var(--wp--preset--color--violet-surface);
	--vallem-steps-media-ramp-mid: var(--wp--preset--color--accent);
	--vallem-steps-media-ramp-end: var(--wp--preset--color--primary);
}

/* Two unequal tracks, which core's grid layout cannot write: it emits
   `repeat(auto-fill, minmax(N, 1fr))` and every track comes out the same. Same
   construction as .vallem-split-panel__row and .vallem-media-card. */
.vallem-steps-media .vallem-steps-media__row {
	display: grid;
	grid-template-columns: minmax(0, 1fr) minmax(0, var(--vallem-steps-media-copy));
	gap: var(--vallem-steps-media-gap);
	align-items: center;
}

/* Three classes, and measured rather than defensive. Core's flow layout hangs a
   block gap on every child of a group, and on a grid those margins are real
   space inside the row. `:last-child` sits outside core's `:where()`, so its
   rule is (0,2,0) and a two-class selector only ties it. */
.wp-block-group.vallem-steps-media__row > .vallem-steps-media__shot,
.wp-block-group.vallem-steps-media__row > .vallem-steps-media__copy {
	margin-block: 0;
}

/* ---- Left or right ------------------------------------------------------ *
 *
 * The whole of the flip: the track order and the two grid areas, and nothing
 * else. That is why it is one click in the Stijlen tab rather than a second
 * stylesheet, and why patterns/section-steps-media-flipped.php is a require of
 * the first pattern rather than a copy of it.
 *
 * Three rules and not two, because the tracks here are unequal. Swapping the grid
 * areas alone moves each element into the OTHER track, so the picture came out
 * in the .6fr column and the step list in the 1fr one. Nol caught it on a built
 * page, 2026-09-08. The track order has to be mirrored with the areas, so the
 * picture keeps 1fr and the list keeps .6fr on whichever side each lands.
 *
 * That is what makes this different from .vallem-split-panel, where the tracks
 * are 1fr 1fr and the area swap is the whole flip.
 *
 * The picture is first in the markup both ways round, which is also the reading
 * order that should hold: the numbered list is the content and the picture is
 * decoration beside it.
 *
 * Beeld links and Beeld rechts are registered for core/group in
 * styles/blocks/media-left.json and media-right.json. They were written for
 * card-media-split and they carry an 8px radius, because a variation with no
 * styles at all fails to register. That radius lands on this row, which has no
 * background and clips nothing, so it draws nothing. Same note as
 * .vallem-split-panel.
 * ------------------------------------------------------------------------- */

/* The narrow track moves to the front, so column 2 is the wide one. (0,3,0)
   against the row rule's (0,2,0), so it wins without !important. */
.vallem-steps-media .vallem-steps-media__row.is-style-media-right {
	grid-template-columns: minmax(0, var(--vallem-steps-media-copy)) minmax(0, 1fr);
}

.vallem-steps-media .is-style-media-left > .vallem-steps-media__shot,
.vallem-steps-media .is-style-media-right > .vallem-steps-media__copy {
	grid-area: 1 / 1 / 2 / 2;
}

.vallem-steps-media .is-style-media-left > .vallem-steps-media__copy,
.vallem-steps-media .is-style-media-right > .vallem-steps-media__shot {
	grid-area: 1 / 2 / 2 / 3;
}

/* ---- The picture -------------------------------------------------------- *
 *
 * The crop is here and not in the block markup. width, height and aspectRatio
 * are three attributes that have to match what save() writes or the block goes
 * invalid, so a ratio set in the pattern would be a trap; set one in the editor
 * and it arrives as an inline style, which still wins over this.
 *
 * width rather than max-width, so a picture narrower than the column still
 * spans it instead of floating in the middle of the plate.
 * ------------------------------------------------------------------------- */

.vallem-steps-media .vallem-steps-media__shot img {
	display: block;
	width: 100%;
	height: auto;
	aspect-ratio: var(--vallem-steps-media-ratio);
	object-fit: cover;
}

/* ---- The copy ----------------------------------------------------------- */

.vallem-steps-media .vallem-steps-media__title {
	text-wrap: balance;
}

/* Quieter than the heading through opacity, not through a second colour slug.
   A fixed grey would pin light text to a dark ground and be wrong the moment
   the plate is recoloured, which is the note written out in hero-split. 0.78 of
   #FFFFFF on #18181B is 11.04:1. */
.vallem-steps-media .vallem-steps-media__step-body {
	opacity: 0.78;
}

/* ---- A step ------------------------------------------------------------- *
 *
 * The dot and the line are pseudo-elements on the step itself, so there is no
 * markup for an editor to delete by accident and a duplicated step brings its
 * own. The line runs from under the dot to the bottom of the step's padding
 * box, and the next step's dot sits --air below that, so the same number opens
 * and closes the run.
 *
 * The indent is here rather than on the Afmetingen-paneel because it has to
 * agree with the dot's diameter, and a padding set in the editor serialises
 * inline where nothing can correct it.
 * ------------------------------------------------------------------------- */

.vallem-steps-media .vallem-steps-media__step {
	--vallem-steps-media-tone: var(--vallem-steps-media-ramp-mid);
	--vallem-steps-media-tone-next: var(--vallem-steps-media-ramp-mid);

	position: relative;
	padding-block-end: var(--vallem-steps-media-step);
	padding-inline-start: calc(var(--vallem-steps-media-dot) + var(--wp--preset--spacing--20));
}

.vallem-steps-media .vallem-steps-media__step::before {
	content: "";
	position: absolute;
	inset-block-start: var(--vallem-steps-media-air);
	inset-inline-start: 0;
	width: var(--vallem-steps-media-dot);
	height: var(--vallem-steps-media-dot);
	border-radius: 50%;
	background-color: var(--vallem-steps-media-tone);
}

.vallem-steps-media .vallem-steps-media__step::after {
	content: "";
	position: absolute;
	inset-block-start: calc(var(--vallem-steps-media-dot) + 2 * var(--vallem-steps-media-air));
	inset-block-end: 0;
	inset-inline-start: calc((var(--vallem-steps-media-dot) - var(--vallem-steps-media-line)) / 2);
	width: var(--vallem-steps-media-line);
	background-image: linear-gradient(var(--vallem-steps-media-tone), var(--vallem-steps-media-tone-next));
}

/* ---- The ramp, at any number of steps ----------------------------------- *
 *
 * Both ends of the ramp are pinned off the list itself and the middle holds the
 * mid tone, so two steps read start to end, three read exactly like the source,
 * and five read as a ramp that plateaus. Nothing counts to three.
 *
 * `:not(:has(~ .step))` and not `:last-child`, because the editor appends its
 * own block appender after the last block and `:last-child` would then be a
 * div nobody wrote. `:first-child` is safe: nothing is inserted in front.
 * ------------------------------------------------------------------------- */

.vallem-steps-media .vallem-steps-media__step:first-child {
	--vallem-steps-media-tone: var(--vallem-steps-media-ramp-start);
}

/* The step with one step after it and none after that: its line runs into the
   saturated end. Two `:has()` side by side, neither inside the other, which is
   the nesting the spec forbids. */
.vallem-steps-media .vallem-steps-media__step:has(+ .vallem-steps-media__step):not(:has(+ .vallem-steps-media__step + .vallem-steps-media__step)) {
	--vallem-steps-media-tone-next: var(--vallem-steps-media-ramp-end);
}

/* The last step keeps its dot, loses its line, and closes the list. */
.vallem-steps-media .vallem-steps-media__step:not(:has(~ .vallem-steps-media__step)) {
	--vallem-steps-media-tone: var(--vallem-steps-media-ramp-end);

	padding-block-end: 0;
}

.vallem-steps-media .vallem-steps-media__step:not(:has(~ .vallem-steps-media__step))::after {
	content: none;
}

/* ---- Stacked ------------------------------------------------------------ *
 *
 * 940px, which is already a breakpoint in this stylesheet and the one
 * .vallem-split-panel stacks at. The source stacks at 991. Below it the picture
 * sits on top of the copy with no second rule, because the picture is first in
 * the markup either way.
 * ------------------------------------------------------------------------- */

@media (width <= 940px) {
	.vallem-steps-media {
		--vallem-steps-media-gap: var(--wp--preset--spacing--50);
	}

	.vallem-steps-media .vallem-steps-media__row,
	.vallem-steps-media .vallem-steps-media__row.is-style-media-right {
		grid-template-columns: minmax(0, 1fr);
	}

	/* Both alignments collapse to the same two rows, picture on top. Not
	   optional: the grid areas above name column 2, and on a one-track grid
	   that is an implicit column nobody declared, so half the row would hang
	   off the plate. */
	.vallem-steps-media .is-style-media-left > .vallem-steps-media__shot,
	.vallem-steps-media .is-style-media-right > .vallem-steps-media__shot {
		grid-area: 1 / 1 / 2 / 2;
	}

	.vallem-steps-media .is-style-media-left > .vallem-steps-media__copy,
	.vallem-steps-media .is-style-media-right > .vallem-steps-media__copy {
		grid-area: 2 / 1 / 3 / 2;
	}
}

/* ------------------------------------------------------------------------- *
 * Citaat op een vlak. See patterns/section-quote-card.php
 *
 * A dark plate on the content line carrying one logo, one quote and one line of
 * attribution, centred both ways, with a light section either side of it.
 * Ported from the quote section on aikido.dev/attack/ai-pentesting, measured
 * off their stylesheet: a 33.5rem plate, a 58.5rem column, a 1.5rem gap and a
 * 9rem logo.
 *
 * Everything worth retuning is a custom property on the section, so none of it
 * needs a selector rewritten:
 *
 *   --vallem-quote-card-height   how tall the plate stands
 *   --vallem-quote-card-measure  the centred column's cap
 *   --vallem-quote-card-logo     how wide the logo runs
 *   --vallem-quote-card-dots     the pitch of the dot field
 *   --vallem-quote-card-knock    the logo knockout
 * ------------------------------------------------------------------------- */

.vallem-quote-card {
	--vallem-quote-card-height: 33.5rem;
	--vallem-quote-card-measure: 58.5rem;
	--vallem-quote-card-logo: 9rem;
	--vallem-quote-card-dots: 15px;

	/* The knockout the dark logo row and the case rail already use.
	   grayscale(1) brightness(0) flattens any mark to a silhouette and the
	   invert is what turns that silhouette white, so one uploaded asset serves
	   a light section and a dark plate. It is a property and not a literal
	   because the plate is a colour control: recolour it to something light and
	   this goes to invert(0), which leaves the silhouette black. */
	--vallem-quote-card-knock: invert(1);
}

/* The plate centres its column vertically rather than being sized by it, which
   is what the source's min-height buys: a short quote and a long one leave the
   section the same height, so a page does not jump when the copy is replaced.

   The corner is 12px and not the 8px the ladder gives a card plate. That rule
   was written for cards a few hundred pixels wide; on a 1344 by 536 slab 8px
   reads as a mistake. Same call and the same reasoning as the CTA plate, which
   is the same object, and the nearest step to the source's 16px.

   The dot field is --vallem-dot-field, declared on :root and shared with that
   plate. See the note there for why it is the element's own background-image
   and never a ::before, and what that buys in the Achtergrond-paneel. */
.vallem-quote-card .vallem-quote-card__plate {
	display: flex;
	flex-direction: column;
	justify-content: center;
	min-height: var(--vallem-quote-card-height);
	background-image: var(--vallem-dot-field);
	background-size: var(--vallem-quote-card-dots) var(--vallem-quote-card-dots);
}

/* The plate's only flex child, capped and centred here rather than by a
   constrained layout on the plate itself. Core's constrained layout puts
   `margin-inline: auto` on every child, and an auto cross-axis margin beats
   `align-self: stretch`, so each block would have shrunk to its own content and
   the measure would have moved with the length of the sentence. */
.vallem-quote-card .vallem-quote-card__copy {
	width: 100%;
	max-width: var(--vallem-quote-card-measure);
	margin-inline: auto;
}

.vallem-quote-card .vallem-quote-card__logo img {
	width: var(--vallem-quote-card-logo);
	height: auto;
	object-fit: contain;
	filter: grayscale(1) brightness(0) var(--vallem-quote-card-knock);
}

/* The user agent gives a blockquote `1em 40px`. Only the inline half is reset
   here: the gap above the quote is core's own `margin-block-start` on this
   element at one class, and a `margin: 0` at two classes would eat it. Same
   note as .vallem-case-rail__story. */
.vallem-quote-card .vallem-quote-card__quote {
	margin-inline: 0;
}

/* balance, not the source's fixed three lines. A quote is replaced by a longer
   or a shorter one and a hard line count is a promise this file cannot keep;
   balance evens whatever arrives instead of orphaning its last word. */
.vallem-quote-card .vallem-quote-card__quote p {
	text-wrap: balance;
}

/* Quieter than the name through opacity, not through a second colour slug. A
   fixed grey pins light-on-dark and is wrong the moment the plate is
   recoloured, which is the note written out at length in hero-split. The
   source's own `#fff9` is exactly that mistake. 0.7 of #FFFFFF on the contrast
   plate composites to #BABABB, 9.14:1, so it survives a long way. */
.vallem-quote-card .vallem-quote-card__role {
	opacity: 0.7;
}

/* ---- Narrow ------------------------------------------------------------- *
 *
 * 781px, this stylesheet's breakpoint. The plate gives up its standing height:
 * 536px is most of a phone screen, and holding it there for three short blocks
 * is a scroll the reader pays for nothing. The logo comes down with it, because
 * 144px against a 28px quote is a logo that outranks the words.
 * ------------------------------------------------------------------------- */

@media (width <= 781px) {
	.vallem-quote-card {
		--vallem-quote-card-height: 24rem;
		--vallem-quote-card-logo: 7rem;
	}
}

/* ------------------------------------------------------------------------- *
 * Blog, overzicht. See templates/home.html, archive.html and index.html
 *
 * The shape is aikido.dev/blog: one wide card at the top carrying the newest
 * piece, then a two-up grid under it, each card a cover with the date, the
 * category and the title beneath. What is not taken from it is the search box
 * and the Featured filter, which Nol cut on 2026-09-08, and the all-dark
 * ground: this site's archives are a navy hero over a white list, which is what
 * the klantverhalen archive already does two sections up.
 *
 * The card is `.vallem-case-more`'s and not `.vallem-cases`'s. The klantverhalen
 * grid puts the type on the photograph behind a scrim, which works because a
 * klantverhaal cover is a photograph of a building. A blog cover is a diagram or
 * a screenshot as often as not, and type over a screenshot is unreadable. So the
 * photo is a plate at the top of the card and the words sit under it on white.
 *
 * **The featured card is `:first-child`, not a second query.** The obvious build
 * is a perPage-1 query for the top card and an offset-1 query for the grid, and
 * it is wrong: `build_query_vars_from_query_block()` offsets correctly per page
 * (`( $per_page * ( $page - 1 ) ) + $offset`, wp-includes/blocks.php) but
 * `WP_Query::set_found_posts()` takes its total from `FOUND_ROWS()`, which
 * ignores the LIMIT and therefore ignores the offset. So max_num_pages counts a
 * post the grid never shows and the last page comes out empty. One inherited
 * query and a first-child rule has no second count to disagree with, and
 * pagination is core's own.
 *
 * Which means the wide treatment lands on the first card of page two as well.
 * That is deliberate and it is why there is no "Uitgelicht" label on it: it is a
 * rhythm, one big card then pairs, not a claim about which post is best. A label
 * would be wrong on page two and there is no honest way for CSS to know.
 * ------------------------------------------------------------------------- */

.vallem-blog-hero {
	padding-block: var(--wp--preset--spacing--70);
}

/* Balanced, so the break lands between the two halves of the sentence rather
   than wherever 760px happens to fall. Same call as the klantverhalen hero. */
.vallem-blog-hero h1 {
	text-wrap: balance;
}

/* The term archive's h1 is core/query-title, which brings its own margins. */
.vallem-blog-hero .wp-block-query-title {
	margin-block: 0;
	text-wrap: balance;
}

.vallem-blog-hero .vallem-blog-hero__back a {
	color: inherit;
	text-decoration: none;
}

.vallem-blog-hero .vallem-blog-hero__back a:hover {
	text-decoration: underline;
}

.vallem-blog {
	padding-block: var(--wp--preset--spacing--70);
}

/* ---- The grid ----------------------------------------------------------- *
 *
 * Two columns, not the klantverhalen archive's five. That grid exists to
 * reproduce a 2/3 rhythm from its reference; here the only span that varies is
 * the first card, so two columns and one `:first-child` rule says the whole
 * thing. core/post-template renders a <ul> with an <li> per post and its flow
 * layout puts a block gap between them, which a grid does not want.
 * ------------------------------------------------------------------------- */

.vallem-blog .vallem-blog__list {
	display: grid;
	grid-template-columns: repeat(2, minmax(0, 1fr));
	gap: clamp(1rem, 2vw, 1.5rem);
	margin: 0;
	padding: 0;
	list-style: none;
}

.vallem-blog .vallem-blog__list > li {
	display: grid;
	margin-block: 0;
}

.vallem-blog .vallem-blog__list > li:first-child {
	grid-column: 1 / -1;
}

/* ---- The card ----------------------------------------------------------- */

.vallem-blog .vallem-blog-card {
	position: relative;
	display: flex;
	flex-direction: column;
	height: 100%;
	overflow: hidden;

	/* Mixed out of currentcolor rather than the `border` palette slug. Setting a
	   border colour through the block would put `has-border-color` on the card,
	   which theme.json also emits as a text-colour rule, and every word in the
	   card would repaint to #D4D4D8. See the note at the top of this file. */
	border: 1px solid color-mix(in srgb, currentcolor 12%, transparent);
	border-radius: 6px;
	background-color: var(--wp--preset--color--base);
}

/* 6px and not 8px: the picture is enclosed by a card rather than sitting on the
   section, which is the split the rest of the theme uses. The corner itself
   comes from the card's overflow, so the figure needs none of its own. */
.vallem-blog .vallem-blog-card__photo {
	margin: 0;
	aspect-ratio: 16 / 9;
	background-color: color-mix(in srgb, currentcolor 6%, transparent);
}

.vallem-blog .vallem-blog-card__photo img {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
}

.vallem-blog .vallem-blog-card__body {
	display: flex;
	flex: 1;
	flex-direction: column;
	align-items: flex-start;
	box-sizing: border-box;
	padding: clamp(1.25rem, 2.2vw, 1.75rem);
}

/* ---- The featured card -------------------------------------------------- *
 *
 * Photograph right, words left, which is the reference's arrangement and the
 * opposite of the cards under it. The markup order is photo then body for every
 * card, so the two are placed into the same grid row explicitly rather than
 * reordered: `order` would move it visually and leave the tab order behind it.
 * ------------------------------------------------------------------------- */

.vallem-blog .vallem-blog__list > li:first-child .vallem-blog-card {
	display: grid;
	grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
	align-items: stretch;
}

.vallem-blog .vallem-blog__list > li:first-child .vallem-blog-card__photo {
	grid-column: 2;
	grid-row: 1;
	aspect-ratio: auto;
	min-height: clamp(280px, 26vw, 400px);
}

.vallem-blog .vallem-blog__list > li:first-child .vallem-blog-card__body {
	grid-column: 1;
	grid-row: 1;
	justify-content: center;
	padding: clamp(1.75rem, 3.2vw, 3rem);
}

/* No measure cap here. 26ch is right for a card half this wide; in the featured
   card's own column it breaks a seven word headline over three lines with 100px
   of unused column beside it. The column is the measure. */
.vallem-blog .vallem-blog__list > li:first-child .vallem-blog-card__title {
	max-width: none;
	font-size: var(--wp--preset--font-size--display-small);
}

/* The stack is centred in the card, so the link sits under the excerpt rather
   than being pushed to the floor. `margin-block-start: auto` is what the grid
   cards use to line their links up along the bottom of a row; here there is no
   row to line up with and it only opens a hole under the excerpt. */
.vallem-blog .vallem-blog__list > li:first-child .vallem-blog-card__more {
	margin-block-start: 0;
}

/* ---- The meta line ------------------------------------------------------ *
 *
 * Date, category, leestijd, separated by a disc rather than by a bullet
 * character: a middot in the markup would be a word the screen reader has to
 * say between every pair, and it would need a block of its own between blocks
 * that can each be absent. A generated disc on the second child onwards is
 * silent, and disappears with whatever it was separating.
 *
 * core/post-terms renders nothing at all when a post has no category, so it
 * leaves no empty box behind. The leestijd paragraph can: its binding returns
 * null on a post with no words in it and core then draws the block's own empty
 * fallback, so that one is guarded by :empty.
 * ------------------------------------------------------------------------- */

.vallem-blog .vallem-blog-card__meta {
	align-items: baseline;
	row-gap: 4px;
	color: var(--wp--preset--color--contrast-muted);
	line-height: 1.4;
}

.vallem-blog .vallem-blog-card__meta > * {
	margin: 0;
}

.vallem-blog .vallem-blog-card__meta > *:empty {
	display: none;
}

.vallem-blog .vallem-blog-card__meta > * + *::before {
	content: "";
	display: inline-block;
	width: 3px;
	height: 3px;
	margin-inline: 0.6em;
	border-radius: 50%;
	background-color: currentcolor;
	opacity: 0.55;
	vertical-align: middle;
}

/* Above the stretched title link, so the category is its own target. The one
   exception to one-card-one-link: it goes somewhere else, and it says so. */
.vallem-blog .vallem-blog-card__cat a {
	position: relative;
	z-index: 4;
	color: inherit;
	text-decoration: none;
}

.vallem-blog .vallem-blog-card__cat a:hover {
	color: var(--wp--preset--color--primary);
	text-decoration: underline;
}

/* ---- Headline, excerpt, link -------------------------------------------- */

.vallem-blog .vallem-blog-card__title {
	margin: 0;
	max-width: 26ch;
	text-wrap: balance;
}

.vallem-blog .vallem-blog-card__title a {
	color: inherit;
	text-decoration: none;
}

/* The theme's card idiom: the headline's link is stretched over the whole card,
   so the card is one target carrying a descriptive name, and the "Lees verder"
   under it stays a paragraph rather than a second link to the same page. */
.vallem-blog .vallem-blog-card__title a::after {
	content: "";
	position: absolute;
	inset: 0;
	z-index: 3;
}

.vallem-blog .vallem-blog-card:focus-within {
	outline: 2px solid var(--wp--preset--color--primary);
	outline-offset: 3px;
}

.vallem-blog .vallem-blog-card__title a:focus-visible {
	outline: none;
}

.vallem-blog .vallem-blog-card__excerpt {
	margin: 0;
	max-width: 62ch;
	color: var(--wp--preset--color--contrast-muted);
	line-height: 1.6;
}

.vallem-blog .vallem-blog-card__excerpt p {
	margin: 0;
}

/* The arrow link, matching is-style-link-arrow, built here because this is a
   paragraph and not a button: the card's real link is the headline. */
.vallem-blog .vallem-blog-card__more {
	--vallem-roll: 1.35em;

	display: inline-flex;
	align-items: center;
	gap: 0.4375rem;
	margin: 0;
	margin-block-start: auto;
	padding-block-start: var(--wp--preset--spacing--20);
	color: var(--wp--preset--color--primary);
}

@supports (height: 1lh) {
	.vallem-blog .vallem-blog-card__more {
		--vallem-roll: 1lh;
	}
}

.vallem-blog .vallem-blog-card__more::after {
	content: "";
	flex: none;
	inline-size: 0.6875rem;
	block-size: 0.6875rem;
	background-color: currentcolor;
	-webkit-mask: var(--vallem-arrow) center / contain no-repeat;
	mask: var(--vallem-arrow) center / contain no-repeat;
}

.vallem-blog .vallem-blog__pagination {
	margin-block-start: var(--wp--preset--spacing--50);
}

/* ---- Motion ------------------------------------------------------------- *
 *
 * The theme's one card motion: the photograph drifts, the card does not. Same
 * 800ms and the same curve every other card in this file uses.
 * ------------------------------------------------------------------------- */

@media (hover: hover) and (pointer: fine) {
	.vallem-blog .vallem-blog-card__photo img {
		transition: transform 800ms cubic-bezier(0.165, 0.84, 0.44, 1);
	}

	.vallem-blog .vallem-blog-card__more::after {
		transition: transform 300ms cubic-bezier(0.25, 1, 0.5, 1);
	}

	.vallem-blog .vallem-blog-card:hover .vallem-blog-card__photo img,
	.vallem-blog .vallem-blog-card:focus-within .vallem-blog-card__photo img {
		transform: scale(1.045);
	}

	.vallem-blog .vallem-blog-card:hover .vallem-blog-card__more::after,
	.vallem-blog .vallem-blog-card:focus-within .vallem-blog-card__more::after {
		transform: translateX(4px);
	}

	.vallem-blog .vallem-blog-card:hover .vallem-blog-card__more .vallem-roll__r,
	.vallem-blog .vallem-blog-card:focus-within .vallem-blog-card__more .vallem-roll__r {
		transform: translateY(calc(-1 * var(--vallem-roll))) rotate(0.001deg);
	}
}

@media (prefers-reduced-motion: reduce) {
	.vallem-blog .vallem-blog-card__photo img,
	.vallem-blog .vallem-blog-card__more::after {
		transition: none;
	}

	.vallem-blog .vallem-blog-card:hover .vallem-blog-card__photo img,
	.vallem-blog .vallem-blog-card:focus-within .vallem-blog-card__photo img,
	.vallem-blog .vallem-blog-card:hover .vallem-blog-card__more::after,
	.vallem-blog .vallem-blog-card:hover .vallem-blog-card__more .vallem-roll__r {
		transform: none;
	}
}

/* ---- One column --------------------------------------------------------- *
 *
 * 781px, this stylesheet's breakpoint. The featured card gives up its two
 * columns here: side by side in 390px of screen the words get 170px and the
 * headline breaks after every second word.
 * ------------------------------------------------------------------------- */

@media (width <= 781px) {
	.vallem-blog .vallem-blog__list {
		grid-template-columns: minmax(0, 1fr);
	}

	.vallem-blog .vallem-blog__list > li:first-child .vallem-blog-card {
		display: flex;
		flex-direction: column;
	}

	.vallem-blog .vallem-blog__list > li:first-child .vallem-blog-card__photo {
		aspect-ratio: 16 / 9;
		min-height: 0;
	}

	.vallem-blog .vallem-blog__list > li:first-child .vallem-blog-card__body {
		padding: clamp(1.25rem, 2.2vw, 1.75rem);
	}

	.vallem-blog .vallem-blog__list > li:first-child .vallem-blog-card__title {
		max-width: 26ch;
		font-size: var(--wp--preset--font-size--xx-large);
	}
}

/* ------------------------------------------------------------------------- *
 * Blog, artikel. See templates/single.html
 *
 * The klantverhaal's shape, one content type over: a navy hero carrying the
 * cover, a left rail that stays put while the reader scrolls, and the prose on
 * white beside it. What the rail holds is the difference. A klantverhaal has
 * fields to put there, a company and its numbers; an article has only itself,
 * so the rail indexes the article's own h2s and assets/js/blog-toc.js builds it.
 *
 * Deliberately not the anchor dock. `vallem_dock_collect()` in functions.php
 * returns early unless `is_singular( 'vallem_use_case' )` and has no filter to
 * open it, and it indexes `.vallem-topic` sections that a pattern puts there on
 * purpose. An article's headings are whatever the writer typed. Two different
 * questions, so two different implementations rather than one with a flag.
 * ------------------------------------------------------------------------- */

/* The reading hairline. Fixed to the top of the viewport rather than parked
   under the nav, because the nav hides itself on the way down and a progress
   bar that leaves with it is worse than none. */
.vallem-post-progress {
	position: fixed;
	inset-block-start: 0;
	inset-inline: 0;

	/* Above .vallem-nav, which is 100. The header is sticky, so at 60 the bar
	   was painted behind it from the first scroll onwards, which is the whole
	   time it has anything to say. */
	z-index: 110;
	max-width: none;
	height: 2px;
	margin: 0 !important;
	pointer-events: none;
}

.vallem-post-progress .vallem-post-progress__bar {
	width: 0;
	height: 100%;
	background-color: var(--wp--preset--color--terminal);
}

@media (prefers-reduced-motion: reduce) {
	.vallem-post-progress {
		display: none;
	}
}

/* ---- The hero ----------------------------------------------------------- */

.vallem-post-hero {
	position: relative;
	display: flex;
	align-items: flex-end;
	min-height: clamp(340px, 46vh, 560px);
	padding-block: var(--wp--preset--spacing--60);
	overflow: hidden;
	isolation: isolate;
}

/* max-width matters as much as the inset. The figure is a direct child of a
   constrained group, so core hands it the content measure and centres it, and
   `inset: 0` does not undo a max-width. Same trap as the klantverhaal hero. */
.vallem-post-hero .vallem-post-hero__photo {
	position: absolute;
	inset: 0;
	max-width: none;
	margin: 0;
	z-index: 0;
}

.vallem-post-hero .vallem-post-hero__photo img {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
}

/* The theme's navy veil, weighted to the bottom where the headline sits. An
   article with no cover has no figure at all, and the scrim then sits on the
   section's own primary-darkest, which is the same navy. Nothing to guard. */
.vallem-post-hero::before {
	content: "";
	position: absolute;
	inset: 0;
	z-index: 1;
	background:
		linear-gradient(
			180deg,
			rgb(7 26 58 / 0.62) 0%,
			rgb(7 26 58 / 0.40) 38%,
			rgb(7 26 58 / 0.88) 100%
		);
}

.vallem-post-hero .vallem-post-hero__copy {
	position: relative;
	z-index: 2;
	width: 100%;
}

/* The klantverhaal hero's chip, built from currentcolor so it needs no colour
   of its own. core/post-terms wraps its links in a div, so the chip is on the
   anchor and the div is only a row. */
.vallem-post-hero .vallem-post-hero__cat a {
	display: inline-block;
	padding: 0.3em 0.7em;
	border-radius: 4px;
	background-color: color-mix(in srgb, currentcolor 14%, transparent);
	color: inherit;
	text-decoration: none;
}

.vallem-post-hero .vallem-post-hero__cat a:hover {
	background-color: color-mix(in srgb, currentcolor 26%, transparent);
}

/* margin-block-start is deliberately not reset. Core emits `> * + * {
   margin-block-start: <blockGap> }` for the copy group at one class, and a
   `margin: 0` shorthand here would silently eat the gap the template asked
   for. Same note as .vallem-case-hero__title. */
.vallem-post-hero .vallem-post-hero__title {
	margin-inline: 0;
	margin-block-end: 0;
	max-width: 22ch;
	text-wrap: balance;
}

.vallem-post-hero .vallem-post-hero__meta {
	align-items: baseline;
	row-gap: 4px;
	color: var(--wp--preset--color--base-muted);
	line-height: 1.4;
}

.vallem-post-hero .vallem-post-hero__meta > * {
	margin: 0;
}

.vallem-post-hero .vallem-post-hero__meta > *:empty {
	display: none;
}

.vallem-post-hero .vallem-post-hero__meta > * + *::before {
	content: "";
	display: inline-block;
	width: 3px;
	height: 3px;
	margin-inline: 0.6em;
	border-radius: 50%;
	background-color: currentcolor;
	opacity: 0.55;
	vertical-align: middle;
}

/* ---- The rail beside the prose ------------------------------------------ */

.vallem-post-body {
	padding-block: var(--wp--preset--spacing--80);
}

.vallem-post-body .vallem-post-body__cols {
	gap: clamp(2rem, 6vw, 5rem);
}

/* `align-self: start` is what makes sticky possible at all: a flex item
   stretches to the row by default and a full-height box has nothing to stick
   to. The offset is the dock's anchor, which already accounts for the compact
   nav and the admin bar, rather than a second guess at the same number. */
.vallem-post-body .vallem-post-toc {
	position: sticky;
	top: var(--vallem-dock-anchor, var(--wp--preset--spacing--60));
	align-self: start;
}

/* The rail ships empty and blog-toc.js fills it, so until it has run, or on an
   article with fewer than three headings, there is nothing here to look at.
   Hiding the label with it, so the word does not stand over an empty column. */
.vallem-post-body .vallem-post-toc:not(.is-enhanced) {
	display: none;
}

.vallem-post-body .vallem-post-toc__label {
	margin: 0 0 var(--wp--preset--spacing--20);
	color: var(--wp--preset--color--contrast-muted);
	letter-spacing: 0.01em;
}

/* A neutral rule, not a coloured one. The active item is marked by weight and
   ink; a tinted bar down the side of the current entry is the docs cliche and
   this theme does not use accent borders anywhere. */
.vallem-post-body .vallem-post-toc__list {
	margin: 0;
	padding: 0;
	border-left: 1px solid color-mix(in srgb, currentcolor 14%, transparent);
	list-style: none;
}

.vallem-post-body .vallem-post-toc__item {
	margin: 0;
}

.vallem-post-body .vallem-post-toc__link {
	display: block;
	padding: 0.35em 0 0.35em 0.9em;
	color: var(--wp--preset--color--contrast-muted);
	font-size: var(--wp--preset--font-size--small);
	line-height: 1.45;
	text-decoration: none;
}

.vallem-post-body .vallem-post-toc__link:hover {
	color: var(--wp--preset--color--contrast);
}

.vallem-post-body .vallem-post-toc__link[aria-current] {
	color: var(--wp--preset--color--contrast);
	font-weight: 500;
}

/* ---- The article -------------------------------------------------------- *
 *
 * Smaller than the klantverhaal's narrative, which runs at 18 to 22px because
 * it is a story being told. An article is being read for what it says, and 17
 * to 19 on a 1.7 leading is the measure the reference blogs use.
 * ------------------------------------------------------------------------- */

.vallem-post-body .vallem-post-content {
	font-size: clamp(1.0625rem, 1.2vw, 1.1875rem);
	line-height: 1.7;
}

.vallem-post-body .vallem-post-content > :first-child {
	margin-block-start: 0;
}

/* The opening paragraph. Size and colour are block attributes in the starter so
   a writer can change both; this is the measure, which is shorter than the body
   because the lede is read at a glance and a 68ch line is not. */
.vallem-post-body .vallem-post-content .vallem-post-lede {
	max-width: 56ch;
	line-height: 1.55;
}

.vallem-post-body .vallem-post-content p {
	max-width: 68ch;
}

/* The `:where()` is what keeps a button's label out of this. A block style
   variation is written by WordPress as `:root :where(...)`, one class, see
   get_block_style_variation_selector() and the `:root :where` wrapping around
   line 3207 of class-wp-theme-json.php, so a three-class rule here was
   repainting the label of every button placed in an article, `blog-cta`
   included. Core has the same problem with its own link element and solves it
   the same way, `a:where(:not(.wp-element-button))` at line 655 of that file.
   In `:where()` so the specificity of this rule is exactly what it was. */
.vallem-post-body .vallem-post-content a:where(:not(.wp-element-button)) {
	color: var(--wp--preset--color--primary);
}

.vallem-post-body .vallem-post-content :is(h2, h3, h4) {
	text-wrap: balance;
}

/* Two steps down from theme.json's h2, which is display-medium and tops out at
   48px. That size is for a heading that introduces a page; inside a 760px
   column of running prose it is nearly half the measure and reads as a title
   every time the reader passes one. Rendered at 1440 before and after. A font
   size set on the block still wins, because a preset class carries !important,
   so this is a default and not a lock. */
.vallem-post-body .vallem-post-content h2 {
	margin-block: var(--wp--preset--spacing--60) var(--wp--preset--spacing--20);
	font-size: var(--wp--preset--font-size--xxx-large);
}

.vallem-post-body .vallem-post-content h3 {
	margin-block: var(--wp--preset--spacing--40) var(--wp--preset--spacing--10);
	font-size: var(--wp--preset--font-size--xx-large);
}

/* Where a jump from the rail stops. The dock's anchor again, so an article and
   a klantverhaal land the same distance under the nav. */
.vallem-post-body .vallem-post-content :is(h2, h3) {
	scroll-margin-top: var(--vallem-dock-anchor, 96px);
}

.vallem-post-body .vallem-post-content :is(ul, ol) {
	max-width: 66ch;
}

.vallem-post-body .vallem-post-content li {
	margin-block: 0.35em;
}

/* ---- Who wrote it ------------------------------------------------------- */

.vallem-post-body .vallem-post-author {
	max-width: 760px;
	margin-block-start: var(--wp--preset--spacing--70);
	padding-block-start: var(--wp--preset--spacing--30);
	border-top: 1px solid color-mix(in srgb, currentcolor 14%, transparent);
}

/* No portrait, and that is a decision rather than an omission. core/avatar and
   core/post-author both fetch from gravatar.com, which hands the reader's IP to
   a third party on every article. This theme self-hosts its fonts for exactly
   that reason, so a byline may not undo it. A local portrait can be added later
   as a field on the user; a Gravatar cannot. */
.vallem-post-body .vallem-post-author > * {
	margin: 0;
}

.vallem-post-body .vallem-post-author__label {
	color: var(--wp--preset--color--contrast-muted);
}

.vallem-post-body .vallem-post-author__bio {
	max-width: 62ch;
	margin-block-start: 0.35em;
	line-height: 1.6;
}

.vallem-post-body .vallem-post-author__bio p {
	margin: 0;
}

/* ---- More artikelen ----------------------------------------------------- *
 *
 * Three across rather than the klantverhalen's two, because an article card
 * carries a category and a title and no logo, so it is shorter and three sit
 * comfortably on the same row. `vallem-exclude-current` on the query block is
 * what keeps the piece being read out of its own related list; the filter is in
 * functions.php and is post-type agnostic, so it needed no change.
 * ------------------------------------------------------------------------- */

.vallem-post-more {
	padding-block: var(--wp--preset--spacing--80);
}

.vallem-post-more .vallem-post-more__query {
	margin-block-start: var(--wp--preset--spacing--60);
}

.vallem-post-more .vallem-post-more__list {
	display: grid;
	grid-template-columns: repeat(3, minmax(0, 1fr));
	gap: clamp(1rem, 2vw, 1.5rem);
	margin: 0;
	padding: 0;
	list-style: none;
}

.vallem-post-more .vallem-post-more__list > li {
	margin-block: 0;
}

.vallem-post-more .vallem-post-more__card {
	position: relative;
	display: flex;
	flex-direction: column;
	height: 100%;
	overflow: hidden;
	border: 1px solid rgb(255 255 255 / 0.12);
	border-radius: 6px;
	background-color: rgb(255 255 255 / 0.04);
}

.vallem-post-more .vallem-post-more__photo {
	margin: 0;
	aspect-ratio: 16 / 9;
}

.vallem-post-more .vallem-post-more__photo img {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
}

.vallem-post-more .vallem-post-more__body {
	display: flex;
	flex: 1;
	flex-direction: column;
	align-items: flex-start;
	box-sizing: border-box;
	padding: clamp(1.25rem, 2.2vw, 1.75rem);
}

.vallem-post-more .vallem-post-more__cat,
.vallem-post-more .vallem-post-more__cat a {
	color: var(--wp--preset--color--base-muted);
	text-decoration: none;
}

.vallem-post-more .vallem-post-more__title {
	margin: 0;
	color: #fff;
	text-wrap: balance;
}

.vallem-post-more .vallem-post-more__title a {
	color: inherit;
	text-decoration: none;
}

.vallem-post-more .vallem-post-more__title a::after {
	content: "";
	position: absolute;
	inset: 0;
	z-index: 3;
}

.vallem-post-more .vallem-post-more__card:focus-within {
	outline: 2px solid var(--wp--preset--color--terminal);
	outline-offset: 3px;
}

.vallem-post-more .vallem-post-more__title a:focus-visible {
	outline: none;
}

@media (hover: hover) and (pointer: fine) {
	.vallem-post-more .vallem-post-more__photo img {
		transition: transform 800ms cubic-bezier(0.165, 0.84, 0.44, 1);
	}

	.vallem-post-more .vallem-post-more__card:hover .vallem-post-more__photo img {
		transform: scale(1.04);
	}
}

@media (prefers-reduced-motion: reduce) {
	.vallem-post-more .vallem-post-more__photo img {
		transition: none;
	}

	.vallem-post-more .vallem-post-more__card:hover .vallem-post-more__photo img {
		transform: none;
	}
}

/* ---- Narrow ------------------------------------------------------------- *
 *
 * The rail goes away rather than moving above the article. A table of contents
 * stacked over the prose is a list the reader has to scroll past to reach the
 * first paragraph, every time, and on a phone the scroll it saves is shorter
 * than the one it costs.
 * ------------------------------------------------------------------------- */

@media (width <= 781px) {
	.vallem-post-body .vallem-post-toc {
		display: none;
	}

	.vallem-post-more .vallem-post-more__list {
		grid-template-columns: minmax(0, 1fr);
	}
}

/* ------------------------------------------------------------------------- *
 * The blocks an article is built from.
 *
 * Six things a writer drops into post content: the summary above the piece, a
 * kanttekening between two paragraphs, a pull quote, a stretch of terminal
 * output, a row of figures, and a verwijzing to a dienstpagina. They are
 * patterns/blog-*.php and they are unsynced, so the blocks land in the post and
 * survive whatever happens to this theme.
 *
 * Ground, padding and corner live in the block attributes and not here, so all
 * three stay editable in the sidebar. What is left in this file is what a block
 * attribute cannot say: the rhythm between the blocks, the two article rules
 * that have to be undone inside a plate, and the hairlines.
 *
 * Rules that undo something the article does are scoped to
 * `.vallem-post-content`; rules that are the object's own look are not. Dropped
 * on a page instead of into an article there is nothing to undo, and the
 * patterns still stand up on their own.
 * ------------------------------------------------------------------------- */

/* One rhythm for five of the six. The constrained layout writes its gap as
   `.wp-container-x > * + *` at one class, and this is three, so it wins with no
   !important. Same construction as `.vallem-case-story .vallem-case-block`. */
.vallem-post-body .vallem-post-content :is(
	.vallem-blog-tldr,
	.vallem-blog-callout,
	.vallem-blog-terminal,
	.vallem-blog-stats,
	.vallem-blog-cta
) {
	margin-block: var(--wp--preset--spacing--50);
}

/* The quote gets more air than the rest, because it is the only one of the six
   that is a break in the reading rather than a thing attached to it. */
.vallem-post-body .vallem-post-content .vallem-blog-quote {
	margin-block: var(--wp--preset--spacing--60);
}

/* The article caps its paragraphs at 68ch and its lists at 66ch. That is right
   for running prose on white and wrong inside a tinted plate, where the text
   then stops about 80px short of the padding edge and the card looks broken.
   Give the plates their own box back. */
.vallem-post-body .vallem-post-content :is(
	.vallem-blog-tldr,
	.vallem-blog-callout,
	.vallem-blog-quote,
	.vallem-blog-terminal,
	.vallem-blog-stats,
	.vallem-blog-cta
) :is(p, ul, ol) {
	max-width: none;
}

/* Same argument for the heading margins. `.vallem-post-content h3` is 2rem over
   and 0.5rem under, which is the distance a subheading needs from the paragraph
   before it; inside the CTA plate the group's own gap is already doing that job
   and the 2rem opens a hole under the padding. */
.vallem-post-body .vallem-post-content .vallem-blog-cta :is(h2, h3, h4) {
	margin-block: 0;
}

/* ---- In het kort -------------------------------------------------------- */

.vallem-blog-tldr .vallem-blog-tldr__label {
	letter-spacing: 0.01em;
}

/* The browser's 40px list indent is drawn for a list in a page margin, not for
   one inside 32px of card padding. */
.vallem-blog-tldr .vallem-blog-tldr__list {
	padding-inline-start: 1.15em;
}

/* The bullet is furniture and should not be as black as the sentence. Mixed out
   of currentcolor rather than named, so it follows whatever colour the plate is
   given in the editor, which is the same idiom every hairline in this file
   uses. */
.vallem-blog-tldr .vallem-blog-tldr__list li::marker {
	color: color-mix(in srgb, currentcolor 45%, transparent);
}

/* ---- Kanttekening ------------------------------------------------------- */

/* The lead-in is a `strong` inside the sentence, so it takes the browser's 700.
   Inter at 700 next to a 400 sentence is a shout; 600 is the weight the rest of
   the theme labels things at, and it is what the eyebrows and the names in the
   testimonials already use. */
.vallem-blog-callout .vallem-blog-callout__text strong {
	font-weight: 600;
}

/* ---- Uitgelicht citaat -------------------------------------------------- */

.vallem-blog-quote .vallem-blog-quote__text {
	text-wrap: pretty;
}

/* Core's flow layout already zeroes these, the inner group carries a zero block
   gap, and the equivalent pair under `case-quote` is what keeps that row from
   opening up. Spelled out so the two attributions cannot drift apart. */
.vallem-blog-quote .vallem-blog-quote__who p {
	margin: 0;
}

/* ---- Uitvoer op de terminal --------------------------------------------- */

.vallem-blog-terminal .vallem-blog-terminal__caption {
	letter-spacing: 0.01em;
}

/* Output, not prose. Core sets `white-space: pre-wrap` on every preformatted
   block, which is right for a poem and wrong for a log: a line that folds in
   the middle stops being a line of output. So it is `pre` and the box scrolls
   sideways the way a terminal does, with the overscroll contained so a trackpad
   swipe at the end of it does not turn into a browser back gesture.

   The tracking is for the face rather than for the size. WarGames Terminal is a
   dot-matrix drawing and its glyphs sit tight in their advance; a hair of extra
   space is what stops a row of them reading as one block of texture. */
.vallem-blog-terminal .vallem-blog-terminal__out {
	margin: 0;
	overflow-x: auto;
	overscroll-behavior-x: contain;
	white-space: pre;
	line-height: 1.7;
	letter-spacing: 0.04em;
	tab-size: 4;
}

/* ---- Drie cijfers ------------------------------------------------------- */

/* One hairline over the row and nothing else. Neutral, one pixel, mixed out of
   currentcolor so it follows the block's own ink, which is the same rule
   `case-metrics` draws between its rows and the contents rail draws beside its
   links. Not a coloured bar and not a card per figure: this theme has no accent
   stripes, and three tinted boxes in a 760px column would read as a dashboard
   dropped into an article. */
.vallem-blog-stats {
	padding-block-start: var(--wp--preset--spacing--30);
	border-top: 1px solid color-mix(in srgb, currentcolor 14%, transparent);
}

.vallem-blog-stats .vallem-blog-stats__figure,
.vallem-blog-stats .vallem-blog-stats__label {
	margin: 0;
}

.vallem-blog-stats .vallem-blog-stats__label {
	line-height: 1.45;
}

/* ---- Verwijzing naar een dienst ----------------------------------------- */

.vallem-blog-cta .vallem-blog-cta__title {
	max-width: 24ch;
	text-wrap: balance;
}

/* === BEGIN dienstenbento, gegenereerd uit docs/prototypes === */
/* Overgezet met docs/prototypes/migrate-bento-to-theme.py. Niet met de
 * hand bijwerken: pas het prototype aan en draai het script opnieuw. */

/* ------------------------------------------------------------------ *
 * Bento grid. Stripe: display:flex + flex-wrap, gap 16px, per-card
 * aspect-ratio and flex-basis. Their gap token is space-core-200 = 16px.
 * ------------------------------------------------------------------ */
.bento { position: relative; }
.bento__layout {
	display: flex;
	flex-wrap: wrap;
	gap: 16px;
	width: 100%;
}

.card {
	--card-ease: cubic-bezier(0.165, 0.84, 0.44, 1);
	--card-duration: 800ms;
	--card-radius: 6px;
	--card-radius-inner: 5px;
	--card-shift-x: 0;
	--card-shift-y: 0;
	--card-grow-x: 0px;
	--card-grow-y: 0px;
	--card-mouse-x: 0px;
	--card-mouse-y: 0px;

	position: relative;
	display: flex;
	flex: 1;
	transition: z-index 0.1s step-end;
	cursor: pointer;
	aspect-ratio: 344 / 504;
	max-height: 676px;
	contain: layout style;

	border: none;
	background: none;
	-webkit-appearance: none;
	border-radius: 0;
	text-align: inherit;
	box-shadow: none;
	padding: 0;
	color: inherit;
	font: inherit;
}

.bento__layout .card { flex-basis: 100%; max-height: 576px; }

@media (min-width: 640px) {
	.bento__layout .card { flex-basis: calc(50% - 16px); max-height: 100%; }
}

/* Stripe's desktop spans. Onze vijf: een volle band, daaronder twee rijen
 * van een 2/3 hero naast een 1/3. De tweede rij staat gespiegeld, hero
 * rechts.
 *
 * Ontwerphoogte is 576, niet Stripe's 676. De band is 488 hoog en bij 676
 * torenden de kaarten daar zo ver bovenuit dat de band als een strook las
 * in plaats van als de eerste rij. Op 576 lopen beide rijen op 572, de hero
 * wordt door de third gerekt, dus een goede 17% boven de band. Een
 * rijhoogte veranderen doe je hier, in beide noemers tegelijk, en dan in de
 * cardSlot-notities hieronder.
 *
 * third-b is de noemer voor een rij van drie. Geen kaart gebruikt hem nu. */
@media (min-width: 940px) {
	.bento__layout .card--hero {
		aspect-ratio: 816 / 576;
		max-width: 816px;
		flex-basis: calc(66.666% - 8px);
	}
	.bento__layout .card--third {
		aspect-ratio: 400 / 576;
		max-width: 400px;
		flex-basis: calc(33.333% - 8px);
	}
	.bento__layout .card--third-b {
		aspect-ratio: 400 / 576;
		max-width: 400px;
		flex-basis: calc(33.333% - 12px);
	}
}

.card:focus-visible { outline: none; }
.card:focus-visible .card__expand-wrapper {
	outline: 2px solid var(--wp--preset--color--primary);
	outline-offset: 2px;
	border-radius: 4px;
}
.card img, .card svg { pointer-events: none; }

/* The white plate that masks the graphic. Sits at a negative inset and is
 * clipped back in, so hover can animate the clip to zero and the card
 * appears to grow with no reflow. */
.card__inner {
	pointer-events: none;
	position: absolute;
	inset: 0;
	z-index: 2;
	border-radius: var(--card-radius-inner);
	background: #fff;
}
.card__content {
	position: absolute;
	z-index: 2;
	inset: 0;
	border-radius: var(--card-radius-inner);
	overflow: hidden;
}
.card__content-inner { width: 100%; height: 100%; }

@supports selector(:has(*)) {
	@media (min-width: 940px) {
		.card__inner {
			inset: calc(-1 * var(--card-grow-y)) calc(-1 * var(--card-grow-x));
			border-radius: 0;
			clip-path: inset(var(--card-grow-y) var(--card-grow-x) round var(--card-radius-inner));
		}
	}
	@media (min-width: 940px) and (prefers-reduced-motion: no-preference) {
		.card__inner { transition: clip-path var(--card-duration) var(--card-ease); }
	}
}
@media (prefers-reduced-motion: no-preference) {
	.card__content-inner { transition: transform var(--card-duration) var(--card-ease); }
}

/* Title. Stripe caps it at 21ch and reserves 60px on the right for the
 * expand button. Their hero card widens to 42ch. */
.card__text {
	pointer-events: none;
	max-width: min(100% - 80px, 21ch);
	position: absolute;
	top: 16px;
	left: 16px;
	z-index: 3;
	font-size: 24px;
}
@media (min-width: 640px) {
	.card__text { max-width: min(100% - 100px, 21ch); }
	.card__title { font-size: min(24px, 2.75vw); }
}
@media (min-width: 840px) {
	.card__text { max-width: min(100% - 120px, 21ch); }
	.card__title { font-size: min(24px, 3.3vw); }
}
@media (min-width: 940px) {
	.card__text { max-width: 21ch; padding-right: 60px; top: 24px; left: 24px; }
	.card__title { font-size: min(24px, 2.1vw); }
	.card--hero .card__text { max-width: 42ch; }
}
.card__title { text-wrap: balance; }
@media (prefers-reduced-motion: no-preference) {
	.card__title { transition: transform var(--card-duration) var(--card-ease); }
}

/* Expand affordance. 38x38 plate, two-path 20x20 glyph that splays on hover. */
.card__expand {
	position: absolute;
	right: 16px;
	top: 16px;
	width: 38px;
	height: 38px;
	z-index: 3;
	pointer-events: none;
}
.card__expand::before,
.card__expand::after {
	content: "";
	position: absolute;
	display: block;
	inset: 0;
	border-radius: 4px;
}
.card__expand::before { opacity: 0; background: var(--wp--preset--color--primary); }
.card__expand::after { background: var(--wp--preset--color--surface); }
.card__expand-wrapper {
	position: relative;
	border: none;
	cursor: pointer;
	padding: 0;
	text-decoration: none;
	width: 100%;
	height: 100%;
	display: flex;
	align-items: center;
	justify-content: center;
	z-index: 1;
}
.card__expand svg path { fill: var(--wp--preset--color--primary); }

@media (prefers-reduced-motion: no-preference) {
	.card__expand { transition: transform var(--card-duration) var(--card-ease); }
	.card__expand::before,
	.card__expand::after { transition: opacity 0.6s cubic-bezier(0.16, 1, 0.3, 1); }
	.card__expand svg path { transition: transform 0.6s cubic-bezier(0.16, 1, 0.3, 1); }
}

@media (pointer: fine) {
	.card:hover .card__expand svg path { fill: #fff; }
	.card:hover .card__expand svg path:first-child { transform: translate(1px, -1px); }
	.card:hover .card__expand svg path:nth-child(2) { transform: translate(-1px, 1px); }
	.card:hover .card__expand::before { opacity: 1; }
	.card:hover .card__expand::after { opacity: 0; }
}

.card__graphic { position: absolute; z-index: 1; inset: 0; }
@media (prefers-reduced-motion: no-preference) {
	.card__graphic { transition: transform var(--card-duration) var(--card-ease); }
}

/* 1px border that also grows, with a cursor-tracking bloom behind it. */
.card__border {
	position: absolute;
	inset: -1px;
	z-index: 1;
	border-radius: var(--card-radius);
	background: var(--wp--preset--color--border);
	overflow: hidden;
	pointer-events: none;
}
@supports selector(:has(*)) {
	@media (min-width: 940px) {
		.card__border {
			inset: calc(-1 * var(--card-grow-y) - 1px) calc(-1 * var(--card-grow-x) - 1px);
			clip-path: inset(var(--card-grow-y) var(--card-grow-x) round var(--card-radius));
		}
	}
	@media (min-width: 940px) and (prefers-reduced-motion: no-preference) {
		.card__border { transition: clip-path var(--card-duration) var(--card-ease); }
	}
}
.card__border-color {
	position: absolute;
	top: 50%;
	left: 50%;
	height: 80%;
	aspect-ratio: 1;
	opacity: 0;
	transform: translate(-50%, -50%);
	display: none;
}
@media (min-width: 940px) { .card__border-color { display: block; } }
@media (prefers-reduced-motion: no-preference) {
	.card__border-color { transition: opacity 0.25s linear; }
}
.card__border-gradient {
	position: absolute;
	inset: 0;
	opacity: 0.5;
	background: radial-gradient(circle, var(--wp--preset--color--primary), var(--wp--preset--color--violet) 33%, var(--wp--preset--color--border) 66%);
	transform: translate3d(var(--card-mouse-x), var(--card-mouse-y), 0) scale(var(--border-scale, 1));
}
@media (prefers-reduced-motion: no-preference) {
	.card__border-gradient {
		transition: transform 1s cubic-bezier(0.16, 1, 0.3, 1);
		will-change: transform;
	}
}
@media (min-width: 940px) {
	.card--hero .card__border-color { width: 130%; height: auto; }
	.card--hero .card__border-gradient { inset: 15%; }
}

@media (min-width: 940px) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
	.card:hover .card__border-color { opacity: 1; }
	.card:hover .card__expand svg path:first-child { transform: translate(2px, -2px); }
	.card:hover .card__expand svg path:nth-child(2) { transform: translate(-2px, 2px); }
}
@supports selector(:has(*)) {
	@media (min-width: 940px) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
		.card:hover .card__title { transform: translate(var(--card-shift-x), var(--card-shift-y)); }
		.card:hover .card__expand { transform: translate(calc(var(--card-shift-x) * -1), var(--card-shift-y)); }
		.card:hover .card__inner { clip-path: inset(0 round var(--card-radius-inner)); }
		.card:hover .card__border { clip-path: inset(0 round var(--card-radius)); }
	}
}

/* De kaart was een <button> in het prototype en is hier een <a> naar de
 * dienstpagina, dus de link-opmaak van het thema moet er nog af. */
.bento__layout .card { text-decoration: none; }

/* De sectie vult de breedte die hij krijgt. Het prototype draaide op een
 * contentbox van 1216 en had max-widths van 816 en 400 op de kaarten; die
 * binden vanaf 1232 en laten de rijen tekortkomen terwijl de band wel
 * doorloopt. Gemeten op 1280: 48px tekort, op 1344: 112px. */
.bento__layout .card--hero,
.bento__layout .card--third,
.bento__layout .card--third-b { max-width: none; }

/* De sectiekop. Overgenomen uit .section-title in het prototype, zonder
 * de margin-bottom: de blockGap van de groep zet die afstand nu. */
.vallem-bento__title {
	max-width: 66.666%;
	font-size: clamp(28px, 3.4vw, 48px);
	line-height: 1.14;
	letter-spacing: -0.028em;
	text-wrap: balance;
}
@media (max-width: 939px) { .vallem-bento__title { max-width: 100%; } }
.vallem-bento__subdued { color: var(--wp--preset--color--contrast-muted); }
/* De constrained layout van core zet margin-inline op auto op elk kind
 * van de groep. Een kop met een eigen max-width wordt daardoor
 * gecentreerd in plaats van links uitgelijnd, en stond 195px
 * ingesprongen ten opzichte van de bento eronder (gemeten in
 * Playground: bento x=60, kop x=255 bij een contentbox van 1280).
 * De kop zit daarom in een eigen flow-groep, zie het pattern. */
.vallem-bento__title { margin-inline: 0; padding-inline: 0; }

/* ------------------------------------------------------------------ *
 * The MDR cell runs dark. Stripe puts one dark cell in its bento too;
 * here it is load-bearing, because the radar is unreadable on white.
 * ------------------------------------------------------------------ */
.card--dark .card__inner { background: #050B1F; }
.card--dark .card__border { background: #16213F; }
.card--dark .card__title { color: #F4F5FA; }
.card--dark .card__expand::after { background: rgb(255 255 255 / 0.10); }
.card--dark .card__expand svg path { fill: #6A9FFF; }
.card--dark .card__expand:hover svg path,
.card--dark .card:hover .card__expand svg path { fill: #050B1F; }

/* ------------------------------------------------------------------ *
 * MDR radar module.
 *
 * Everything is namespaced rdr- so it can drop into the bento without
 * colliding with the card styles. The tuning bench lives in
 * mdr-radar.html; this file carries only the settled design.
 *
 * Locked values: centre 709/678, r 500, 4 rings, 8 spokes, bands at 15%,
 * 13s revolution, 62deg tail, two beams. Those are units of the 816x676
 * design box, not pixels; the cell it lands in is some other shape and
 * gets covered, see .rdr__sweep.
 * ------------------------------------------------------------------ */
.rdr {
	position: absolute;
	inset: 0;
	overflow: hidden;
	background: var(--rdr-bg);

	/* Makes cqw and cqh the cell, which is what the sweep measures itself
	 * against. Without it they silently fall back to the viewport. */
	container-type: size;

	--rdr-bg: #050B1F;
	--rdr-ink: #F4F5FA;
	--rdr-ink-muted: #8E9BC4;

	--rdr-field: 106 159 255;
	--rdr-ring: rgb(140 160 255 / 0.20);
	--rdr-ring-outer: rgb(150 170 255 / 0.34);
	--rdr-spoke: rgb(120 140 255 / 0.14);
	--rdr-dot: rgb(170 190 255 / 0.45);
	--rdr-tick: rgb(150 170 255 / 0.30);
	--rdr-blip: rgb(180 200 255 / 0.8);

	--rdr-band-a: rgb(106 159 255 / 0.014);
	--rdr-band-b: rgb(106 159 255 / 0.025);

	--rdr-sweep: 120 160 255;
	--rdr-peak: 0.34;
	--rdr-tail: 0.12;

	/* On the dark field. The panel overrides both back to the light pair. */
	--rdr-alert: #FF6A5E;
	--rdr-success: #34D399;
	--rdr-dur: 13s;
	--rdr-arc: 62deg;
}

/* ---------------- the field ---------------- */
.rdr__svg { position: absolute; inset: 0; z-index: 2; width: 100%; height: 100%; display: block; }

/* The linework is an svg and gets fitted by preserveAspectRatio; the sweep
 * is a div and cannot inherit that, so it repeats the same fit here off the
 * same numbers, published by mountRadar. --rdr-fit is the scale that
 * xMaxYMax slice applies, px per design unit, and the offsets are measured
 * from the bottom-right corner because that is the edge slice pins.
 *
 * The two fits must stay identical. This was percentages of the cell once,
 * which scales x and y apart, and the beam sailed off the rings as an
 * ellipse on every cell that was not exactly 816x676. */
.rdr__sweep {
	--rdr-fit: max(100cqw / var(--rdr-vb-w), 100cqh / var(--rdr-vb-h));

	position: absolute;
	z-index: 1;
	right: calc((var(--rdr-vb-w) - var(--rdr-cx) - var(--rdr-r)) * var(--rdr-fit));
	bottom: calc((var(--rdr-vb-h) - var(--rdr-cy) - var(--rdr-r)) * var(--rdr-fit));
	width: calc(var(--rdr-r) * 2 * var(--rdr-fit));
	height: calc(var(--rdr-r) * 2 * var(--rdr-fit));
	border-radius: 50%;
	pointer-events: none;
	/* Two beams, 180 apart. With the centre parked off the bottom-right
	 * corner only a quadrant is on the card, so one beam left it dead
	 * three quarters of the time. */
	background: conic-gradient(from 0deg,
		rgb(var(--rdr-sweep) / 0)                 0deg,
		rgb(var(--rdr-sweep) / var(--rdr-tail))   calc(var(--rdr-arc) * 0.62),
		rgb(var(--rdr-sweep) / var(--rdr-peak))   var(--rdr-arc),
		rgb(var(--rdr-sweep) / 0)                 calc(var(--rdr-arc) + 0.5deg),
		rgb(var(--rdr-sweep) / 0)                 180deg,
		rgb(var(--rdr-sweep) / var(--rdr-tail))   calc(180deg + var(--rdr-arc) * 0.62),
		rgb(var(--rdr-sweep) / var(--rdr-peak))   calc(180deg + var(--rdr-arc)),
		rgb(var(--rdr-sweep) / 0)                 calc(180deg + var(--rdr-arc) + 0.5deg),
		rgb(var(--rdr-sweep) / 0)                 360deg);
	animation: rdr-spin var(--rdr-dur) linear infinite;
	will-change: transform;
}
.rdr__sweep::after,
.rdr__sweep::before {
	content: "";
	position: absolute;
	left: 50%; width: 1px; height: 50%;
	transform: translateX(-50%) rotate(var(--rdr-arc));
}
.rdr__sweep::after {
	top: 0;
	transform-origin: bottom center;
	background: linear-gradient(to bottom,
		rgb(var(--rdr-sweep) / 0),
		rgb(var(--rdr-sweep) / calc(var(--rdr-peak) * 2.2)));
}
.rdr__sweep::before {
	top: 50%;
	transform-origin: top center;
	background: linear-gradient(to top,
		rgb(var(--rdr-sweep) / 0),
		rgb(var(--rdr-sweep) / calc(var(--rdr-peak) * 2.2)));
}
@keyframes rdr-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .rdr__sweep { animation: none; } }

/* ---------------- blips ---------------- */
.rdr__blip { transition: opacity 700ms ease; }
.rdr__pulse { transform-box: fill-box; transform-origin: center; opacity: 0; }
.rdr__blip[data-state="alert"] .rdr__core,
.rdr__blip[data-state="alert"] .rdr__halo { fill: var(--rdr-alert); }
.rdr__blip[data-state="alert"] .rdr__pulse {
	fill: var(--rdr-alert);
	animation: rdr-pulse 1.9s ease-out infinite;
}
.rdr__blip[data-state="ok"] .rdr__core,
.rdr__blip[data-state="ok"] .rdr__halo { fill: var(--rdr-success); }
@keyframes rdr-pulse {
	0%   { transform: scale(0.6); opacity: 0.45; }
	100% { transform: scale(3.2); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) { .rdr__pulse { animation: none; } }

.rdr__label {
	font-size: 11px; font-weight: 500;
	fill: var(--rdr-ink-muted);
	font-variant-numeric: tabular-nums;
	transition: fill 300ms ease;
}
.rdr__label[data-state="alert"] { fill: var(--rdr-alert); }
.rdr__label[data-state="ok"] { fill: var(--rdr-success); }

/* ---------------- the status card ---------------- *
 * Stays a white card on the dark field, so it carries the light palette
 * with it. Without this it inherits the dark ink and prints near-white
 * text on its own white surface.
 */
.rdr__panel {
	position: absolute;
	left: 24px; bottom: 30px; z-index: 3;
	width: 348px;
	padding: 22px 22px 18px;
	border-radius: 10px;
	background: linear-gradient(180deg, #FFFFFF 0%, #FCFCFE 100%);
	box-shadow: 0 0 0 0.5px rgb(255 255 255 / 0.10),
	            0 2px 6px rgb(0 0 0 / 0.30),
	            0 28px 72px -24px rgb(0 0 0 / 0.85);

	--rdr-ink: #18181B;
	--rdr-ink-muted: #71717A;
	--rdr-alert: #D92D20;
	--rdr-success: #0A8F60;
	--rdr-hair: #EDEDF2;
	color: var(--rdr-ink);
}

.rdr__head { display: flex; align-items: center; gap: 7px; }
.rdr__mark { flex: none; width: 13px; height: 13px; display: block; color: var(--rdr-ink-muted); }
.rdr__mark path { stroke: currentColor; stroke-width: 2.2; fill: none; stroke-linecap: round; stroke-linejoin: round; }
.rdr__provenance { font-size: 12.5px; font-weight: 500; letter-spacing: -0.006em; color: var(--rdr-ink); }

.rdr__hero { margin-top: 14px; }
.rdr__statement {
	font-size: 21px; font-weight: 550; letter-spacing: -0.03em; line-height: 1.15;
	transition: color 300ms ease;
}
.rdr__statement .word { display: inline-block; white-space: nowrap; }
.rdr__statement .ch { display: inline-block; will-change: transform, opacity, filter; }
.rdr__meta .word { display: inline-block; white-space: nowrap; }
.rdr__meta .ch { display: inline-block; will-change: transform, opacity, filter; }
.rdr__meta { margin-top: 6px; font-size: 12.5px; letter-spacing: -0.004em; color: var(--rdr-ink-muted); }

.rdr__panel[data-state="alert"] .rdr__statement { color: var(--rdr-alert); }
.rdr__panel[data-state="ok"] .rdr__statement { color: var(--rdr-success); }

/* One bar per sweep the radar actually completed. Honest by construction:
 * it counts real revolutions rather than asserting a statistic. */
.rdr__scan { margin-top: 20px; display: flex; gap: 2.5px; }
.rdr__scan i {
	flex: 1; height: 24px; border-radius: 1.5px;
	background: rgb(24 60 221 / 0.13);
	transition: background 500ms ease;
}
.rdr__scan i[data-s="live"]  { background: rgb(24 60 221 / 0.34); }
.rdr__scan i[data-s="alert"] { background: var(--rdr-alert); }
.rdr__scan i[data-s="ok"]    { background: var(--rdr-success); }
.rdr__legend {
	display: flex; justify-content: space-between;
	margin-top: 9px; font-size: 11.5px; color: var(--rdr-ink-muted);
	letter-spacing: -0.002em; font-variant-numeric: tabular-nums;
}

/* Below the bento breakpoint the cell changes shape entirely, so the card
 * gets its own crop rather than a scaled-down desktop one. */
@media (max-width: 939px) {
	.rdr__panel { left: 16px; bottom: 16px; width: min(320px, calc(100% - 32px)); padding: 18px 18px 15px; }
	.rdr__statement { font-size: 18px; }
	.rdr__scan i { height: 20px; }
}
@media (max-width: 639px) {
	.rdr__label { display: none; }
}

/* ------------------------------------------------------------------ *
 * AIO Awareness, de 3/3 band in de bento.
 *
 * Twee dingen om te weten voordat je hier iets verzet.
 *
 * 1. [data-graphic] is niet de kaart. Het zit in .card__inner, en die
 *    staat op een negatieve inset van --card-grow-x / --card-grow-y en
 *    wordt met clip-path teruggeknipt. Dat is de groei bij hover: de
 *    plaat is altijd al groter, alleen de knip beweegt. Een band die
 *    zijn kop op 24px zet ten opzichte van [data-graphic] komt dus 15px
 *    te ver naar links uit ten opzichte van de andere kaarten. Daarom
 *    staat .aw__stage op precies die groei ingesprongen: dat is de doos
 *    die je op het scherm ziet.
 *
 * 2. De wash vult wel de hele plaat, ook het stuk dat pas bij hover in
 *    beeld komt, anders verschijnt daar bij hover de kale kaartkleur.
 *
 * Op de bento-desktop staat alles in echte pixels, zodat de kop op exact
 * 24 zit net als bij MDR. De band is op 1232 getekend en de kaart is
 * 1216; dat verschil valt weg in de grafiek, die toch van de rechterrand
 * af loopt. Pas onder de bento-breakpoint, waar de kaart veel smaller
 * wordt dan het ontwerp, schaalt de band als geheel.
 *
 * Hoort bij awareness-module.js, dat window.mountAwareness(el) levert.
 * ------------------------------------------------------------------ */

.bento__layout .card--band {
	flex-basis: 100%;
	max-width: 100%;
	max-height: none;
	aspect-ratio: 1232 / 494;
}

/* De band draagt zijn eigen kop binnen het vlak, want die moet naast de
 * grafiek en het venster staan. De h3 uit het sjabloon blijft bestaan
 * voor de toegankelijke naam, maar wordt niet getoond. */
.card--band .card__text {
	position: absolute;
	width: 1px; height: 1px;
	margin: -1px; padding: 0;
	overflow: hidden;
	clip-path: inset(50%);
	white-space: nowrap;
}

.aw {
	position: absolute; inset: 0;
	overflow: hidden;
}

/* De zichtbare kaartdoos. Alleen inspringen waar .card__inner ook echt
 * op een negatieve inset staat, en dat is precies onder dezelfde twee
 * voorwaarden. Onder 940px is er geen groei, en dan zou inspringen de
 * band juist --card-grow-x te ver naar rechts zetten. */
.aw__stage {
	position: absolute;
	inset: 0;
}
@supports selector(:has(*)) {
	@media (min-width: 940px) {
		.aw__stage { inset: var(--card-grow-y, 0px) var(--card-grow-x, 0px); }
	}
}

/* Vult de hele plaat, groei incluis, en staat daarom naast .aw__stage en
 * niet erin. In de stage stopt hij op de kaartrand, en dan verschijnt bij
 * hover een harde donkere rand langs rechts en onder: het veld valt daar
 * van rgb(26 47 77) terug naar de kale kaartkleur rgb(8 21 37). De radar
 * heeft dat niet omdat .rdr zelf op de plaat staat. */
.aw__wash {
	position: absolute; inset: 0; z-index: 1; pointer-events: none;
	background:
		radial-gradient(120% 150% at 88% 118%, rgb(106 159 255 / 0.30) 0%, rgb(106 159 255 / 0.10) 34%, rgb(106 159 255 / 0) 66%),
		radial-gradient(80% 120% at 8% -20%, rgb(76 0 255 / 0.20) 0%, rgb(76 0 255 / 0) 60%);
}

/* ------------------------------------------------------------------ *
 * De kop, met het roterende woord. Zelfde maatvoering als .card__title
 * in de bento, zodat de band niet uit de toon valt.
 * ------------------------------------------------------------------ */
.aw__title {
	position: absolute; z-index: 8; top: 24px; left: 24px;
	width: 680px; padding-right: 60px; margin: 0;
	font-size: 24px; line-height: 1.15; font-weight: 500;
	letter-spacing: -0.022em;
	color: #F4F5FA;
}
.aw__title [data-rotating-words] { display: inline-block; position: relative; vertical-align: top; }
.aw .rot__inner { display: inline-block; position: relative; height: 1.15em; vertical-align: top; }
.aw .rot__word {
	display: block; position: absolute; top: 0; left: 0;
	white-space: nowrap; line-height: 1.15;
	color: #6A9FFF;
}
.aw .rot-line { padding-bottom: 0.14em; margin-bottom: -0.14em; white-space: nowrap; }
.aw .rot-line-mask { overflow-x: visible !important; overflow-y: clip !important; }

/* De band beweert twee getallen, dus noemt hij de bron. */
.aw__source {
	position: absolute; z-index: 8; right: 24px; bottom: 18px;
	font-size: 11px; letter-spacing: -0.002em;
	color: rgb(142 155 196 / 0.72);
	font-variant-numeric: tabular-nums;
}

/* ------------------------------------------------------------------ *
 * De grafiek. Twee lijnen, een bevinding: dezelfde training beweegt
 * melden vier keer en raakt klikken nauwelijks. Loopt van de rechterrand
 * af en achter het venster langs.
 * ------------------------------------------------------------------ */
.aw-chart { position: absolute; inset: 0; z-index: 2; }
.aw-chart svg { position: absolute; left: 300px; top: 104px; width: 1000px; height: 430px; overflow: visible; }
.aw-chart .grid { stroke: rgb(140 160 255 / 0.10); stroke-width: 1; }
.aw-chart .p-click  { fill: none; stroke: #8E9BC4; stroke-width: 1.5; }
.aw-chart .p-report { fill: none; stroke: #34D399; stroke-width: 2.5; }
.aw-chart .p-fill   { fill: url(#awRepFill); }
.aw-chart .tag      { fill: #F4F5FA; font-size: 12.5px; font-weight: 550; letter-spacing: -0.01em; }
.aw-chart .tag-sub  { fill: #8E9BC4; font-size: 11.5px; letter-spacing: -0.004em; }
.aw-chart .dot      { fill: #34D399; }

/* ------------------------------------------------------------------ *
 * Het venster.
 *
 * Een halftransparante bezel om een Safari-frame om de video. De bezel
 * is een padding-ring van 10px op 12% wit met een inset hairline op 22%,
 * zodat hij als een zachte lijst over het donkere veld leest en niet als
 * een gloed. Vlakke transparantie, geen backdrop-filter, net als Stripe's
 * eigen scrim en conform de no-blur regel van dit project.
 *
 * Breedte noch hoogte staat vast. Het script rekent --aw-win-w uit de
 * kaarthoogte en de beeldverhouding van de video, zodat het venster
 * altijd precies --aw-bleed van de onderrand af loopt. Zonder die som
 * blijft er onderin donker over zodra de video van vorm verandert, en
 * dat is precies wat er gebeurde toen de render van 1256x768 naar
 * 1256x682 ging.
 * ------------------------------------------------------------------ */
.aw-win {
	position: absolute; z-index: 5;
	left: 24px; top: 100px;
	width: var(--aw-win-w, 648px);
	border-radius: 16px 16px 0 0;
	padding: 10px;
	background: rgb(255 255 255 / 0.12);
	box-shadow: inset 0 0 0 1px rgb(255 255 255 / 0.22),
	            0 2px 6px rgb(0 0 0 / 0.30),
	            0 34px 80px -26px rgb(0 0 0 / 0.85);
}
.aw-win__frame {
	aspect-ratio: var(--slot-ar, 1256 / 682);
	border-radius: 9px 9px 0 0;
	overflow: hidden;
	background: #FFFFFF;
	display: flex;
	flex-direction: column;
}
.aw-win__slot {
	flex: 1;
	position: relative;
	background: #000;
}
.aw-win__slot video {
	position: absolute; inset: 0;
	width: 100%; height: 100%;
	object-fit: cover;
	display: block;
}

/* ------------------------------------------------------------------ *
 * Onder de bento-breakpoint stapelen de kaarten. De band is getekend op
 * 1232 breed, en die als geheel schalen maakte hem op een telefoon een
 * postzegel: op een kaart van 342px staat alles op 28% terwijl de vier
 * andere kaarten gewoon meegroeien.
 *
 * Dus geen schaal maar een echte herindeling. De kop krijgt kaartmaat,
 * het venster vult de breedte en houdt zijn bleed onderlangs, en de
 * grafiek gaat eruit: zijn labels staan op x=1000 van de 1232 en zijn op
 * deze breedte toch niet te lezen of te tonen zonder de rest te knippen.
 * ------------------------------------------------------------------ */
@media (max-width: 939px) {
	/* Hoog genoeg voor kop plus venster, niet hoger: de band is een band
	 * en hoeft niet de staande maat van de andere vier aan te nemen.
	 *
	 * De hoogte die past is kaartbreedte / 1,842 (het venster) plus de kop,
	 * en die kop groeit niet mee. Eén verhouding klopt dus maar op één
	 * breedte: op 736 gaf 344/300 een kaart van 642 voor een venster van
	 * 409. Vandaar twee, een per staffel van de bento zelf. */
	.bento__layout .card--band { aspect-ratio: 344 / 504; max-height: 576px; }

	.aw__stage {
		inset: 0;
		width: auto; height: auto;
		transform: none;
	}

	.aw__title {
		width: auto; right: 24px;
		font-size: min(24px, 6.2vw);
	}
	/* De regel met het roterende woord staat op nowrap, want op de brede
	 * band past hij altijd. Hier niet: dan valt de helft van de kop weg
	 * achter de overflow van .aw. */
	.aw .rot-line { white-space: normal; }

	/* De grafiek gaat eruit, dus de bronregel eronder ook: die noemt de
	 * getallen van precies die grafiek. */
	.aw-chart,
	.aw__source { display: none; }

	/* Breedte uit de kaart, hoogte uit de beeldverhouding van het frame.
	 * De som die --aw-win-w uit de hoogte rekent is voor de brede band;
	 * fit() slaat hem hier over, zie awareness-module.js.
	 *
	 * Het venster loopt hier niet meer van de onderrand af. Op de brede
	 * band is die bleed het sluitstuk, maar op een staande kaart moet het
	 * venster de hoogte vullen om dat te kunnen, en dat gaat niet: de
	 * video is 1256x682 en op cover geknipt tot kaartbreedte sneed hij de
	 * onderwerpregels doormidden. Dus een compleet zwevend venster,
	 * gecentreerd in de ruimte onder de kop, met alle vier de hoeken rond.
	 * De marge is de halve kophoogte, zodat het optisch midden klopt. */
	.aw-win {
		left: 0; right: 0;
		width: auto;
		top: 50%; bottom: auto;
		margin-top: 45px;
		transform: translateY(-50%);
		border-radius: 16px;
	}
	.aw-win__frame { border-radius: 9px; }
}

/* ------------------------------------------------------------------ *
 * Pentest module. De 2/3 hero.
 *
 * Alles namespaced pt- zodat het in de bento kan vallen zonder met de
 * kaartstijlen te botsen. De tuning bench is pentest-bench.html; dit
 * bestand draagt alleen het uitgekristalliseerde ontwerp.
 *
 * Er stond hier eerst een wit paneel met de drieledige schaduw uit
 * .rdr__panel, en daarin per rij een gekleurde woordpil: Open, Opgelost,
 * Gemitigeerd. Daarna een afgerond vlak per rij met een pictogram erin.
 * Alle drie zijn eruit. Vijf identieke gekleurde etiketten onder elkaar
 * is precies het patroon dat als gegenereerd leest, het paneel maakte er
 * een kaart-in-een-kaart van, en de vlakjes waren hetzelfde probleem in
 * het klein. Wat er staat is type en één glyph op het veld.
 *
 * De drie statuswoorden zijn niet verdwenen, want dat is de woordenschat
 * uit de sheet, maar ze staan één keer in de ondertitel in plaats van
 * vijf keer als etiket. Per rij doet de vorm het werk: rood
 * uitroepteken is open, neutraal streepje is gemitigeerd, groen vinkje
 * plus doorhaling is opgelost.
 *
 * Vastgezette waarden: tekstblok links 24 / onder 24, glyph 14 naast een
 * naam van 15, coderegel 10px op 21px, 66 regels per blok, 60s per
 * omloop. Dat zijn eenheden van de 816x576 ontwerpdoos. De cel die het
 * krijgt is een andere maat, en in tegenstelling tot de radar hoeft hier
 * niets rond te blijven: het blok hangt aan de linkeronderhoek en de
 * codekolom aan de rechterrand.
 * ------------------------------------------------------------------ */
.pt {
	position: absolute;
	inset: 0;
	overflow: hidden;
	background: var(--pt-bg);

	/* Maakt cqw/cqh de cel. Nog niet gebruikt in de berekeningen, maar
	 * zonder dit vallen ze stil terug op de viewport zodra dat wel zo is. */
	container-type: size;

	--pt-bg: #050B1F;
	--pt-ink: #F4F5FA;
	--pt-ink-muted: #8E9BC4;

	--pt-accent: 106 159 255;
	--pt-alert: #FF6A5E;
	--pt-success: #34D399;
	--pt-hair: rgb(140 160 255 / 0.20);
	--pt-ease: cubic-bezier(0.4, 0, 0.2, 1);

	/* De code is achtergrond. Eén grondtoon, en de tokens liggen er
	 * dichtbij: op 0.30 alpha leest 10px monospace als textuur, en dat is
	 * de bedoeling. Op 0.45 begon het te concurreren met de tekst. */
	--pt-code: 146 160 200;
	--pt-code-alpha: 0.30;
	--pt-code-left: 436px;
	--pt-code-top: 0px;
	--pt-code-h: auto;

	/* Door mountPentest gemeten en gepubliceerd. De waarden hier zijn
	 * alleen de stand voor het eerste frame; publish() overschrijft ze
	 * inline zodra de kaartkop en het tekstblok opgemeten zijn. */
	--pt-title-bottom: 92px;
	--pt-report-h: 275px;

	/* Gemeten vanaf de bovenkant van de codekolom, niet van de kaart: de
	 * clip-path van de twee lagen zit in dezelfde ruimte, en op de smalle
	 * crops begint de kolom niet meer op 0. Ook deze zet publish(). */
	--pt-scan-y: 297px;
	--pt-lh: 21px;
	--pt-dur: 60s;

	--pt-ico: 14px;
	/* Door alignGlyph() gemeten uit de font: de optische verschuiving van
	 * de glyph naar de kaphoogte van de naam, en de hoogte waarop de
	 * doorhaling hoort. De waarden hier gelden alleen voor het eerste
	 * frame. */
	--pt-glyph-shift: 3.5px;
	--pt-strike-y: 12px;
}

/* ---------------- de coderegen ---------------- *
 * Twee identieke panelen boven elkaar: het onderste toont de code zoals
 * die gerapporteerd is (rode regels), het bovenste dezelfde code na
 * remediation. Elk wordt hard afgeknipt op de hertestlijn. Daardoor hoeft
 * geen enkele regel een status bij te houden: boven de lijn is opgelost,
 * eronder is open, en het omslaan van de regel gebeurt vanzelf terwijl
 * hij de lijn passeert.
 *
 * Dit was eerst een JS-toggle per regel. Die moest twee kopieen van
 * dezelfde regel los uit elkaar houden en liep bij het rondlopen van de
 * strip een frame uit de pas. */
.pt__code {
	position: absolute;
	left: var(--pt-code-left);
	right: 0;
	top: var(--pt-code-top);
	height: var(--pt-code-h);
	bottom: 0;
	z-index: 1;
	overflow: hidden;

	/* Bovenin uitgefade zodat de kaartkop vrij blijft, onderin zodat de
	 * regels niet tegen de onderrand aan stoppen. */
	-webkit-mask-image: linear-gradient(to bottom, transparent 0, transparent 8%, #000 32%, #000 84%, transparent 100%);
	        mask-image: linear-gradient(to bottom, transparent 0, transparent 8%, #000 32%, #000 84%, transparent 100%);
}

/* Tweede as van de fade. Twee geneste maskers in plaats van
 * mask-composite: dat scheelt een compositing-modus die per browser
 * anders heet, en het geeft de kolom precies wat hij nodig had: een
 * rechterrand waar de regels oplossen in plaats van netjes stoppen.
 * Daar past het label, dat eerst een gat in een coderegel sloeg. */
.pt__code-inner {
	position: absolute;
	inset: 0;
	-webkit-mask-image: linear-gradient(to right, #000 0, #000 calc(100% - 190px), transparent 100%);
	        mask-image: linear-gradient(to right, #000 0, #000 calc(100% - 190px), transparent 100%);
}

/* Veiligheidsklep. Onder de drie en een halve regel is een band geen band
 * meer maar een streepje ruis; dan liever niets. Geen display:none, want
 * dan stopt de animatie en daarmee het hele verhaal. */
.pt[data-band="none"] .pt__code,
.pt[data-band="none"] .pt__scan,
.pt[data-band="none"] .pt__scan-label { opacity: 0; }

.pt__pane { position: absolute; inset: 0; }
.pt__pane[data-v="open"]  { clip-path: inset(var(--pt-scan-y) 0 0 0); }
.pt__pane[data-v="fixed"] { clip-path: inset(0 0 calc(100% - var(--pt-scan-y)) 0); }

/* De strip is twee blokken hoog en schuift precies één blok op, dus -50%.
 * Een percentage, geen custom property in de keyframe: dan hoeft de
 * bloklengte nergens tweemaal te staan. */
.pt__strip {
	position: absolute;
	left: 0;
	top: 0;
	width: 100%;
	will-change: transform;
	animation: pt-scroll var(--pt-dur) linear infinite;
}
@keyframes pt-scroll { to { transform: translate3d(0, -50%, 0); } }

.pt__line {
	position: relative;
	height: var(--pt-lh);
	line-height: var(--pt-lh);
	padding: 0 6px 0 18px;
	font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
	font-size: 10px;
	font-weight: 450;
	white-space: pre;
	color: rgb(var(--pt-code) / var(--pt-code-alpha));
}
.pt__line i { font-style: normal; }
.pt__line i.s { color: rgb(var(--pt-code) / calc(var(--pt-code-alpha) * 0.67)); }
.pt__line i.k { color: rgb(150 172 240 / calc(var(--pt-code-alpha) * 1.4)); }
.pt__line i.c { color: rgb(var(--pt-code) / calc(var(--pt-code-alpha) * 0.5)); }

/* De markering ligt om de tekst heen, niet over de volle kolombreedte.
 * Sinds het paneel weg is staat de kolom naast kale achtergrond in
 * plaats van naast een dekvlak, en een band met een harde linkerrand
 * middenin het veld las als een fout. Zo hoort hij bij de regel. */
.pt__hl {
	display: inline-block;
	margin: 0 -6px;
	padding: 0 6px;
	border-radius: 4px;
}
.pt__pane[data-v="open"] .pt__hl {
	background: rgb(255 106 94 / 0.10);
	color: rgb(255 106 94 / 0.48);
}
.pt__pane[data-v="fixed"] .pt__line[data-end="opgelost"] .pt__hl    { background: rgb(52 211 153 / 0.08); }
.pt__pane[data-v="fixed"] .pt__line[data-end="gemitigeerd"] .pt__hl { background: rgb(146 160 200 / 0.06); }

.pt__glyph {
	position: absolute;
	left: 3px;
	top: 50%;
	width: 11px;
	height: 11px;
	transform: translateY(-50%);
	fill: none;
	stroke-width: 1.5;
	stroke-linecap: round;
	stroke-linejoin: round;
}
.pt__pane[data-v="open"] .pt__glyph { stroke: rgb(255 106 94 / 0.55); }
.pt__line[data-end="opgelost"]    .pt__glyph { stroke: rgb(52 211 153 / 0.50); }
.pt__line[data-end="gemitigeerd"] .pt__glyph { stroke: rgb(var(--pt-code) / 0.45); }

/* ---------------- de hertestlijn ---------------- *
 * Het enige zichtbare mechanisme. Een regel die deze lijn passeert is de
 * reden dat er links iets verandert; de flits bevestigt dat op het moment
 * zelf. Alleen de uiteinden staan er: een doorlopende lijn over
 * bewegende tekst leest onvermijdelijk als een doorhaling. */
.pt__scan {
	position: absolute;
	left: var(--pt-code-left);
	right: 0;
	top: calc(var(--pt-code-top) + var(--pt-scan-y));
	height: 1px;
	z-index: 2;
	pointer-events: none;
}
.pt__scan::before,
.pt__scan::after {
	content: "";
	position: absolute;
	top: 0;
	width: 26px;
	height: 1px;
	background-color: var(--pt-hair);
}
.pt__scan::before { left: 0;     transform-origin: left center; }
.pt__scan::after  { right: 24px; transform-origin: right center; }
.pt__scan[data-hit]::before,
.pt__scan[data-hit]::after { animation: pt-hit 900ms var(--pt-ease); }
@keyframes pt-hit {
	0%   { background-color: var(--pt-hair); transform: scaleX(1); }
	12%  { background-color: rgb(var(--pt-accent) / 0.9); transform: scaleX(1.8); }
	100% { background-color: var(--pt-hair); transform: scaleX(1); }
}

/* Zonder label leest de lijn als een scheiding. Eén woord maakt er een
 * mechanisme van. Het verloop-vlak houdt de code eronder weg zonder de
 * harde rechthoek die een gat in de regel sloeg. */
.pt__scan-label {
	position: absolute;
	right: 58px;
	top: calc(var(--pt-code-top) + var(--pt-scan-y) - 8px);
	z-index: 3;
	padding: 0 9px;
	background: linear-gradient(90deg, transparent, var(--pt-bg) 22%, var(--pt-bg) 78%, transparent);
	font-size: 11px;
	line-height: 16px;
	letter-spacing: 0.01em;
	color: rgb(142 155 196 / 0.55);
}

/* ---------------- het bevindingenblok ---------------- *
 * Kaal op het veld. Geen plaat, geen rand, geen scheidingslijn: de
 * afstand tussen de ondertitel en de lijst doet dat werk al, en elk
 * extra vlak was een element dat zichzelf niet kon verantwoorden. */
.pt__report {
	position: absolute;
	left: 24px;
	bottom: 24px;
	z-index: 3;
	width: min(416px, calc(100% - 48px));
	color: var(--pt-ink);
}

.pt__statement {
	font-size: 19px;
	font-weight: 550;
	letter-spacing: -0.024em;
	line-height: 1.18;
	transition: color 300ms ease;
}
.pt__statement .word,
.pt__meta .word { display: inline-block; white-space: nowrap; }
.pt__statement .ch,
.pt__meta .ch { display: inline-block; will-change: transform, opacity, filter; }
.pt__meta {
	margin-top: 6px;
	font-size: 13px;
	letter-spacing: -0.004em;
	line-height: 17px;
	color: var(--pt-ink-muted);
	font-variant-numeric: tabular-nums;
	white-space: nowrap;
}
.pt[data-state="done"] .pt__statement { color: var(--pt-success); }

/* 28 tegen 16 tussen de rijen: de narratie en de lijst moeten uit elkaar
 * vallen, anders leest de ondertitel als de eerste bevinding. */
.pt__list { margin-top: 28px; }
.pt__row { display: flex; align-items: flex-start; gap: 10px; }
.pt__row + .pt__row { margin-top: 16px; }

/* De glyph draagt de stand, en niets anders. Geen vlak, geen tint, geen
 * rand: alleen de vorm en de kleur. --pt-glyph-shift zet hem op de
 * kaphoogte van de naam in plaats van in het midden van de regelbox; dat
 * scheelt ruim drie pixels en is precies wat je ziet als het niet
 * klopt. Zie alignGlyph(). */
.pt__ico {
	position: relative;
	flex: none;
	width: var(--pt-ico);
	height: var(--pt-ico);
	margin-top: var(--pt-glyph-shift);
}
.pt__ico svg {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	fill: none;
	stroke: currentColor;
	stroke-width: 2;
	stroke-linecap: round;
	stroke-linejoin: round;
	opacity: 0;
	transform: scale(0.65);
	transition: opacity 220ms var(--pt-ease), transform 220ms var(--pt-ease);
}
.pt__row[data-status="open"]        .pt__ico { color: var(--pt-alert); }
.pt__row[data-status="gemitigeerd"] .pt__ico { color: var(--pt-ink-muted); }
.pt__row[data-status="opgelost"]    .pt__ico { color: var(--pt-success); }
.pt__row[data-status="open"]        .bang,
.pt__row[data-status="gemitigeerd"] .dash,
.pt__row[data-status="opgelost"]    .check {
	opacity: 1;
	transform: scale(1);
	transition-delay: 80ms;
}

/* Wat nog open staat weegt vol; wat af is trekt zich terug en krijgt de
 * streep. Die streep is een getekende lijn, geen text-decoration, want
 * text-decoration valt niet te animeren en dit moet dóórlopen op het
 * moment dat de coderegel de hertestlijn passeert. */
.pt__name {
	position: relative;
	display: inline-block;
	font-size: 15px;
	font-weight: 500;
	line-height: 21px;
	letter-spacing: -0.011em;
	color: var(--pt-ink);
	white-space: nowrap;
	transition: color 340ms var(--pt-ease);
}
.pt__name::after {
	content: "";
	position: absolute;
	left: -2px;
	right: -2px;
	top: calc(var(--pt-strike-y) - 0.5px);   /* het midden van de lijn op de x-hoogte, niet de bovenkant */
	height: 1px;
	background: currentColor;
	transform: scaleX(0);
	transform-origin: left center;
	transition: transform 420ms var(--pt-ease);
}
.pt__row[data-status="opgelost"] .pt__name {
	color: rgb(244 245 250 / 0.52);
}
.pt__row[data-status="opgelost"] .pt__name::after { transform: scaleX(1); }

/* ---------------- responsive ---------------- *
 * Onder 940 wordt de cel staand en past er geen kolom naast de tekst. De
 * code loopt dan over de volle breedte als band tussen de kaartkop en
 * het tekstblok. Beide randen worden gemeten, want vaste px klopten op
 * precies één breedte: op 800 slaat de kop om naar drie regels en liep
 * de code er op volle sterkte doorheen, en stond de hertestlijn buiten
 * een kolom die inmiddels korter was dan de lijnpositie, waardoor de
 * open laag volledig weggeknipt werd. 20 is de lucht onder de kop, 28 de
 * ondermarge van het blok plus lucht. max() zodat de calc nooit negatief
 * wordt op een lage kaart. */
@media (max-width: 939px) {
	.pt {
		--pt-code-left: 0px;
		--pt-code-top: calc(var(--pt-title-bottom) + 20px);
		--pt-code-h: max(0px, calc(100% - var(--pt-code-top) - var(--pt-report-h) - 28px));
		--pt-lh: 19px;

		/* De band ligt dichter op het oog dan de zijkolom op de hero, dus
		 * dezelfde alpha las daar als inhoud in plaats van als sfeer. */
		--pt-code-alpha: 0.22;
	}
	.pt__code {
		-webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 16px, #000 calc(100% - 26px), transparent 100%);
		        mask-image: linear-gradient(to bottom, transparent 0, #000 16px, #000 calc(100% - 26px), transparent 100%);
	}
	.pt__code-inner {
		-webkit-mask-image: linear-gradient(to right, #000 0, #000 calc(100% - 120px), transparent 100%);
		        mask-image: linear-gradient(to right, #000 0, #000 calc(100% - 120px), transparent 100%);
	}
	.pt__line { padding-left: 20px; }
	.pt__scan { left: 16px; right: 16px; }
	.pt__scan::after { right: 0; }
	.pt__scan-label { right: 34px; }

	.pt__report { left: 16px; right: 16px; bottom: 16px; width: auto; }
	.pt__statement { font-size: 17px; }
	.pt__meta { font-size: 12.5px; line-height: 16px; }
	.pt__list { margin-top: 20px; }
	.pt__row { gap: 9px; }
	.pt__row + .pt__row { margin-top: 14px; }
	.pt { --pt-ico: 13px; }
	.pt__name { font-size: 14px; line-height: 20px; }
}
@media (max-width: 639px) {
	.pt {
		--pt-code-top: calc(var(--pt-title-bottom) + 14px);
		--pt-code-h: max(0px, calc(100% - var(--pt-code-top) - var(--pt-report-h) - 24px));
		--pt-lh: 18px;
	}
	.pt__code {
		-webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 14px, #000 calc(100% - 20px), transparent 100%);
		        mask-image: linear-gradient(to bottom, transparent 0, #000 14px, #000 calc(100% - 20px), transparent 100%);
	}
	.pt__statement { font-size: 16px; }
	.pt__meta { font-size: 12px; }
	.pt__list { margin-top: 18px; }
	.pt__row { gap: 9px; }
	.pt__row + .pt__row { margin-top: 12px; }
	.pt__name { font-size: 13.5px; line-height: 19px; }
	.pt__scan-label { display: none; }
}

/* Ambient uit, en alles landt in de eindstand. mountPentest zet de strip
 * daarnaast op de fase waar alle vijf de bevindingen boven de lijn
 * liggen, zodat de stilstaande code de lijst niet tegenspreekt. */
@media (prefers-reduced-motion: reduce) {
	.pt__strip { animation: none; }
	.pt__scan[data-hit]::before,
	.pt__scan[data-hit]::after { animation: none; }
	.pt__ico svg, .pt__name, .pt__name::after { transition: none; }
}

/* ------------------------------------------------------------------ *
 * CISO-Advisors, de 1/3 kaart. Alles namespaced ciso-.
 *
 * Het onderwerp is een toegangspas die aan een keycord hangt. Dezelfde
 * persoon, dezelfde rol, een andere opdrachtgever. De pas draait om zijn
 * eigen koord en komt terug met een nieuwe uitgever; dat draaien is de
 * hele animatie.
 *
 * Vastgelegde waarden: de pas is 264 x 352 op de bento-cel van 397 x 572 en
 * begint daar op y=173; kop 134, koord 15 breed, ring 34. Op 272 x 368 stond
 * hij 26px van de onderrand en las de cel als volgeplakt. Slingertijd 4,4s,
 * heruitgifte om de twee slingers, dus 8,8s. De draai duurt 860ms.
 *
 * Op die cel loopt de kaartkop tot y=107 en tot x=218; daaronder komt het
 * koord pas op, dus de stille zone linksboven blijft leeg.
 *
 * Drie dingen die hier anders zijn dan bij de radar, met opzet:
 *
 * 1. Geen GSAP. De wissel zit in een 3D-draai, niet in tekst, dus er valt
 *    niets te staggeren. Zonder GSAP werkt dit bestand hetzelfde als ermee.
 * 2. Geen kleurinterpolatie op de pas. Twee merkkleuren door elkaar faden
 *    gaat via een vuile tussenkleur (blauw naar rood wordt onderweg paars).
 *    De twee gezichten staan rug aan rug met backface-visibility: hidden,
 *    dus op 90 graden wisselt de browser zelf van gezicht op het moment dat
 *    de pas nul pixels breed is. Er bestaat geen frame met twee kleuren of
 *    twee woordmerken op één pas.
 * 3. Dit verving een veeg van boven naar beneden. Die had wél zo'n frame:
 *    ~50ms lang stond er een halve oude en een halve nieuwe naam. Bevroren
 *    las dat als kapotte tekst.
 * ------------------------------------------------------------------ */

.ciso {
	position: absolute;
	inset: 0;
	overflow: hidden;
	background: var(--ciso-bg);

	/* Maakt cqw de cel. Zonder dit valt hij stil terug op de viewport en
	 * meet de pas zich aan het scherm op in plaats van aan de kaart. */
	container-type: size;

	--ciso-bg: #050B1F;
	--ciso-ink: #F4F5FA;
	--ciso-ink-muted: #8E9BC4;
	--ciso-hair: rgb(140 160 255 / 0.20);

	--ciso-sway: 4400ms;
	--ciso-turn: 860ms;

	/* ------------------------------------------------------------------ *
	 * Maatvoering. Eén hoogte stuurt alles.
	 *
	 * De cel is bij lange na niet altijd 400 x 576. Uit services-bento.html
	 * gemeten, met de module er echt in gehangen:
	 *
	 *   viewport 1440 -> 397 x 572   (een derde)
	 *   viewport  800 -> 360 x 527   (halve breedte, aspect 344/504)
	 *   viewport  600 -> 552 x 576   (volle breedte, geknipt op max-height)
	 *
	 * Die laatste is breder dan hoog. Vaste px per breekpunt liep daar mis:
	 * op 800 stak de pas 11px onder de cel uit en op 640 was hij breder dan
	 * de kaart. Daarom is de pas hier één object dat meeschaalt, precies
	 * zoals de band in awareness-module dat doet. Een pas is een voorwerp;
	 * die hoort te schalen. De radar-regel over een eigen uitsnede per
	 * breekpunt gaat over een paneel met UI-tekst, en dat is dit niet.
	 *
	 * 62cqh met een plafond van 352px: op de bento-cel van 572 raakt hij dat
	 * plafond, daaronder krimpt hij mee. De breedte volgt uit de hoogte,
	 * 0.75 is 264/352. De cqw-grens is een vangrail voor een cel die smaller
	 * uitvalt dan verwacht.
	 *
	 * De bovenkant is niet vast maar het midden van wat er overblijft onder
	 * de stille zone. Zo zakt de pas niet scheef in het veld als de cel van
	 * vorm verandert.
	 * ------------------------------------------------------------------ */
	--ciso-quiet: min(178px, 30cqh);
	--ciso-hem: 8cqh;
	--ciso-badge-h: min(352px, 62cqh);
	--ciso-badge-w: min(calc(var(--ciso-badge-h) * 0.75), 74cqw);
	--ciso-badge-top: calc(var(--ciso-quiet)
		+ (100cqh - var(--ciso-quiet) - var(--ciso-hem) - var(--ciso-badge-h)) / 2);

	/* Alles op de pas is een vaste verhouding van die hoogte, gemeten op de
	 * 352 waar het ontwerp op is uitgetekend. */
	--ciso-head-h: calc(var(--ciso-badge-h) * 0.381);
	--ciso-pad:    calc(var(--ciso-badge-h) * 0.0568);
	--ciso-cord-w: calc(var(--ciso-badge-h) * 0.0426);
	--ciso-ring:   calc(var(--ciso-badge-h) * 0.0966);
	--ciso-radius: calc(var(--ciso-badge-h) * 0.0284);
}

/* ------------------------------------------------------------------ *
 * Het veld. Vier lagen die samen diepte maken zonder het veld op te
 * lichten: het blijft #050B1F-familie en het blijft blauw.
 *
 * De richting klopt met de pas: het licht komt van boven het beeld, en
 * dat is dezelfde hoek als de 0 28px 72px worp onder de pas. Een gloed
 * van onderaf zou de schaduw laten liegen.
 * ------------------------------------------------------------------ */
.ciso__field {
	position: absolute;
	inset: 0;
	z-index: 0;
	background:
		/* koel strijklicht van boven het kader */
		radial-gradient(76% 40% at 50% -8%, rgb(126 164 255 / 0.11) 0%, rgb(126 164 255 / 0) 66%),
		/* de bodem loopt weg in het donker, waardoor de pas voor de wand hangt */
		radial-gradient(124% 66% at 50% 114%, rgb(6 16 48 / 0.85) 0%, rgb(6 16 48 / 0) 60%),
		linear-gradient(180deg, #071129 0%, #050B1F 46%, #03060F 100%);
}

/* Twee rasters over elkaar, 44 en 176. Eén raster leest als ruitjespapier;
 * twee niveaus lezen als een wand met stramien, en dat is waar de pas voor
 * hangt. Datasheet-linework in dezelfde familie als de radar, maar niet
 * dezelfde vorm: concentrische ringen zouden de radar naschrijven.
 *
 * Het masker houdt het raster weg bij de kaartkop linksboven, waar het als
 * ruis onder tekst zou liggen. */
.ciso__grid {
	position: absolute;
	inset: 0;
	z-index: 0;
	background-image:
		linear-gradient(to right,  rgb(150 175 255 / 0.085) 1px, transparent 1px),
		linear-gradient(to bottom, rgb(150 175 255 / 0.085) 1px, transparent 1px),
		linear-gradient(to right,  rgb(140 160 255 / 0.042) 1px, transparent 1px),
		linear-gradient(to bottom, rgb(140 160 255 / 0.042) 1px, transparent 1px);
	background-size: 176px 176px, 176px 176px, 44px 44px, 44px 44px;
	background-position: center 8px;
	-webkit-mask-image: radial-gradient(100% 64% at 50% 70%, #000 16%, transparent 86%);
	mask-image: radial-gradient(100% 64% at 50% 70%, #000 16%, transparent 86%);
}

/* Twee gloedlagen die om en om aan staan. Elke heruitgifte zet de kleur op
 * de laag die uit staat en wisselt daarna de opacity, dus er is nooit een
 * commit-dans nodig en de oude kleur hangt nooit onder de nieuwe.
 *
 * De vertraging is er zodat de wand pas van tint wisselt als de pas al aan
 * het draaien is. Zonder die 260ms verkleurt de kamer voordat de nieuwe pas
 * te zien is, en dan loopt het veld op het beeld vooruit. */
.ciso__glow {
	position: absolute;
	inset: 0;
	z-index: 0;
	opacity: 0;
	transition: opacity 780ms cubic-bezier(0.4, 0, 0.2, 1) 260ms;
	background:
		radial-gradient(46% 28% at 50% 60%,
			rgb(var(--ciso-glow-rgb) / 0.30) 0%,
			rgb(var(--ciso-glow-rgb) / 0.08) 44%,
			rgb(var(--ciso-glow-rgb) / 0) 74%),
		radial-gradient(96% 62% at 50% 56%,
			rgb(var(--ciso-glow-rgb) / 0.10) 0%,
			rgb(var(--ciso-glow-rgb) / 0) 72%);
}

/* Boven de gloed, zodat ook de gloed naar de randen wegvalt. Dit is de
 * grootste winst in diepte en het kost geen enkel lichtpunt: het veld
 * wordt er alleen donkerder van. */
.ciso__vignette {
	position: absolute;
	inset: 0;
	z-index: 0;
	background: radial-gradient(114% 76% at 50% 40%, rgb(2 4 12 / 0) 42%, rgb(2 4 12 / 0.66) 100%);
}

/* feTurbulence over het hele veld. Een verloop van #071129 naar #03060F
 * over 576px bandeert zichtbaar op 8-bit schermen; ruis breekt de banden.
 * Staat boven het veld en onder de pas. */
.ciso__grain {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	z-index: 1;
	opacity: 0.035;
	pointer-events: none;
}

/* ------------------------------------------------------------------ *
 * Het koord en wat eraan hangt.
 *
 * Twee geneste draaipunten, allebei op 50% 0, dus op de bovenrand van de
 * cel. Dat is waar het koord het beeld uit loopt, en daarmee het enige
 * eerlijke scharnier: een pas aan een koord draait om de nek, niet om
 * zichzelf. De onderkant van de pas legt daardoor meer weg af dan de
 * bovenkant, precies zoals het hoort.
 *
 * Twee elementen en niet één, omdat er twee bewegingen overheen moeten:
 * de trage slinger die altijd loopt, en de duw die de draai eraan geeft.
 * Eén element kan maar één transform animeren.
 * ------------------------------------------------------------------ */
.ciso__hanger,
.ciso__swing {
	position: absolute;
	inset: 0;
	z-index: 2;
	transform-origin: 50% 0;
}

/* Rust op 0%, 50% en 100%, niet op de uitersten. Twee redenen: de bench
 * kan de slinger dan op delay 0 pauzeren en krijgt gegarandeerd een pas die
 * recht hangt, en de ritmedraad heeft een doorgang midden in de cyclus om
 * op te mikken in plaats van precies op de naad van de herhaling. */
@keyframes ciso-sway {
	0%   { transform: rotate(0deg); }
	25%  { transform: rotate(0.75deg); }
	50%  { transform: rotate(0deg); }
	75%  { transform: rotate(-0.75deg); }
	100% { transform: rotate(0deg); }
}
.ciso__hanger { animation: ciso-sway var(--ciso-sway) ease-in-out infinite; }

/* Gedempte naslinger na de draai. 2,4s, ruim langer dan de draai zelf,
 * zodat de pas nog even natrilt als hij al weer stilstaat. */
@keyframes ciso-nudge {
	0%   { transform: rotate(0deg); }
	16%  { transform: rotate(-1.45deg); }
	42%  { transform: rotate(0.95deg); }
	64%  { transform: rotate(-0.5deg); }
	84%  { transform: rotate(0.2deg); }
	100% { transform: rotate(0deg); }
}
.ciso__swing.is-nudging { animation: ciso-nudge 2400ms ease-in-out both; }

/* Het lint. Loopt tot de bovenrand van de cel en verdwijnt onderweg in het
 * donker, want de kaartkop staat daar. Volledig doorgetrokken liep het
 * dwars achter "een CISO in dienst" langs. Vanaf y=0 tot ~46% is het niets;
 * pas onder de kop komt het op. */
.ciso__cord {
	position: absolute;
	top: 0;
	left: 50%;
	width: var(--ciso-cord-w);
	height: calc(var(--ciso-badge-top) - var(--ciso-badge-h) * 0.0568);
	margin-left: calc(var(--ciso-cord-w) / -2);
	background:
		linear-gradient(90deg,
			rgb(0 0 0 / 0.55) 0%,
			rgb(255 255 255 / 0.055) 34%,
			rgb(255 255 255 / 0.02) 62%,
			rgb(0 0 0 / 0.45) 100%),
		#131C36;
	-webkit-mask-image: linear-gradient(to bottom, transparent 0%, transparent 46%, #000 96%);
	mask-image: linear-gradient(to bottom, transparent 0%, transparent 46%, #000 96%);
}

/* De vouw waar het lint om de ring is gestikt. Zonder dit stoten lint en
 * ring op elkaar en leest het als twee losse dingen. */
.ciso__fold {
	position: absolute;
	left: 50%;
	top: calc(var(--ciso-badge-top) - var(--ciso-badge-h) * 0.0795);
	width: calc(var(--ciso-badge-h) * 0.0597);
	height: calc(var(--ciso-badge-h) * 0.0483);
	margin-left: calc(var(--ciso-badge-h) * -0.0298);
	border-radius: 3px;
	background: linear-gradient(180deg, #26314F 0%, #151E3A 100%);
	box-shadow: inset 0 1px 0 rgb(255 255 255 / 0.12), 0 1px 2px rgb(0 0 0 / 0.5);
}

/* De ring hangt in de sleuf. Hij wordt achter de pas getekend, dus wat je
 * ziet is de boog boven de bovenrand; de onderkant van de ring valt precies
 * op de sleuf en het oog maakt het af. Een ring die er echt doorheen steekt
 * zou twee half doorzichtige bogen kosten voor 5px winst.
 *
 * Het masker maakt de schijf hol. Een border zou werken maar kan geen
 * verloop dragen, en zonder verloop ziet metaal er vlak uit. */
.ciso__ring {
	position: absolute;
	left: 50%;
	top: calc(var(--ciso-badge-top) + var(--ciso-badge-h) * 0.0504 - var(--ciso-ring));
	width: var(--ciso-ring);
	height: var(--ciso-ring);
	margin-left: calc(var(--ciso-ring) / -2);
	border-radius: 50%;
	background: linear-gradient(155deg, #B4BECF 0%, #7C87A0 42%, #49526A 100%);
	-webkit-mask-image: radial-gradient(circle at 50% 50%,
		transparent calc(var(--ciso-ring) * 0.394), #000 calc(var(--ciso-ring) * 0.400));
	mask-image: radial-gradient(circle at 50% 50%,
		transparent calc(var(--ciso-ring) * 0.394), #000 calc(var(--ciso-ring) * 0.400));
}

/* ------------------------------------------------------------------ *
 * De pas.
 *
 * De doos draait, de twee gezichten staan er rug aan rug in. perspective
 * staat op de transform zelf en niet op een voorouder, zodat het vluchtpunt
 * het hart van de pas is en niet het hart van de cel; anders kijk je de pas
 * scheef van bovenaf in.
 *
 * Geen overflow: hidden en geen schaduw op deze doos. Allebei zetten ze in
 * Chrome transform-style terug naar flat, en dan valt de hele 3D-opbouw uit
 * elkaar. Radius, achtergrond en schaduw zitten daarom op de gezichten.
 * ------------------------------------------------------------------ */
.ciso__badge {
	position: absolute;
	left: 50%;
	top: var(--ciso-badge-top);
	/* De cqw-grens is een vangrail, geen ontwerpmaat: op de cellen die de
	 * bento echt uitdeelt (397, 402 en 342 breed) bijt hij nooit. Hij is er
	 * voor de cel die smaller uitvalt dan verwacht. */
	width: var(--ciso-badge-w);
	height: var(--ciso-badge-h);
	margin-left: calc(var(--ciso-badge-w) / -2);
	transform-style: preserve-3d;
	transform: perspective(1600px) rotateY(0deg);
}

/* De middenstukken zijn de snelste. Vlak bij 90 graden is de pas een dunne
 * wig en dat is de enige stand die er raar uitziet als je hem bevriest, dus
 * daar wordt doorheen gegaan: van 70 naar 116 graden duurt ~100ms. Aan het
 * eind een kleine doorslag en terug, zoals iets dat uitzwaait. */
@keyframes ciso-turn {
	0%   { transform: perspective(1600px) rotateY(0deg); }
	18%  { transform: perspective(1600px) rotateY(22deg); }
	38%  { transform: perspective(1600px) rotateY(70deg); }
	50%  { transform: perspective(1600px) rotateY(116deg); }
	70%  { transform: perspective(1600px) rotateY(158deg); }
	87%  { transform: perspective(1600px) rotateY(184.5deg); }
	95%  { transform: perspective(1600px) rotateY(178.6deg); }
	100% { transform: perspective(1600px) rotateY(180deg); }
}
/* De pauze loopt via custom properties en niet via een inline style, omdat
 * ::after niet inline te stylen is en de schaduwlaag dus anders vrolijk
 * doorloopt terwijl de draai stilstaat. Elke bench-opname liet daardoor een
 * pas zien die wel dwars stond maar niet donker was. Custom properties
 * erven wel in een pseudo-element, dus dit houdt de twee animaties in de
 * pas. De longhands moeten ná de shorthand, die zet ze anders terug. */
.ciso__badge.is-turning {
	animation: ciso-turn var(--ciso-turn) linear both;
	animation-play-state: var(--ciso-play, running);
	animation-delay: var(--ciso-delay, 0ms);
}

/* Het lichte vlak op het donkere veld. Het recept uit .rdr__panel,
 * ongewijzigd: een halve pixel lichtrand, een strakke contactschaduw en een
 * brede zachte worp. Eén blur in plaats hiervan haalt de pas van het veld
 * af.
 *
 * backface-visibility doet het echte werk: op 90 graden is de pas nul
 * pixels breed en wisselt de browser daar zelf van gezicht. */
.ciso__face {
	position: absolute;
	inset: 0;
	display: flex;
	flex-direction: column;
	overflow: hidden;
	border-radius: var(--ciso-radius);
	background: linear-gradient(180deg, #FFFFFF 0%, #FCFCFE 100%);
	box-shadow: 0 0 0 0.5px rgb(255 255 255 / 0.10),
	            0 2px 6px rgb(0 0 0 / 0.30),
	            0 28px 72px -24px rgb(0 0 0 / 0.85);
	-webkit-backface-visibility: hidden;
	backface-visibility: hidden;

	/* De pas neemt het lichte palet mee. Zonder dit erft hij de donkere
	 * inkt en drukt hij bijna-wit op wit. */
	--ciso-ink: #18181B;
	--ciso-ink-muted: #71717A;
	--ciso-hair: #EDEDF2;
	color: var(--ciso-ink);
}
.ciso__face--b { transform: rotateY(180deg); }

/* Schaduw op de pas zelf terwijl hij draait. Zonder dit blijft het witte
 * lijf op 90 graden vol wit en staat er een lichtgevend mesje in beeld; het
 * enige frame van de hele draai dat er bevroren raar uitziet.
 *
 * De waarden volgen 1 - |cos t|, dus gewoon hoeveel licht een vlak vangt dat
 * van de lichtbron af draait. Op 0 en 180 graden vangt de pas alles en is de
 * laag weg; rond 90 graden vangt hij niets. Beide gezichten krijgen hem,
 * maar er is er altijd maar één zichtbaar.
 *
 * Wel afgetopt op 0.50 in plaats van op de volle Lambert. Op de rekenwaarde
 * (0.66 bij 70 graden) werd het witte lijf halverwege de draai grijs en las
 * het als wegfaden in plaats van als wegdraaien. Een echte kamer heeft ook
 * strooilicht; dit is dat strooilicht. */
.ciso__face::after {
	content: "";
	position: absolute;
	inset: 0;
	border-radius: var(--ciso-radius);
	background: #050B1F;
	opacity: 0;
	pointer-events: none;
}
@keyframes ciso-shade {
	0%   { opacity: 0; }
	18%  { opacity: 0.02; }
	38%  { opacity: 0.22; }
	44%  { opacity: 0.50; }
	50%  { opacity: 0.20; }
	70%  { opacity: 0.02; }
	100% { opacity: 0; }
}
.ciso__badge.is-turning .ciso__face::after {
	animation: ciso-shade var(--ciso-turn) linear both;
	animation-play-state: var(--ciso-play, running);
	animation-delay: var(--ciso-delay, 0ms);
}

/* ---------------- kop van de pas ---------------- */
.ciso__head {
	position: relative;
	flex: none;
	height: var(--ciso-head-h);
	background: var(--ciso-brand);
}

/* De sleuf. Eén element, en het enige dat dit object onmiskenbaar een
 * draagpas maakt in plaats van een visitekaartje of een bankpas. Nu hangt
 * er ook echt iets in. */
.ciso__slot {
	position: absolute;
	top: calc(var(--ciso-badge-h) * 0.0426);
	left: 50%;
	transform: translateX(-50%);
	width: calc(var(--ciso-badge-h) * 0.1136);
	height: calc(var(--ciso-badge-h) * 0.0156);
	border-radius: 3px;
	background: rgb(0 0 0 / 0.28);
	box-shadow: inset 0 1px 1.5px rgb(0 0 0 / 0.32), 0 1px 0 rgb(255 255 255 / 0.16);
}

/* Logo linksonder in de kop, op dezelfde 20px als het lijf eronder. */
.ciso__lockup {
	position: absolute;
	left: var(--ciso-pad);
	bottom: calc(var(--ciso-badge-h) * 0.0682);
	display: flex;
	align-items: center;
	gap: calc(var(--ciso-badge-h) * 0.0313);
	color: #FFFFFF;
}
.ciso__mark {
	flex: none;
	width: calc(var(--ciso-badge-h) * 0.0739);
	height: calc(var(--ciso-badge-h) * 0.0739);
	display: block;
	fill: currentColor;
}
.ciso__wordmark {
	font-size: calc(var(--ciso-badge-h) * 0.044);
	font-weight: 600;
	letter-spacing: 0.05em;
	line-height: 1;
	text-transform: uppercase;
	white-space: nowrap;
}

/* ---------------- lijf van de pas ---------------- */
.ciso__body {
	flex: 1;
	display: flex;
	flex-direction: column;
	padding: 0 var(--ciso-pad) calc(var(--ciso-badge-h) * 0.0511);
}

/* margin auto boven en onder: het naam/rol-blok staat optisch midden in
 * het witte lijf, met gelijke lucht erboven en eronder. Op een vaste marge
 * zakte het telkens naar de haarlijn toe. */
.ciso__who { margin: auto 0; }
.ciso__name {
	font-size: calc(var(--ciso-badge-h) * 0.0455);
	font-weight: 550;
	letter-spacing: -0.012em;
	line-height: 1.2;
	color: var(--ciso-ink);
}

/* De rol is het grootste op de pas, want de rol is waar de kaart over gaat,
 * en hij staat in de merkkleur van de opdrachtgever. Dat is de tweede plek
 * waar de tint landt, zodat de wissel ook halverwege de pas te zien is en
 * niet alleen in de kop. */
.ciso__role {
	margin-top: calc(var(--ciso-badge-h) * 0.0085);
	font-size: calc(var(--ciso-badge-h) * 0.1023);
	font-weight: 600;
	letter-spacing: -0.035em;
	line-height: 1.02;
	color: var(--ciso-brand);
}

.ciso__foot {
	flex: none;
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: calc(var(--ciso-badge-h) * 0.0341);
	padding-top: calc(var(--ciso-badge-h) * 0.0341);
	border-top: 1px solid var(--ciso-hair);
	font-size: calc(var(--ciso-badge-h) * 0.0341);
	letter-spacing: -0.002em;
	line-height: 1.2;
}
.ciso__kind { color: var(--ciso-ink-muted); }
/* tabular-nums, anders springt het nummer bij elke heruitgifte in breedte. */
.ciso__no {
	color: var(--ciso-ink);
	font-weight: 500;
	font-variant-numeric: tabular-nums;
}

/* ------------------------------------------------------------------ *
 * Bewegingsarm.
 *
 * Geen slinger, geen duw, geen draai. De pas hangt stil aan het koord op
 * de eerste uitgever. De ritmedraad in ciso-module.js hangt aan de slinger
 * en start daar niet, dus er valt ook niets te heruitgeven.
 * ------------------------------------------------------------------ */
@media (prefers-reduced-motion: reduce) {
	.ciso__hanger,
	.ciso__swing.is-nudging,
	.ciso__badge.is-turning { animation: none; }
	.ciso__badge { transform: perspective(1600px) rotateY(0deg); }
}

/* ------------------------------------------------------------------ *
 * Kleinere cellen.
 *
 * De maatvoering hierboven regelt zichzelf, dus hier staat alleen wat niet
 * uit de hoogte van de pas volgt: het fijne raster is op een kleine cel
 * meer ruis dan textuur, en het grove niveau plus de gloed dragen het veld
 * daar prima.
 * ------------------------------------------------------------------ */
@media (max-width: 639px) {
	.ciso__grid {
		background-image:
			linear-gradient(to right,  rgb(150 175 255 / 0.075) 1px, transparent 1px),
			linear-gradient(to bottom, rgb(150 175 255 / 0.075) 1px, transparent 1px);
		background-size: 176px 176px, 176px 176px;
	}
}

/* ------------------------------------------------------------------ *
 * Cyberverzekering — het insured badge op een honingraat. De 1/3 kaart.
 *
 * Alles is ins- genamespaced zodat het zonder botsingen in de bento valt.
 * De tuning bench staat in insurance-bench.html; dit bestand draagt alleen
 * het uitgewerkte ontwerp.
 *
 * Eén echt badge in het midden, spookbadges eromheen als dunne contouren
 * op een honingraatrooster met ruimte ertussen, uitgefade richting de
 * randen. Verder niets: geen tekst, geen status, geen teller. De enige
 * letters op de kaart zijn die in het artwork zelf.
 *
 * Het artwork is img/insured-badge.svg en gaat er ongewijzigd in, als
 * <img>. Niet inline: dan botsen de gradient-id's zodra de kaart twee keer
 * op een pagina staat, en dan zou de module aan zijn kleuren kunnen komen.
 * Dat mag niet, het is andermans tekening.
 *
 * Maatvoering hangt aan één waarde, --ins-badge-w. De zeshoek in het
 * artwork is regelmatig en staand (punt boven en onder, vlakke zijkanten),
 * dus bij een gerenderde breedte W is de zeshoek 0,86603W breed en precies
 * W hoog. Het rooster rekent in eenheden van W, zodat spoken en badge
 * altijd exact dezelfde zeshoek zijn, op elke celbreedte, zonder JS.
 * ------------------------------------------------------------------ */

/* De halo van het masker is één geanimeerd percentage. Geregistreerd,
 * anders interpoleert een custom property niet maar klapt hij op 50% om. */
@property --ins-halo {
	syntax: "<percentage>";
	inherits: true;
	initial-value: 34%;
}

.ins,
.ins *,
.ins *::before,
.ins *::after { box-sizing: border-box; }

.ins {
	position: absolute;
	inset: 0;
	overflow: hidden;
	background: var(--ins-bg);

	/* Hiermee meten cqw en cqh de cel in plaats van stilletjes terug te
	 * vallen op de viewport. De badge schaalt op cqw, dus zonder dit staat
	 * hij op de bento-pagina ineens op viewportbreedte. */
	container-type: size;

	--ins-bg: #050B1F;
	--ins-field: 106 159 255;

	/* De contour van de spoken is de onderkleur van de stroke-gradient in
	 * het artwork (#91AAC2 -> #D2E9FF), iets opgelicht. Een spook is
	 * daarmee letterlijk hetzelfde badge, alleen niet aan. */
	--ins-ghost: 178 204 224;

	/* 38cqw houdt het badge op ~151px in de 397-cel, waar het kleine
	 * regeltje "verified 27/07/2026 / vallem.com/assured" nog leesbaar is.
	 * Onder 120px valt dat weg en wordt het artwork een vlek; boven 168px
	 * loopt de honingraat leeg omdat er nog maar één ring spoken past. */
	--ins-badge-w: clamp(120px, 38cqw, 168px);

	/* Het optische midden zit onder het geometrische. De kaarttitel loopt
	 * over drie regels tot y=107 en die ruimte is niet van ons. */
	--ins-cy: 55%;

	/* Waar de titel staat gaat het patroon helemaal uit. De radiale fade
	 * alleen liet daar nog lijnen door de letters lopen; dit is een
	 * kaart-eis, geen radiaal verschijnsel, dus krijgt hij zijn eigen
	 * masker. Schoon tot 96px, vol vanaf 190px. */
	--ins-quiet-top: 96px;
	--ins-quiet-ramp: 190px;
}

/* Het gezicht van het badge is #080808 op een veld van #050B1F, dus zonder
 * iets eronder zweeft het niet, het verdwijnt. Dezelfde blauwe waas als het
 * radarveld, alleen strak om het badge heen. */
.ins__field {
	position: absolute;
	inset: 0;
	background: radial-gradient(62% 42% at 50% var(--ins-cy),
		rgb(var(--ins-field) / 0.12) 0%,
		rgb(var(--ins-field) / 0.045) 44%,
		rgb(var(--ins-field) / 0) 74%);
}

/* ---------------- de honingraat ---------------- *
 * Twee lagen met een reden. Het masker moet vervagen ten opzichte van de
 * randen van de CEL, dus het zit op deze wrapper, die precies de cel is.
 * Het rooster zelf is groter dan de cel en zit erbinnen; wat buiten de
 * wrapper valt maskeert vanzelf weg, want mask-repeat staat op no-repeat.
 */
.ins__grid {
	position: absolute;
	inset: 0;

	/* Twee maskers die elkaar snijden. De radiale laat het patroon naar de
	 * randen oplossen in plaats van afgesneden worden; de lineaire houdt de
	 * titelzone leeg. Intersect, want beide moeten waar zijn. */
	--ins-mask:
		radial-gradient(closest-side at 50% var(--ins-cy),
			#000 0%,
			#000 var(--ins-halo),
			rgb(0 0 0 / 0.30) calc(var(--ins-halo) + 28%),
			transparent calc(var(--ins-halo) + 54%)),
		linear-gradient(to bottom,
			transparent 0,
			transparent var(--ins-quiet-top),
			#000 var(--ins-quiet-ramp));

	-webkit-mask-image: var(--ins-mask);
	mask-image: var(--ins-mask);
	-webkit-mask-repeat: no-repeat;
	mask-repeat: no-repeat;
	-webkit-mask-composite: source-in;
	mask-composite: intersect;

	/* De enige beweging op de kaart. De halo zwelt en zakt, dus de buitenste
	 * ring spoken komt langzaam op en weer weg. 22s: bij 12s las het als
	 * ademen van iets levends, hier moet het als weer lezen. Dit is de
	 * stilste van de vijf kaarten en dat moet zo blijven. */
	animation: ins-halo 22s ease-in-out infinite;
}
@keyframes ins-halo {
	0%, 100% { --ins-halo: 30%; }
	50%      { --ins-halo: 46%; }
}

.ins__lattice {
	position: absolute;
	left: 50%;
	top: var(--ins-cy);
	/* 7 badgebreedtes is ruim genoeg voor elke celvorm die de bento
	 * uitdeelt, ook de brede lage onder 640px. */
	width: calc(var(--ins-badge-w) * 7);
	height: calc(var(--ins-badge-w) * 7);
	transform: translate(-50%, -50%);
	display: block;
	overflow: visible;
}
.ins__lattice path {
	fill: none;
	stroke: rgb(var(--ins-ghost) / 0.38);
	stroke-width: 1;
	stroke-linejoin: round;
}

/* ---------------- het badge ---------------- */
.ins__badge {
	position: absolute;
	left: 50%;
	top: var(--ins-cy);
	transform: translate(-50%, -50%);
	width: var(--ins-badge-w);
	height: auto;
	display: block;
	pointer-events: none;
	-webkit-user-drag: none;
}

/* ---------------- reduced motion ---------------- *
 * De halo blijft staan op het midden van zijn slag, zodat de kaart er
 * precies uitziet als op een willekeurig moment in de animatie.
 */
@media (prefers-reduced-motion: reduce) {
	.ins__grid { animation: none; --ins-halo: 36%; }
}

/* Onder de bento-breakpoints verandert de cel van vorm: op 640-939 smaller
 * en hoger, onder 640 breed en laag. De badge staat dan op zijn clamp en
 * het rooster volgt hem, dus er valt alleen het optische midden bij te
 * stellen: op een lage brede cel is er minder ruimte onder de titel. */
@media (max-width: 639px) {
	.ins { --ins-cy: 57%; }
}
/* === EINDE dienstenbento === */
