/**
 * Kezzler theme — global frontend styles.
 *
 * Supplemental CSS for classes applied via patterns and block markup
 * that cannot be expressed through theme.json alone.
 */

/* ── Global icon assets ─────────────────────────────────────────────────
 * Single source of truth for icons used across multiple block types.
 * Each consumer paints the icon via `mask-image: var(...)` +
 * `background: currentColor` so the icon colour follows the local
 * text colour automatically. Adding a new icon = drop the SVG in
 * `assets/icons/`, add a CSS custom property here, reference it from
 * the block style.
 */
:root {
	--kezzler-icon-arrow-right: url( '../icons/arrow-right.svg' );
	--kezzler-icon-scanbuy-s:   url( '../images/scanbuy-icon.svg' );
}

/* ── kezzler/icon — mask rendering ────────────────────────────────────
 * The block's `--mask` render mode paints an SVG via `mask-image` +
 * `background-color: currentColor` (or theme palette slug) so the
 * icon inherits the local text colour.
 *
 * Two URL sources, one binding:
 *   1. Curated presets        → asset URL lives in `:root` above as
 *      `--kezzler-icon-{slug}`. The per-preset rule below re-binds
 *      `--kezzler-icon-mask` (the var `mask-image` actually reads) to
 *      point at the matching asset. The block's render.php only emits
 *      a class + size + colour inline — no URL.
 *   2. Author-uploaded icons  → render.php emits inline
 *      `--kezzler-icon-mask: url(...)` with an absolute URL.
 *
 * All `.kezzler-icon--mask` styling lives HERE (not in the block's
 * stylesheet) because relative `url('../images/...')` paths inside
 * CSS custom properties resolve relative to the stylesheet that
 * substitutes the var — keeping declaration, binding, and consumer
 * in one file guarantees correct resolution.
 *
 * To add a new preset: declare the asset var in `:root` above + add
 * one binding line here.
 */
.kezzler-icon--mask {
	-webkit-mask-image:    var( --kezzler-icon-mask );
	mask-image:            var( --kezzler-icon-mask );
	-webkit-mask-position: center center;
	mask-position:         center center;
	-webkit-mask-repeat:   no-repeat;
	mask-repeat:           no-repeat;
	-webkit-mask-size:     contain;
	mask-size:             contain;
}

.kezzler-icon--mask.has-preset-scanbuy-s {
	--kezzler-icon-mask: var( --kezzler-icon-scanbuy-s );
}

/* ── Global box-sizing reset ────────────────────────────────────────────────
 * Every element calculates width + height including padding + border. The
 * modern default that every block, pattern, and inline style assumes
 * (Bootstrap, Tailwind, every CSS reset since 2015 ships this). Without it,
 * `width: 100%` + `padding: 24px` overflows by 48px — the bug that
 * surfaced on solution-cards mobile cards.
 *
 * Individual elements may still override to `content-box` when they need
 * the legacy behavior (rare — none currently in this codebase).
 */
*,
*::before,
*::after {
	box-sizing: border-box;
}

/* ── First-child margin reset ───────────────────────────────────────────────
 * The first child of any container never needs a top margin — the container's
 * own padding supplies the leading gap. Universal rule with single-element
 * specificity (0,0,1,0) so any class, ID, or inline style overrides cleanly.
 *
 * Override examples:
 *   .my-block > .my-first { margin-top: 1rem; }          ← class wins
 *   style="margin-top: 1rem"                              ← inline wins
 *   :is(article) > :first-child { margin-top: 2rem; }    ← higher specificity wins
 */
*:first-child {
	margin-top: 0;
}

/* ── Component dimension tokens ─────────────────────────────────────────────
 * Project-level sizing primitives that don't fit the WP spec presets
 * (spacing presets are for layout gaps, not widget dimensions). Block
 * stylesheets read these via `var(--kezzler-*)`; override per block by
 * setting the var locally on a parent selector.
 */
:root {
	--kezzler-card-fixed-height: 351px;
	--kezzler-dot-size:           16px;
	--kezzler-icon-size:          48px;
}

/* ── Carousel right-bleed utility ───────────────────────────────────────────
 * Common pattern: place a carousel inside an alignfull section with a left
 * gutter (heading column or padding) and let the track flow off the right
 * viewport edge — content "appears from off-screen" instead of from a
 * container boundary. Apply `.is-right-bleed` to the carousel's inner
 * container (sibling of the heading). Section must be alignfull / 100vw.
 *
 * For marquees, fade the LEFT edge with a mask-image to soften where
 * logos emerge from behind the heading; right side is naturally clipped
 * by the viewport.
 */
.is-right-bleed {
	padding-inline-start: var( --wp--preset--spacing--margin );
	padding-inline-end: 0;
	overflow: hidden;
}

/* ── Sticky navbar (parts/header.html) ──────────────────────────────────────
 * The header template-part is fixed at top so the pill floats above content.
 * top uses --wp-admin--admin-bar--height so the pill clears the WP admin
 * toolbar when logged in (32px desktop / 46px mobile). Defaults to 0px.
 *
 * Structure (composed of core blocks — no custom block):
 *   header.wp-block-template-part
 *     > .site-header              (constrained centering wrapper)
 *       > .site-header-pill       (the floating pill itself — flex row)
 *         > .wp-block-site-logo
 *         > nav.wp-block-navigation.site-header-pill__nav
 *         > .wp-block-buttons.site-header-pill__cta
 */
header.wp-block-template-part {
	position: fixed;
	top: var( --wp-admin--admin-bar--height, 0px );
	left: 0;
	right: 0;
	z-index: 100;
	pointer-events: none; /* let clicks pass through the transparent margin */
}

header.wp-block-template-part > .site-header {
	pointer-events: auto; /* but the actual header block is interactive */
}

/* The floating pill itself.
 *
 * Glass effect (background + blur + border) lives on a pseudo-element so the
 * pill itself doesn't have backdrop-filter. backdrop-filter creates a
 * containing block for position:fixed descendants — applying it directly to
 * the pill traps core/navigation's mobile overlay inside the pill instead
 * of letting it fill the viewport. The pseudo wraps the pill visually but
 * leaves the layout box clean.
 */
.site-header-pill {
	position: relative;
	box-sizing: border-box;
	gap: 24px !important;
	padding: 0 16px !important;
	min-height: 65px;
	max-width: 871px;
	width: 100%;

	/* Desktop offset for the `kezzler/site-search` expand presentation:
	 * the panel's left edge starts past the site-logo so the logo stays
	 * visible. Logo width 120 + 24 gap. Mobile is handled inside the
	 * block — the panel fills the whole pill (logo included). */
	--kezzler-site-search-expand-left: 144px;
}

/* Right-cluster actions group (search + Contact us button). */
.site-header-pill__actions {
	gap: 8px;
}

/* ── Header search toggle (mobile) ───────────────────────────────────────
 * On mobile, sit the search icon BETWEEN the Contact us button and the
 * navigation hamburger, and style its toggle to match the hamburger's
 * 36×36 ring (1px midnight-navy border, translucent white bg).
 *
 * Order: hamburger is set to `order: 3` elsewhere in this file, Contact
 * stays at default 0; setting search to `order: 1` slots it between
 * them. Desktop is untouched — the search keeps the bare-icon look + its
 * source-order position to the left of Contact us. */
@media ( max-width: 768px ) {
	.site-header-pill__actions .kezzler-site-search {
		/* Beat `.site-header-pill__cta { order: 2 }` defined further
		 * down in this stylesheet — that rule was meant for the pill's
		 * top-level flex children, but it still applies as a descendant
		 * inside the actions group. order:3 slots search AFTER Contact. */
		order: 3;
	}

	.site-header-pill .kezzler-site-search__toggle {
		width:        36px;
		height:       36px;
		border:       1px solid var( --wp--preset--color--midnight-navy );
		background:   rgba( 255, 255, 255, 0.3 );
	}
}

.site-header-pill::before {
	content: '';
	position: absolute;
	inset: 0;
	background: rgba( 255, 255, 255, 0.9 );
	backdrop-filter: blur( 8px );
	-webkit-backdrop-filter: blur( 8px );
	border: 0.5px solid rgba( 219, 216, 213, 0.4 );
	border-radius: 12px;
	box-shadow: 0 0 6px rgba( 0, 0, 0, 0.1 );
	z-index: -1;
	pointer-events: none;
}

/* Logo sizing inside pill */
.site-header-pill .wp-block-site-logo img,
.site-header-pill .custom-logo {
	max-height: 28px;
	width: auto;
}

/* Navigation styling inside pill */
.site-header-pill .wp-block-navigation {
	font-family: var( --wp--preset--font-family--gt-standard-l );
	font-size: 1rem;
}

.site-header-pill .wp-block-navigation__container {
	gap: 36px;
}

.site-header-pill .wp-block-navigation-item__content {
	color: var( --wp--preset--color--midnight-navy );
	text-decoration: none;
	padding: 0;
	transition: color var( --wp--custom--transition--default );
}

.site-header-pill .wp-block-navigation-item__content:hover,
.site-header-pill .wp-block-navigation-item__content:focus-visible {
	color: var( --wp--preset--color--origin-orange );
}

/* Submenu trigger chevron color */
.site-header-pill .wp-block-navigation__submenu-icon {
	color: var( --wp--preset--color--midnight-navy );
}

/* Submenu dropdown panel — match the pill's glass aesthetic */
.site-header-pill .wp-block-navigation__submenu-container {
	background: rgba( 255, 255, 255, 0.92 );
	backdrop-filter: blur( 10px );
	-webkit-backdrop-filter: blur( 10px );
	border: 0.5px solid rgba( 219, 216, 213, 0.4 );
	border-radius: 12px;
	box-shadow: 0 12px 32px rgba( 15, 31, 71, 0.12 );
	padding: 12px 8px;
	min-width: 220px;
}

.site-header-pill .wp-block-navigation__submenu-container .wp-block-navigation-item__content {
	padding: 8px 12px;
	border-radius: 6px;
}

.site-header-pill .wp-block-navigation__submenu-container .wp-block-navigation-item__content:hover {
	background: rgba( 15, 31, 71, 0.04 );
}

/* CTA button inside pill — slightly smaller than default for pill height fit */
.site-header-pill .wp-block-buttons {
	margin: 0;
}

.site-header-pill .wp-block-button .wp-block-button__link {
	font-size: 0.9375rem;
}

/* ── Mobile pill + menu (Figma 3875:196852 / 197641 / 197744) ─────────────
 * Figma design: at narrow widths, the pill becomes:
 *   logo (K-mark only) | Book a demo (no arrow, 36px) | dot-trigger circle
 * When the trigger is tapped, a glass panel appears below the pill (NOT
 * fullscreen) containing the navigation items as a 3-level accordion.
 * The overlay's container is reused for the panel; positioning + bg are
 * heavily overridden below.
 *
 * Logo swap (mobile shows K-mark only; desktop shows full wordmark) is
 * done via display rules on two image blocks in parts/header.html.
 */

/* Logo visibility per breakpoint */
.site-header-pill__logo--mobile { display: none; }

@media ( max-width: 768px ) {
	.site-header-pill__logo--desktop { display: none; }
	.site-header-pill__logo--mobile  {
		display: block;
		margin: 0;
		flex: 1 1 auto;  /* grow to fill remaining space, pushing right cluster to the edge */
	}
	.site-header-pill__logo--mobile img {
		height: 28px;
		width: auto;
	}

	/* Tighten pill gap on mobile (right cluster items use this gap) */
	.site-header-pill {
		gap: 8px !important;
		padding: 0 14px !important;
	}

	/* Right-side cluster order: Book a demo, then trigger button */
	.site-header-pill__cta { order: 2; }
	.site-header-pill__nav {
		order: 3;
		/* Drop the nav's own position context so the overlay positions
		 * relative to the pill (which has position: relative) instead. */
		position: static;
	}

	/* Mobile pill button: drop the arrow + circle, smaller height */
	.site-header-pill .wp-block-button .wp-block-button__link {
		height: 36px;
		padding: 3px 18px !important;
		gap: 0;
		font-size: 15px;
	}
	.site-header-pill .wp-block-button .wp-block-button__link::before,
	.site-header-pill .wp-block-button .wp-block-button__link::after {
		display: none;
	}
}

/* ── Trigger dot-icon (CSS-only, single toggle button) ──────────────────
 * The open button stays in flex flow at the right of the pill. Its
 * default SVG hamburger is hidden. Two pseudo-elements carry the dot
 * patterns and cross-fade when overlay state changes:
 *   ::before — 3 horizontal dots (closed/default)
 *   ::after  — 5-dot dice (overlay open)
 *
 * State swap detection: `.is-menu-open` class is added by core to the
 * responsive-container when open. We use `:has()` to flip the pseudos'
 * opacities. The same button click toggles state via our extended
 * IA action `actions.toggleMenuOnClick` (see assets/js/site-header-toggle.js
 * + render_block filter in inc/setup.php).
 *
 * Dot positions sourced from Figma SVG paths (DotsThree assets):
 *   Closed: centers (7.5, 16), (16, 16), (24.5, 16) in 32×32 viewBox
 *           = 23.4375%, 50%, 76.5625% on X axis
 *   Open:   centers (10, 10), (22, 10), (16, 16), (10, 22), (22, 22)
 *           = 31.25%, 50%, 68.75% percentages
 *   Radius: 1.5px (3px diameter dots)
 *   The 32×32 pseudo is inset 1px from the 36×36 button (mirrors Figma's
 *   DotsThree inner placement at left:1.06px top:1.09px). */
.site-header-pill .wp-block-navigation__responsive-container-open {
	width: 36px;
	height: 36px;
	padding: 0;
	border: 1px solid var( --wp--preset--color--midnight-navy );
	border-radius: 50%;
	background: rgba( 255, 255, 255, 0.3 );
	color: var( --wp--preset--color--midnight-navy );
	position: relative;
	cursor: pointer;
	flex-shrink: 0;
}

.site-header-pill .wp-block-navigation__responsive-container-open svg {
	display: none;
}

/* Closed dot pattern (3 horizontal) — visible by default */
.site-header-pill .wp-block-navigation__responsive-container-open::before {
	content: '';
	position: absolute;
	top: 1px;
	left: 1px;
	width: 32px;
	height: 32px;
	opacity: 1;
	background:
		radial-gradient( circle 1.5px at 23.4375% 50%, var( --wp--preset--color--midnight-navy ) 100%, transparent 100% ),
		radial-gradient( circle 1.5px at 50% 50%, var( --wp--preset--color--midnight-navy ) 100%, transparent 100% ),
		radial-gradient( circle 1.5px at 76.5625% 50%, var( --wp--preset--color--midnight-navy ) 100%, transparent 100% );
	transition: opacity 280ms cubic-bezier( 0.4, 0, 0.2, 1 );
}

/* Open dot pattern (5-dot dice) — hidden by default */
.site-header-pill .wp-block-navigation__responsive-container-open::after {
	content: '';
	position: absolute;
	top: 1px;
	left: 1px;
	width: 32px;
	height: 32px;
	opacity: 0;
	background:
		radial-gradient( circle 1.5px at 31.25% 31.25%, var( --wp--preset--color--midnight-navy ) 100%, transparent 100% ),
		radial-gradient( circle 1.5px at 68.75% 31.25%, var( --wp--preset--color--midnight-navy ) 100%, transparent 100% ),
		radial-gradient( circle 1.5px at 50% 50%, var( --wp--preset--color--midnight-navy ) 100%, transparent 100% ),
		radial-gradient( circle 1.5px at 31.25% 68.75%, var( --wp--preset--color--midnight-navy ) 100%, transparent 100% ),
		radial-gradient( circle 1.5px at 68.75% 68.75%, var( --wp--preset--color--midnight-navy ) 100%, transparent 100% );
	transition: opacity 280ms cubic-bezier( 0.4, 0, 0.2, 1 );
}

/* Cross-fade when overlay opens — :has() detects the .is-menu-open class
 * on the responsive container (sibling of the open button inside the
 * navigation block). */
.site-header-pill .wp-block-navigation:has( .wp-block-navigation__responsive-container.is-menu-open ) .wp-block-navigation__responsive-container-open::before {
	opacity: 0;
}
.site-header-pill .wp-block-navigation:has( .wp-block-navigation__responsive-container.is-menu-open ) .wp-block-navigation__responsive-container-open::after {
	opacity: 1;
}

/* The close button stays in the DOM (core/navigation needs it for its own
 * state machine + focus management). We hide it visually — the open button
 * now toggles both states. JS in assets/js/site-header-toggle.js intercepts
 * the open-button click when overlay is already open and triggers the
 * (hidden) close-button click. */
.site-header-pill .wp-block-navigation__responsive-container-close {
	display: none;
}

/* ── Mobile menu panel (overlay, repositioned below pill) ─────────────────
 * Override core/navigation's default fullscreen overlay. Make it a glass
 * panel that sits directly below the pill, matching the pill's aesthetic.
 *
 * Core sets height: 0 on the container (used for its own transition); we
 * force height: auto so the panel sizes to its content. box-sizing also
 * needs to be border-box since core defaults to content-box and our padding
 * would otherwise inflate the rendered size. */
.site-header-pill .wp-block-navigation__responsive-container.is-menu-open {
	position: absolute;
	/* Use `inset` shorthand to reset core's bottom:0 / right:0 defaults
	 * which would otherwise stretch the overlay between top and bottom
	 * of the pill, collapsing height to 0. */
	inset: calc( 100% + 13px ) 0 auto 0;
	width: auto;
	height: auto;
	box-sizing: border-box;
	padding: var( --wp--preset--spacing--md );
	background: rgba( 255, 255, 255, 0.9 );
	backdrop-filter: blur( 8px );
	-webkit-backdrop-filter: blur( 8px );
	border: 0.5px solid rgba( 219, 216, 213, 0.4 );
	border-radius: 12px;
	box-shadow: 0 0 6px rgba( 0, 0, 0, 0.1 );
	color: var( --wp--preset--color--midnight-navy );
	/* overflow: visible lets the close button (positioned -76px above the
	 * overlay) render in the pill area. Tall menus on small viewports may
	 * extend past the viewport — acceptable for now; user can scroll the
	 * page itself, or we add a scrollable inner wrapper in Phase F. */
	overflow: visible;
}

.site-header-pill .wp-block-navigation__responsive-container.is-menu-open .wp-block-navigation__responsive-container-content {
	width: 100%;
	align-items: flex-start;
	/* Override core's padding-top on the content wrapper (core defaults to
	 * roomy fullscreen-overlay spacing; we want a tight glass panel). */
	padding-top: 0;
}

/* Core injects a top margin on the inner dialog wrapper to clear its
 * fullscreen close-button row. We don't show that close button in this
 * pill — drop the margin so content sits at the panel's top padding. */
.site-header-pill .wp-block-navigation__responsive-container.is-menu-open .wp-block-navigation__responsive-dialog {
	margin-top: 0;
}

.site-header-pill .wp-block-navigation__responsive-container.is-menu-open .wp-block-navigation__container {
	width: 100%;
}

.site-header-pill .wp-block-navigation__responsive-container.is-menu-open .wp-block-navigation__container {
	gap: 17px;
	font-size: 18px;
	flex-direction: column;
	align-items: flex-start;
	justify-content: flex-start;
	text-align: left;
}

/* Scope to DIRECT children of the responsive container's outer UL.
 * Sub-items inside .kezzler-mega-menu-link__list keep their own
 * smaller 16px size from mega-menu-link/style.scss. */
.site-header-pill .wp-block-navigation__responsive-container.is-menu-open .wp-block-navigation__container > .wp-block-navigation-item {
	width: 100%;
	align-items: flex-start;
	text-align: left;
}

.site-header-pill .wp-block-navigation__responsive-container.is-menu-open .wp-block-navigation__container > .wp-block-navigation-item > .wp-block-navigation-item__content {
	color: var( --wp--preset--color--midnight-navy );
	padding: 0;
	font-size: 18px;
	line-height: 1.3;
}

.site-header-pill .wp-block-navigation__responsive-container.is-menu-open .wp-block-navigation__container > .wp-block-navigation-item > .wp-block-navigation-item__content:hover,
.site-header-pill .wp-block-navigation__responsive-container.is-menu-open .wp-block-navigation__container > .wp-block-navigation-item > .wp-block-navigation-item__content:focus-visible {
	color: var( --wp--preset--color--origin-orange );
}

/* ── Remove default margin between template parts ───────────────────────
 * WordPress adds margin-block-start via :where(.wp-site-blocks) > *.
 * We manage all section spacing ourselves via block padding attributes.
 */
:where(.wp-site-blocks) > * {
	margin-block-start: 0;
	margin-block-end: 0;
}

/* Same treatment one level down inside `.entry-content` — pages compose
 * out of section blocks that own their own vertical rhythm, so the
 * default block-gap between top-level children would just add visible
 * seams. Scoped to entry-content used by PAGE templates; the single-
 * post body column re-enables block-gap below via a more specific
 * override so prose paragraphs + headings keep their reading rhythm.
 */
:root :where(.entry-content.is-layout-flow) > *,
:root :where(.entry-content.is-layout-constrained) > *,
:root :where(main) > * {
	margin-block-start: 0;
	margin-block-end: 0;
}

/* Single post — restore the default block-gap inside the right column's
 * post-content. The rule above strips margins on every .entry-content
 * child, which is correct for page-template section flow but kills the
 * paragraph/heading rhythm needed for prose. Specificity 0,3,0 beats
 * the 0,1,0 reset above. Uses `--wp--style--block-gap` (the same token
 * WP would have applied natively) so spacing tracks theme.json.
 */
.kezzler-post-twocol__body .entry-content > * + *,
.kezzler-post-twocol__body .wp-block-post-content > * + * {
	margin-block-start: var( --wp--style--block-gap, 1.5rem );
}

/* Cap the prose reading column at 750px (Figma 8:19217). The right
 * twocol slot is wider than this on desktop — clamping the
 * `.entry-content` itself keeps the column comfortable to read without
 * needing to constrain each block individually. */
.kezzler-post-twocol__body .entry-content {
	max-width: 750px;
}

/* Excerpt lede — sits above post-content as a slightly larger intro
 * paragraph. The plugin filter (inc/post-excerpt-conditional.php)
 * suppresses the block entirely when no manual excerpt is set, so
 * these styles only ever paint when there's something to paint.
 */
.kezzler-post-twocol__body .kezzler-post-excerpt {
	max-width: 750px;
	margin-block-end: var( --wp--style--block-gap, 1.5rem );
}

.kezzler-post-twocol__body .kezzler-post-excerpt .wp-block-post-excerpt__excerpt {
	font-family: var( --wp--preset--font-family--gt-standard-l );
	/* Font-size driven by the `excerpt` preset (30→32px fluid) set via
	 * the block's fontSize attribute. */
	line-height: 1.3;
	letter-spacing: -0.01em;
	color: var( --wp--preset--color--midnight-navy );
	margin: 0;
}

/* Article body — H3 subheadings within the post-content read as
 * section labels rather than full-weight H3s in the theme scale.
 * Step them down to the h-4 preset so the reading rhythm stays
 * comfortable inside the 750px column. Scoped to the post-twocol
 * right column so global H3 use elsewhere is untouched. */
.kezzler-post-twocol__body .wp-block-post-content h3 {
	font-size: var( --wp--preset--font-size--h-4 );
}

/* ── Newsletter CTA section ──────────────────────────────────────────────
 * Full-width rounded card pattern used on single posts (and reusable
 * via patterns/newsletter-cta.php). Inner core/group carries the bg +
 * border-radius + padding from block attributes; this rule just sets
 * the heading scale, body width, and the small disclaimer styling that
 * theme.json + the existing presets don't already cover. Figma 8:19270.
 *
 * Heading targets 65px desktop / 40px mobile — between h-2 (44px) and
 * h-1 (92px). Using an inline clamp rather than adding a new preset
 * until this scale shows up elsewhere on the site.
 */
.kezzler-newsletter-cta__heading {
	font-family: var( --wp--preset--font-family--gt-standard-l );
	font-size: clamp( 2.5rem, 4.5vw, 4.0625rem ); /* 40 → 65px */
	line-height: 1.05;
	letter-spacing: -0.03em;
	margin: 0;
	max-width: 60rem;
}

.kezzler-newsletter-cta__body {
	font-family: var( --wp--preset--font-family--gt-standard-l );
	font-size: 1.125rem; /* 18px */
	line-height: 1.3;
	margin: 0;
	max-width: 44rem;
}

.kezzler-newsletter-cta__disclaimer {
	font-family: var( --wp--preset--font-family--gt-standard-l );
	font-size: 0.6875rem; /* 11px — Figma 8:19286 */
	line-height: 1.2;
	margin: 0;
	max-width: 32rem;
	opacity: 0.7;
}

/* ── kezzler/faqs ────────────────────────────────────────────────────────
 * Two-column FAQ section. Left column: tag pill + heading. Right
 * column: stacked accordion items (core/accordion frontend). Below
 * ~860px the columns stack to a single flow. Figma 8:19287.
 *
 * Visual targets:
 *   - Section padding: section (top/bottom) + margin (inline) presets
 *   - Column gap: pane preset (~48–70px, fluid)
 *   - Tag: 14px GT Standard L, orange dot, midnight-navy
 *   - Heading: 45px GT Standard L (h-3 preset), line-height 1.1
 *   - Accordion item: 1px divider (grey-tint-dark border), 20px
 *     vertical padding, 32px question, 15px answer, +/− toggle icon
 */
.kezzler-faqs {
	padding-block: var( --wp--preset--spacing--section );
	padding-inline: var( --kezzler-section-padding-inline );
}

.kezzler-faqs__inner {
	display: flex;
	flex-wrap: wrap;
	gap: var( --wp--preset--spacing--pane );
	align-items: flex-start;
	margin-inline: auto;
}

.kezzler-faqs__inner.is-content-wide    { max-width: var( --wp--style--global--wide-size ); }
.kezzler-faqs__inner.is-content-content { max-width: var( --wp--style--global--content-size ); }

.kezzler-faqs__header {
	flex: 0 0 420px;
	display: flex;
	flex-direction: column;
	gap: 1.25rem;
}

.kezzler-faqs__body {
	flex: 1 1 480px;
	min-width: 0;
}

.kezzler-faqs__tag {
	display: inline-flex;
	align-items: center;
	gap: 0.5rem;
	font-family: var( --wp--preset--font-family--gt-standard-l );
	font-size: 0.875rem; /* 14px */
	line-height: 1.2;
	letter-spacing: -0.01em;
	color: var( --wp--preset--color--midnight-navy );
}

.kezzler-faqs__tag-dot {
	display: inline-block;
	width: 6px;
	height: 6px;
	border-radius: 50%;
	background-color: var( --wp--preset--color--origin-orange );
	flex-shrink: 0;
}

.kezzler-faqs__heading {
	font-family: var( --wp--preset--font-family--gt-standard-l );
	font-size: var( --wp--preset--font-size--h-3 );
	line-height: 1.1;
	letter-spacing: -0.02em;
	color: var( --wp--preset--color--midnight-navy );
	margin: 0;
	max-width: 24rem;
}

/* Accordion items inside the FAQ block. Core renders
 * .wp-block-accordion-item / __heading / __panel; we scope every
 * style to .kezzler-faqs__accordion so other accordions on the site
 * keep their own treatment. */
.kezzler-faqs__accordion {
	display: flex;
	flex-direction: column;
	gap: 0;
}

.kezzler-faqs__accordion .wp-block-accordion-item {
	border-bottom: 1px solid var( --wp--preset--color--grey-tint-dark );
}

.kezzler-faqs__accordion .wp-block-accordion-heading {
	margin: 0;
}

.kezzler-faqs__accordion .wp-block-accordion-heading__toggle {
	display: flex;
	width: 100%;
	align-items: center;
	justify-content: space-between;
	gap: 1rem;
	padding: 1.25rem 0; /* 20px vertical, no inline padding (border spans full width) */
	background: transparent;
	border: 0;
	cursor: pointer;
	font-family: var( --wp--preset--font-family--gt-standard-l );
	font-size: 2rem; /* 32px */
	line-height: 1.2;
	letter-spacing: -0.02em;
	color: var( --wp--preset--color--midnight-navy );
	text-align: left;
}

.kezzler-faqs__accordion .wp-block-accordion-heading__toggle-title {
	flex: 1 1 auto;
}

.kezzler-faqs__accordion .wp-block-accordion-heading__toggle-icon {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 1.5rem;
	height: 1.5rem;
	flex-shrink: 0;
	font-size: 1.5rem;
	font-weight: 300;
	line-height: 1;
	color: var( --wp--preset--color--midnight-navy );
	transition: transform 0.2s ease;
}

.kezzler-faqs__accordion .wp-block-accordion-item.is-open .wp-block-accordion-heading__toggle-icon {
	transform: rotate( 45deg );
}

.kezzler-faqs__accordion .wp-block-accordion-panel {
	padding: 0 0 1.25rem; /* breathing room before the divider */
}

.kezzler-faqs__accordion .wp-block-accordion-panel p {
	margin: 0;
	font-family: var( --wp--preset--font-family--gt-standard-l );
	font-size: 0.9375rem; /* 15px */
	line-height: 1.4;
	color: var( --wp--preset--color--midnight-navy );
	max-width: 56ch;
}

/* Single-column collapse below 54em (~864px) — header sits above the
 * accordion, heading max-width relaxes so longer titles don't crop. */
@media ( max-width: 54em ) {
	.kezzler-faqs__header {
		flex: 1 1 100%;
	}
	.kezzler-faqs__body {
		flex: 1 1 100%;
	}
	.kezzler-faqs__heading {
		max-width: none;
	}
	.kezzler-faqs__accordion .wp-block-accordion-heading__toggle {
		font-size: 1.5rem; /* 24px on mobile */
	}
}

/* ── Kezzler split-header layout ────────────────────────────────────────
 * Pattern: a section header that puts heading text on the left and a CTA
 * button on the right via a flex group. Without a flex constraint the
 * text column grows to its content width and pushes the button to the
 * next row when the heading is long. Letting the first child flex-grow
 * (with min-width: 0 so the heading can wrap inside) keeps the button
 * on the same row at desktop and stacks naturally on narrow viewports
 * via the parent's flex-wrap. The split-row mobile fallback is handled
 * by the section's `kezzler-section-split-header` className.
 */
.kezzler-section-split-header > :first-child {
	flex: 1 1 0;
	min-width: 0;
}

/* ── Kezzler Section default padding ────────────────────────────────────
 * `.kezzler-section` is the className on the core/group "Kezzler Section"
 * variation (src/plugins/kezzler-section). Gives every section a sensible
 * default rhythm without each pattern having to declare it inline:
 *   - vertical (block):  preset `section`  (fluid section padding)
 *   - horizontal (inline): preset `margin` (page-edge gutter clamp)
 *
 * Per-instance overrides via the block inspector emit inline style attrs
 * (specificity 1000) which naturally win over this stylesheet rule, so
 * authors can still tweak any individual section.
 */
.kezzler-section {
	padding-block:  var( --wp--preset--spacing--section );
	padding-inline: var( --wp--preset--spacing--margin );
}

/* ── Section outer-padding toggle (shared filter) ───────────────────────
 * The `outerPadded` toggle wraps a participating block in
 * `<div class="kezzler-section-frame">` via PHP render filter. The frame
 * holds the alignment class (alignfull/alignwide moves from inner block
 * to frame) and produces a 15px gutter on every side via real padding +
 * margin (no box-shadow trickery, so adjacent padded blocks have proper
 * page-bg separation between them, and rounded inner blocks don't leave
 * triangular gaps in their corners).
 *
 * Optional gutter colour is applied as `background-color: var(--wp--...)`
 * inline style on the frame.
 */
.kezzler-section-frame {
	/* Padding sourced from theme.json root padding (`styles.spacing.padding.left/right`)
	 * so the frame gutter stays in sync with the page-edge gutter automatically.
	 * No `--block` analog exists in WP for root padding — same value reused. */
	padding: var( --wp--style--root--padding-left );
}

/* ── Frame-aware section padding-inline ─────────────────────────────────
 * Kezzler section blocks normally have `padding-inline: var(--wp--preset--spacing--margin)`
 * (~20–70px clamp). When wrapped in `.kezzler-section-frame`, the frame
 * itself contributes 15px inline padding, so the inner block's padding
 * must be reduced by the same amount to keep content aligned with
 * non-framed sections.
 *
 * Blocks consume this via `padding-inline: var(--kezzler-section-padding-inline)`.
 * Default = `--margin`. Inside frame = `--margin - root-padding-left`.
 */
:root {
	--kezzler-section-padding-inline: var( --wp--preset--spacing--margin );
}
.kezzler-section-frame {
	--kezzler-section-padding-inline: calc(
		var( --wp--preset--spacing--margin ) - var( --wp--style--root--padding-left )
	);
}

/* ── Vertical spacing axis (kezzler section blocks) ─────────────────────
 * Per-instance margin-block on the outermost wrapper of opt-in blocks (see
 * KEZZLER_VERTICAL_SPACING_BLOCKS in inc/cover-content-width.php). Default
 * value matches the page-edge gutter token so vertical breathing room
 * scales with the same fluid clamp as horizontal padding. Large = 2×.
 */
.has-vertical-spacing-default {
	margin-block: var( --wp--preset--spacing--margin );
}
.has-vertical-spacing-large {
	margin-block: calc( var( --wp--preset--spacing--margin ) * 2 );
}

/* ── Pattern-driven cover padding (hero / solutions-hero) ────────────────
 * Hero patterns apply this class on the inner core/cover so its inline
 * padding can react to frame wrapping the same way kezzler blocks do.
 * Block-direction padding stays at the full `card` token (clamp 20–55px).
 */
.kezzler-cover-padding {
	padding: var( --wp--preset--spacing--card );
}
.kezzler-section-frame > .kezzler-cover-padding,
.kezzler-section-frame .kezzler-cover-padding {
	padding-inline: calc(
		var( --wp--preset--spacing--card ) - var( --wp--style--root--padding-left )
	);
}

/* ── Cover block styles ─────────────────────────────────────────────────
 * Five named styles registered in inc/block-styles.php on core/cover.
 * Phase 1 = paddings only. Each style mirrors the inline padding used by
 * the corresponding pattern (hero.php / solutions-hero.php / single.html
 * post hero / cta.php / mid-page-cta.php) so an author can pick the look
 * via the block Styles selector without authoring a full pattern.
 *
 * Note: cover's default min-height (430px) is overridden to 0 — these
 * are content-driven sections, not min-height-driven heroes.
 */
.wp-block-cover.is-style-kezzler-hero-large,
.wp-block-cover.is-style-kezzler-medium,
.wp-block-cover.is-style-kezzler-small,
.wp-block-cover.is-style-kezzler-extra-small {
	min-height: 0;
}

.wp-block-cover.is-style-kezzler-hero-large {
	/* Hero Large — generous top space, surface + radius + inner content
	 * cap. Content position is driven by the per-instance
	 * `contentPosition` attribute (variation pre-fills `bottom left`)
	 * so authors can change it via the toolbar without CSS overrides.
	 * padding-bottom is locked equal to padding-inline so the cover
	 * sits in a balanced visual frame. */
	min-height:     0 !important;
	padding-top:    var( --wp--preset--spacing--hero );          /* 200 → 224px */
	padding-bottom: var( --wp--preset--spacing--margin );        /*  20 →  70px */
	padding-inline: var( --wp--preset--spacing--margin );        /*  20 →  70px */
	background-color: var( --wp--preset--color--warm-white );
	color:            var( --wp--preset--color--midnight-navy );
	border-radius:    var( --wp--custom--border-radius--md );
}
@media ( min-width: 1024px ) {
	.wp-block-cover.is-style-kezzler-hero-large {
		min-height: 700px !important;
	}
}
.kezzler-section-frame .wp-block-cover.is-style-kezzler-hero-large {
	padding-inline: max(
		1.25rem,
		calc( var( --wp--preset--spacing--margin ) - var( --wp--style--root--padding-left ) )
	);
	padding-bottom: max(
		1.25rem,
		calc( var( --wp--preset--spacing--margin ) - var( --wp--style--root--padding-left ) )
	);
}
/* Mobile: fixed 450px floor (matches Hero Medium). `!important` needed
 * because the base hero-large rule pins `min-height: 0 !important`. */
@media ( max-width: 767.98px ) {
	.wp-block-cover.is-style-kezzler-hero-large,
	.kezzler-section-frame .wp-block-cover.is-style-kezzler-hero-large {
		min-height: 450px !important;
	}
}

/* Hero Large — default background image shipped with the theme. Painted
 * via a `::before` pseudo so we can flip the image independently of the
 * cover's contents (transform on the cover itself would flip the heading,
 * buttons, overlay tint).
 *
 * Single source asset (`hero-bg-lg.jpg`) reused for both viewports:
 *   - Mobile (default): upright.
 *   - Desktop (≥ 768px): flipped vertically via `scaleY(-1)`.
 *
 * The `:has()` guards skip the default when the author picks an image or
 * video via the cover toolbar (cover only renders
 * `wp-block-cover__image-background` / `__video-background` when media
 * is set). `.kezzler-default-bg-off` is the opt-out class from the
 * Layout → Disable default background image toggle.
 *
 * Pseudo sits at the bottom of the cover stack (z-index 0): under the
 * cover's `__background` overlay tint (z-index 1) and the
 * `__inner-container` content (z-index 2). Inherits the cover's
 * border-radius so the image clips to the rounded corners. */
.wp-block-cover.is-style-kezzler-hero-large:not(.kezzler-default-bg-off):not(:has(.wp-block-cover__image-background)):not(:has(.wp-block-cover__video-background))::before {
	content:             '';
	position:            absolute;
	inset:               0;
	z-index:             0;
	pointer-events:      none;
	border-radius:       inherit;
	background-image:    url( '../images/cover/hero-bg-lg.jpg' );
	background-size:     cover;
	background-position: center;
	background-repeat:   no-repeat;
}
@media ( min-width: 768px ) {
	.wp-block-cover.is-style-kezzler-hero-large:not(.kezzler-default-bg-off):not(:has(.wp-block-cover__image-background)):not(:has(.wp-block-cover__video-background))::before {
		transform:           scaleY( -1 );
		background-size:     145%;
		background-position: 20% -15%;
	}
}
@media ( max-width: 767.98px ) {
	.wp-block-cover.is-style-kezzler-hero-large:not(.kezzler-default-bg-off):not(:has(.wp-block-cover__image-background)):not(:has(.wp-block-cover__video-background))::before {
		background-size:     200%;
		background-position: 25% -100px;
	}
}

.wp-block-cover.is-style-kezzler-hero-medium {
	/* Hero Medium — tighter top, used on solution / post heroes.
	 * Same chassis as Hero Large (paired with the JS block variation
	 * which pre-fills attributes) with: tighter top padding, smaller
	 * min-height baseline (380 mobile / 470 desktop), narrower content.
	 * All values are editor-overridable via inline styles (the Cover
	 * height slider sets inline min-height which wins over these
	 * non-!important class rules). padding-bottom locked equal to
	 * padding-inline so the cover sits in a balanced frame. */
	min-height:     380px;
	padding-top:    var( --wp--preset--spacing--compact-hero ); /* 140px */
	padding-bottom: var( --wp--preset--spacing--margin );       /*  20 →  70px */
	padding-inline: var( --wp--preset--spacing--margin );       /*  20 →  70px */
	background-color: var( --wp--preset--color--warm-white );
	color:            var( --wp--preset--color--midnight-navy );
	border-radius:    var( --wp--custom--border-radius--md );
}
@media ( min-width: 1024px ) {
	.wp-block-cover.is-style-kezzler-hero-medium {
		min-height: 470px;
	}
}
/* Mobile: fixed 450px floor. */
@media ( max-width: 767.98px ) {
	.wp-block-cover.is-style-kezzler-hero-medium,
	.kezzler-section-frame .wp-block-cover.is-style-kezzler-hero-medium {
		min-height: 450px;
	}
}
.kezzler-section-frame .wp-block-cover.is-style-kezzler-hero-medium {
	padding-inline: max(
		1.25rem,
		calc( var( --wp--preset--spacing--margin ) - var( --wp--style--root--padding-left ) )
	);
	padding-bottom: max(
		1.25rem,
		calc( var( --wp--preset--spacing--margin ) - var( --wp--style--root--padding-left ) )
	);
}
/* Hero Medium heading — fixed 46px at every viewport (no fluid clamp).
 * Targets core/heading and core/post-title only (both carry
 * .wp-block-heading) so the rule does NOT bleed into nested blocks
 * that render as a heading element for semantic reasons (e.g.
 * kezzler/tag with htmlTag: h1). */
.wp-block-cover.is-style-kezzler-hero-medium .wp-block-cover__inner-container h1.wp-block-heading,
.wp-block-cover.is-style-kezzler-hero-medium .wp-block-cover__inner-container h2.wp-block-heading,
.wp-block-cover.is-style-kezzler-hero-medium .wp-block-cover__inner-container h1.wp-block-post-title,
.wp-block-cover.is-style-kezzler-hero-medium .wp-block-cover__inner-container h2.wp-block-post-title {
	font-size: var( --wp--preset--font-size--h-1 );
}

@media ( min-width: 768px ) {
	.wp-block-cover.is-style-kezzler-hero-medium .wp-block-cover__inner-container h1.wp-block-heading,
	.wp-block-cover.is-style-kezzler-hero-medium .wp-block-cover__inner-container h2.wp-block-heading,
	.wp-block-cover.is-style-kezzler-hero-medium .wp-block-cover__inner-container h1.wp-block-post-title,
	.wp-block-cover.is-style-kezzler-hero-medium .wp-block-cover__inner-container h2.wp-block-post-title {
		font-size: var( --wp--preset--font-size--h-2 );
	}
}

.wp-block-cover.is-style-kezzler-medium {
	/* Medium — full-section vertical padding + margin horizontal padding.
	 * Default chassis for any mid-page CTA / call-out cover (originally
	 * shipped as cta.php). Renamed from `kezzler-cta-large` 2026-05-24
	 * when the CTA Large/Small pair was generalised to Medium/Small +
	 * paired with JS block variations (B+A pattern). DB content migrated
	 * via wp search-replace at the same commit. */
	padding-block:  var( --wp--preset--spacing--section );      /* 48 → 150px */
	padding-inline: var( --wp--preset--spacing--margin );       /* 20 →  70px */
}

/* Medium — default background image. Same `::before` pattern as Hero
 * Large (z-index 0, under cover overlay + inner-container). Desktop:
 * `cover` for full-bleed; mobile: `contain` anchored top-right so the
 * decorative graphic sits compact above the stacked content. */
.wp-block-cover.is-style-kezzler-medium:not(.kezzler-default-bg-off):not(:has(.wp-block-cover__image-background)):not(:has(.wp-block-cover__video-background))::before {
	content:             '';
	position:            absolute;
	inset:               0;
	z-index:             0;
	pointer-events:      none;
	border-radius:       inherit;
	background-image:    url( '../images/cover/cover-bg-medium.jpg' );
	background-size:     cover;
	background-position: center;
	background-repeat:   no-repeat;
}
@media ( max-width: 767.98px ) {
	.wp-block-cover.is-style-kezzler-medium:not(.kezzler-default-bg-off):not(:has(.wp-block-cover__image-background)):not(:has(.wp-block-cover__video-background))::before {
		background-size:     contain;
		background-position: top right;
	}
}

/* Medium heading — Heading Medium preset (4.0625rem max → 2.25rem min,
 * fluid-clamped: 36px mobile → 65px desktop). Mirrors the Hero Medium
 * pattern where the heading size is locked via scoped CSS regardless of
 * author-picked size in the inspector. Scoped to .wp-block-heading +
 * .wp-block-post-title so the rule does NOT bleed into nested blocks
 * rendered as a heading element for semantic reasons (kezzler/tag with
 * htmlTag h1, etc.). */
.wp-block-cover.is-style-kezzler-medium .wp-block-cover__inner-container h1.wp-block-heading,
.wp-block-cover.is-style-kezzler-medium .wp-block-cover__inner-container h2.wp-block-heading,
.wp-block-cover.is-style-kezzler-medium .wp-block-cover__inner-container h1.wp-block-post-title,
.wp-block-cover.is-style-kezzler-medium .wp-block-cover__inner-container h2.wp-block-post-title {
	font-size: var( --wp--preset--font-size--heading-medium );
}

.wp-block-cover.is-style-kezzler-extra-small {
	/* Extra Small — compact pale-azure banner. Desktop: uniform 20px
	 * padding on every side. Mobile (≤ 767.98px): 20px horizontal +
	 * 48px vertical so the stacked heading + newsletter signup get
	 * breathing room top-and-bottom while preserving the narrow
	 * page-edge gutter. Neither 20px nor 48px maps to a Kezzler
	 * spacing preset (closest are `sm` 16 / `md` 24 / `lg` 32); raw
	 * pixels here, add tokens if the values repeat elsewhere. Defaults
	 * seeded by the JS block variation in assets/js/block-variations.js
	 * — pale-azure bg, row layout (heading + newsletter-signup) that
	 * stacks below 600px. */
	padding: 20px;
}
@media ( max-width: 767.98px ) {
	.wp-block-cover.is-style-kezzler-extra-small {
		padding: 48px 20px;
	}
}

/* Extra Small — default background image (light variant, matches the
 * pale-azure chassis). Same `::before` pattern as the other cover
 * styles. Variation already pre-fills dimRatio 0 + kezzlerBgColor
 * pale-azure, so the image paints over pale-azure with no overlay. */
.wp-block-cover.is-style-kezzler-extra-small:not(.kezzler-default-bg-off):not(:has(.wp-block-cover__image-background)):not(:has(.wp-block-cover__video-background))::before {
	content:             '';
	position:            absolute;
	inset:               0;
	z-index:             0;
	pointer-events:      none;
	border-radius:       inherit;
	background-image:    url( '../images/cover/cover-bg-extra-small-light.jpg' );
	background-size:     cover;
	background-position: top right;
	background-repeat:   no-repeat;
}

.wp-block-cover.is-style-kezzler-small {
	/* Small — LG (32px) vertical, margin (20→70px fluid) horizontal.
	 * Horizontal padding matches the Medium + Hero pair so all four
	 * cover styles share the same page-edge rhythm. Default chassis for
	 * compact CTA banners (originally shipped as mid-page-cta.php).
	 * Renamed from `kezzler-cta-small` 2026-05-24. */
	padding-block:  var( --wp--preset--spacing--lg );           /* 32px */
	padding-inline: var( --wp--preset--spacing--margin );       /* 20 → 70px */
}

/* Small — default background image (dark variant, matches the deep-
 * midnight-navy overlay this style ships with). Same `::before` pattern
 * as the other cover styles. Desktop: cover/center. Mobile: contain
 * anchored top-right so the compact graphic doesn't crowd the heading. */
.wp-block-cover.is-style-kezzler-small:not(.kezzler-default-bg-off):not(:has(.wp-block-cover__image-background)):not(:has(.wp-block-cover__video-background))::before {
	content:             '';
	position:            absolute;
	inset:               0;
	z-index:             0;
	pointer-events:      none;
	border-radius:       inherit;
	background-image:    url( '../images/cover/cover-bg-small-dark.jpg' );
	background-size:     cover;
	background-position: top right;
	background-repeat:   no-repeat;
}

/* ── Cover inner-container stacking ─────────────────────────────────────
 * Pinned above the bg + dim layers so authored content always reads
 * over any background treatment. Needed regardless of whether precise
 * positioning is on; the precise-positioning rule below additionally
 * lifts the bg img/video above the dim by default. */
.wp-block-cover .wp-block-cover__inner-container {
	position: relative;
	z-index: 2;
}

/* ── Cover precise positioning (kezzlerPrecisePositioning attr) ─────────
 * Replaces the full-bleed default (inset:0; width:100%; height:100%;
 * object-fit:cover) with per-side anchoring on the cover's NATIVE
 * <img class="wp-block-cover__image-background"> /
 * <video class="wp-block-cover__video-background"> element. Image source
 * stays on core's `url` / `id` / `alt` attributes — only positioning is
 * custom.
 *
 * Defaults: width 760px, top: 0, right: 0 (anchored top-right corner)
 * — same starting point the legacy corner-image system used.
 *
 * Stacking: above the dim layer (z-index 2) by default so the image
 * reads at full colour. The `.is-kezzler-bg-below-dim` modifier flips
 * back to core's stacking (image gets tinted by dim).
 *
 * Mobile (≤767.98px) reads --kezzler-bg-*-mobile vars with the desktop
 * value as fallback — authors only override the axes they need.
 */
.wp-block-cover[data-kezzler-precise-bg] > .wp-block-cover__image-background,
.wp-block-cover[data-kezzler-precise-bg] > .wp-block-cover__video-background {
	inset:           auto;
	top:             var( --kezzler-bg-top,    0 );
	right:           var( --kezzler-bg-right,  0 );
	bottom:          var( --kezzler-bg-bottom, auto );
	left:            var( --kezzler-bg-left,   auto );
	width:           var( --kezzler-bg-width,  760px );
	height:          var( --kezzler-bg-height, auto );
	object-fit:      contain;
	object-position: center;
	z-index:         2;
	transform:
		rotate( var( --kezzler-bg-rotate, 0deg ) )
		scaleX( var( --kezzler-bg-scale-x, 1 ) )
		scaleY( var( --kezzler-bg-scale-y, 1 ) );
	transform-origin: center center;
}

.wp-block-cover[data-kezzler-precise-bg].is-kezzler-bg-below-dim
	> .wp-block-cover__image-background,
.wp-block-cover[data-kezzler-precise-bg].is-kezzler-bg-below-dim
	> .wp-block-cover__video-background {
	z-index: auto;
}

/* Gradient fade — vertical mask that fades the bottom of the precise-
 * positioned image / video to transparent. `--kezzler-bg-fade-distance`
 * (set inline from kezzlerBgFadeDistance, default 25%) is the size of
 * the gradient zone measured from the bottom edge. Composes with the
 * existing rotate + scale transforms.
 *
 * Per-breakpoint overrides:
 *   - `.kezzler-bg-fade-mobile-on`  — force fade on at ≤ 767.98px
 *     regardless of the desktop class (lets authors enable fade only
 *     on mobile).
 *   - `.kezzler-bg-fade-mobile-off` — force fade off at ≤ 767.98px
 *     even when the desktop class is set.
 *   - `--kezzler-bg-fade-distance-mobile` — per-breakpoint distance,
 *     falls back to the desktop value when missing. */
.wp-block-cover[data-kezzler-precise-bg].kezzler-bg-has-fade
	> .wp-block-cover__image-background,
.wp-block-cover[data-kezzler-precise-bg].kezzler-bg-has-fade
	> .wp-block-cover__video-background {
	-webkit-mask-image: linear-gradient(
		to bottom,
		#000 calc( 100% - var( --kezzler-bg-fade-distance, 25% ) ),
		transparent 100%
	);
	mask-image: linear-gradient(
		to bottom,
		#000 calc( 100% - var( --kezzler-bg-fade-distance, 25% ) ),
		transparent 100%
	);
}

@media ( max-width: 767.98px ) {
	/* Mobile force-on — applies regardless of the desktop class. Reads
	 * the mobile distance var with a fallback to desktop (and finally
	 * 25% if neither is set). */
	.wp-block-cover[data-kezzler-precise-bg].kezzler-bg-fade-mobile-on
		> .wp-block-cover__image-background,
	.wp-block-cover[data-kezzler-precise-bg].kezzler-bg-fade-mobile-on
		> .wp-block-cover__video-background {
		-webkit-mask-image: linear-gradient(
			to bottom,
			#000 calc( 100% - var( --kezzler-bg-fade-distance-mobile, var( --kezzler-bg-fade-distance, 25% ) ) ),
			transparent 100%
		);
		mask-image: linear-gradient(
			to bottom,
			#000 calc( 100% - var( --kezzler-bg-fade-distance-mobile, var( --kezzler-bg-fade-distance, 25% ) ) ),
			transparent 100%
		);
	}

	/* Mobile force-off — neutralise the desktop mask. */
	.wp-block-cover[data-kezzler-precise-bg].kezzler-bg-fade-mobile-off
		> .wp-block-cover__image-background,
	.wp-block-cover[data-kezzler-precise-bg].kezzler-bg-fade-mobile-off
		> .wp-block-cover__video-background {
		-webkit-mask-image: none;
		mask-image:         none;
	}

	/* Inherit case (desktop on, no mobile override) — allow the mobile
	 * distance var to tune the fade independently if the author set it. */
	.wp-block-cover[data-kezzler-precise-bg].kezzler-bg-has-fade:not(.kezzler-bg-fade-mobile-on):not(.kezzler-bg-fade-mobile-off)
		> .wp-block-cover__image-background,
	.wp-block-cover[data-kezzler-precise-bg].kezzler-bg-has-fade:not(.kezzler-bg-fade-mobile-on):not(.kezzler-bg-fade-mobile-off)
		> .wp-block-cover__video-background {
		-webkit-mask-image: linear-gradient(
			to bottom,
			#000 calc( 100% - var( --kezzler-bg-fade-distance-mobile, var( --kezzler-bg-fade-distance, 25% ) ) ),
			transparent 100%
		);
		mask-image: linear-gradient(
			to bottom,
			#000 calc( 100% - var( --kezzler-bg-fade-distance-mobile, var( --kezzler-bg-fade-distance, 25% ) ) ),
			transparent 100%
		);
	}
}

@media ( max-width: 767.98px ) {
	.wp-block-cover[data-kezzler-precise-bg] > .wp-block-cover__image-background,
	.wp-block-cover[data-kezzler-precise-bg] > .wp-block-cover__video-background {
		top:    var( --kezzler-bg-top-mobile,    var( --kezzler-bg-top,    0 ) );
		right:  var( --kezzler-bg-right-mobile,  var( --kezzler-bg-right,  0 ) );
		bottom: var( --kezzler-bg-bottom-mobile, var( --kezzler-bg-bottom, auto ) );
		left:   var( --kezzler-bg-left-mobile,   var( --kezzler-bg-left,   auto ) );
		width:  var( --kezzler-bg-width-mobile,  var( --kezzler-bg-width,  760px ) );
		height: var( --kezzler-bg-height-mobile, var( --kezzler-bg-height, auto ) );
		transform:
			rotate( var( --kezzler-bg-rotate-mobile, var( --kezzler-bg-rotate, 0deg ) ) )
			scaleX( var( --kezzler-bg-scale-x-mobile, var( --kezzler-bg-scale-x, 1 ) ) )
			scaleY( var( --kezzler-bg-scale-y-mobile, var( --kezzler-bg-scale-y, 1 ) ) );
	}
}

/* ── Global: match Figma's antialiased font rendering ───────────────────
 * Browsers default to subpixel antialiasing which makes fonts heavier than
 * Figma. Switching to antialiased/grayscale matches Figma's rendering.
 */
body {
	-webkit-font-smoothing: antialiased;
	-moz-osx-font-smoothing: grayscale;
}

/* ── Global: strip browser default <mark> background ────────────────────
 * WordPress inline text-color format wraps text in <mark class="has-inline-color">.
 * All browsers apply a yellow highlight to <mark> by default — remove it.
 */
mark.has-inline-color {
	background: none;
}

/* ── ch/carousel-arrows — Kezzler outline-circle style ─────────────────
 * Used by every Kezzler carousel pattern. Tightens the default
 * ch/carousel-arrows visual (40px circle, full radius, inherit color)
 * to the project's design language: midnight-navy currentColor + 12px
 * gap between the two buttons. Border stays at 1px (0.5px renders as
 * 0px on non-Retina viewports).
 */
.ch-carousel-arrows.is-style-kezzler {
	--ch-carousel-arrows-size: 40px;
	--ch-carousel-arrows-gap:  12px;
	color: var( --wp--preset--color--midnight-navy );
}

.ch-carousel-arrows.is-style-kezzler .ch-carousel-arrows__btn {
	/* Hairline border — Figma spec. On HiDPI (Retina, modern phones,
	 * most laptops) this renders as 1 physical pixel = a true hairline.
	 * On 1x displays the browser rounds up to 1px — same as the previous
	 * default, so worst case is no regression.
	 */
	border-width: 0.5px;
	position:     relative;
}

/* Hide the codehouse-blocks chevron SVG and paint the canonical
 * Kezzler dot-arrow via a mask-image ::before instead. Same source
 * asset as core/button so any future arrow tweak updates everywhere.
 * Prev button is rotated 180deg (the next/prev semantic is owned by
 * the wrapping button — the arrow drawing itself is the same).
 */
.ch-carousel-arrows.is-style-kezzler .ch-carousel-arrows__btn > svg {
	display: none;
}

.ch-carousel-arrows.is-style-kezzler .ch-carousel-arrows__btn::before {
	content: '';
	display: block;
	width:   18px;
	height:  18px;
	background: currentColor;
	mask-image:    var( --kezzler-icon-arrow-right );
	mask-size:     contain;
	mask-repeat:   no-repeat;
	mask-position: center;
	-webkit-mask-image:    var( --kezzler-icon-arrow-right );
	-webkit-mask-size:     contain;
	-webkit-mask-repeat:   no-repeat;
	-webkit-mask-position: center;
}

.ch-carousel-arrows.is-style-kezzler .ch-carousel-arrows__btn--prev::before {
	transform: rotate( 180deg );
}

/* ── ch/carousel-arrows .kezzler-arrows-sides ───────────────────────────
 * ALTERNATIVE arrow placement — does NOT replace the default
 * carousel-controls-below layout. Opt-in via an extra className on
 * ch/carousel-arrows AND moving the block out of carousel-controls
 * into ch/carousel directly.
 *
 * Splits prev/next across the carousel viewport, flanking the centered
 * (active) slide. Designed for `ch/carousel` patterns with bleed + a
 * fixed `slideSizeDesktop` length so the arrow position can anchor to
 * the active card's outer edge via `--ch-carousel-slide-width-*`.
 * Used by the timeline-section pattern to match the Figma layout where
 * arrows flank the focused timeline card. The team-quote-section
 * pattern keeps the default arrows-below layout.
 *
 * Container queries on `.ch-carousel` (container-type: inline-size)
 * cycle the slide-width var per breakpoint so the gap-to-card stays
 * constant. Mobile shows nothing — touch swipe is the affordance there.
 */
/* Mobile-first: arrows hidden. Touch swipe is the affordance on small
 * viewports; the absolute-positioned arrows end up clipped at the edge
 * of the viewport when the card almost fills it, so showing them adds
 * no value. Above the tablet breakpoint the side-arrows render with
 * comfortable breathing room outside the active card.
 */
.ch-carousel-arrows.kezzler-arrows-sides {
	display: none;
}

@container ( min-width: 768px ) {
	.ch-carousel-arrows.kezzler-arrows-sides {
		display:        block;
		position:       absolute;
		inset:          0;
		pointer-events: none;
		z-index:        2;
	}

	.ch-carousel-arrows.kezzler-arrows-sides .ch-carousel-arrows__btn {
		position:       absolute;
		top:            50%;
		transform:      translateY( -50% );
		pointer-events: auto;
	}

	.ch-carousel-arrows.kezzler-arrows-sides .ch-carousel-arrows__btn--prev {
		right: calc( 50% + var( --ch-carousel-slide-width-tablet ) / 2 + 1.5rem );
	}
	.ch-carousel-arrows.kezzler-arrows-sides .ch-carousel-arrows__btn--next {
		left:  calc( 50% + var( --ch-carousel-slide-width-tablet ) / 2 + 1.5rem );
	}
}

@container ( min-width: 1024px ) {
	.ch-carousel-arrows.kezzler-arrows-sides .ch-carousel-arrows__btn--prev {
		right: calc( 50% + var( --ch-carousel-slide-width-desktop ) / 2 + 1.5rem );
	}
	.ch-carousel-arrows.kezzler-arrows-sides .ch-carousel-arrows__btn--next {
		left:  calc( 50% + var( --ch-carousel-slide-width-desktop ) / 2 + 1.5rem );
	}
}

/* ── kezzler-section that hosts the timeline kezzler/steps ──────────────
 * The timeline layout wants its section header (tag + heading + subhead)
 * centered while the steps grid handles its own centering internally.
 * Centering via text-align (rather than nesting a flex container) works
 * because the tag is inline-flex, the heading/paragraph respect text-align,
 * and block children (the steps grid) ignore text-align. Single rule, no
 * markup change required.
 */
.kezzler-section:has( > .kezzler-steps.is-layout-timeline ) {
	text-align: center;
}

/* ── core/group — token-driven border-radius block styles ───────────────
 * WP doesn't ship a "preset" UI for border-radius — only spacing has
 * presets. Block styles are the next-best lever: author picks one from
 * the Styles panel and the matching token paints the radius. "None" (no
 * style selected) leaves the group with flat edges. Used by the Kezzler
 * Section variation + any future group needing a token-aligned radius.
 *
 * Token source of truth: theme.json `settings.custom.border-radius`.
 */
.wp-block-group.is-style-radius-xs   { border-radius: var( --wp--custom--border-radius--xs   ); }
.wp-block-group.is-style-radius-sm   { border-radius: var( --wp--custom--border-radius--sm   ); }
.wp-block-group.is-style-radius-md   { border-radius: var( --wp--custom--border-radius--md   ); }
.wp-block-group.is-style-radius-lg   { border-radius: var( --wp--custom--border-radius--lg   ); }
.wp-block-group.is-style-radius-full { border-radius: var( --wp--custom--border-radius--full ); }

/* When a group has a background colour + radius style, the background
 * needs the same radius so the colour clips to the rounded corners
 * (otherwise the background overflows the visible edge on some browsers).
 * `overflow: hidden` is the simplest fix when there's no negative-margin
 * decoration to preserve. */
.wp-block-group[class*="is-style-radius-"].has-background {
	overflow: hidden;
}

/* ── core/image — Rounded block style ───────────────────────────────────
 * WP core ships an `is-style-rounded` style for `core/image` that applies
 * `border-radius: 9999px` — i.e. a full circle. That's almost never what
 * Kezzler design calls for. Override to use the theme's `md` token (20px)
 * so the default "Rounded" style matches the rest of the system. Applied
 * to both the wrapper and the inner <img> so it works whether the
 * caption sits inside or outside the rounded shape.
 */
.wp-block-image.is-style-rounded,
.wp-block-image.is-style-rounded img {
	border-radius: var( --wp--custom--border-radius--md );
}

/* ── Image figcaption — vertical rule + inline padding ──────────────────
 * Image captions sit below the image with a 1px black left rule and
 * 18px inline padding so they read as an annotation pinned to the
 * image edge instead of free-floating text. Gallery images get their
 * own caption rhythm — opt them out below.
 */
.wp-block-image figcaption {
	border-left:    1px solid #000;
	padding-inline: 18px;
	font-size:      12px;
}
.wp-block-image img + figcaption {
	margin-top: 16px;
}

/* Gallery default gap — 16px. The gallery block reads its tile gap
 * from `--wp--style--gallery-gap-default` (per-instance inline CSS
 * falls back through this variable). Setting it on the gallery root
 * propagates to every instance without needing to bump theme.json's
 * blockGap, which the gallery layout doesn't honor directly. */
.wp-block-gallery {
	--wp--style--gallery-gap-default: 16px;
}

/* Reset inside the gallery block — gallery captions have their own
 * tight per-tile rhythm; the editorial annotation chrome above would
 * dominate the grid. */
.wp-block-gallery .wp-block-image figcaption {
	border-left:    0;
	padding-inline: 0;
	font-size:      inherit;
}
.wp-block-gallery .wp-block-image img + figcaption {
	margin-top: 0;
}

/* ── Universal section reveal-on-scroll opt-in ──────────────────────────
 * Patterns and blocks attach `.kezzler-reveal-section` to a wrapper and
 * `.kezzler-reveal-row` to each child that should fade-up on viewport
 * entry. assets/js/section-reveal.js scans for the section class and
 * toggles `.is-visible` on each row via IntersectionObserver.
 *
 * Reduced motion: skip the transition; rows stay visible by default.
 */
.kezzler-reveal-row {
	opacity: 0;
	transform: translateY( 40px );
	transition:
		opacity   600ms cubic-bezier( 0.2, 0.8, 0.2, 1 ),
		transform 600ms cubic-bezier( 0.2, 0.8, 0.2, 1 );
}

.kezzler-reveal-row.is-visible {
	opacity: 1;
	transform: translateY( 0 );
}

@media ( prefers-reduced-motion: reduce ) {
	.kezzler-reveal-row,
	.kezzler-reveal-row.is-visible {
		opacity: 1;
		transform: none;
		transition: none;
	}
}

/* ── Core button: layout + CSS arrow icon ────────────────────────────────
 * The button renders as: .wp-block-button > a.wp-block-button__link
 *
 * Layout:
 *   Flexbox on the link so ::after (circle) becomes a natural flex item.
 *   Padding matches Figma: pl-16 / pr-4 / py-4.
 *
 * Arrow icon — pure CSS, no SVG, three visual parts:
 *   ::after  →  the 32px circle container (flex item)
 *   ::before →  the full arrow shape (shaft + head) via clip-path polygon.
 *               The polygon is a classic →: a rectangular shaft that widens
 *               into a triangular arrowhead at the right end.
 *               Absolutely positioned over the center of the ::after circle.
 *               z-index: 1 ensures it paints above the circle background.
 *
 * Centering math:
 *   Circle right-edge = 4px from link right. Circle center = 4 + 16 = 20px.
 *   Arrow element: 12px wide → right: 14px puts its center at 20px. ✓
 */
.wp-block-button .wp-block-button__link {
	display: inline-flex !important;
	align-items: center;
	gap: 12px;
	padding: 4px 4px 4px 16px !important;
	position: relative;
	transition: background var( --wp--custom--transition--default ),
	            color var( --wp--custom--transition--default ),
	            box-shadow var( --wp--custom--transition--default );
}

/* Circle — flex item at the right end */
.wp-block-button .wp-block-button__link::after {
	content: '';
	flex-shrink: 0;
	width: 32px;
	height: 32px;
	border-radius: 50%;
	background: rgba( 255, 255, 255, 0.12 );
}

/* Arrow — dot-shaft-arrowhead shape painted via mask-image of the
 * canonical /assets/icons/arrow-right.svg. The mask + `background:
 * currentColor` combo gives a solid-filled icon that follows the
 * button's text colour automatically (dark on light, light on dark,
 * inverted on hover — all without per-variant overrides).
 *
 * Centering: circle ::after is 32px wide, sits flush right with the
 * link's 4px padding-right → circle centre at 4 + 16 = 20px from
 * link right. Arrow ::before is 17×17 → right: 11.5px puts its
 * centre at 20px. ✓
 */
.wp-block-button .wp-block-button__link::before {
	content: '';
	position: absolute;
	right: 11.5px;
	top: 50%;
	transform: translateY( -50% );
	width: 17px;
	height: 17px;
	background: currentColor;
	mask-image:    var( --kezzler-icon-arrow-right );
	mask-size:     contain;
	mask-repeat:   no-repeat;
	mask-position: center;
	-webkit-mask-image:    var( --kezzler-icon-arrow-right );
	-webkit-mask-size:     contain;
	-webkit-mask-repeat:   no-repeat;
	-webkit-mask-position: center;
	z-index: 1;
}

/* ── Button variations ───────────────────────────────────────────────────
 * Base (dark fill): elements.button in theme.json + layout above.
 * These three cover the remaining dark/light × fill/outline matrix.
 * Circle ::after background and arrow color adapt via currentColor.
 */

/* Dark Outline — on light/warm-white backgrounds */
.wp-block-button.is-style-dark-outline .wp-block-button__link {
	background: transparent !important;
	color: var( --wp--preset--color--midnight-navy ) !important;
	box-shadow: inset 0 0 0 1px var( --wp--preset--color--midnight-navy );
}
.wp-block-button.is-style-dark-outline .wp-block-button__link::after {
	background: rgba( 15, 31, 71, 0.08 );
}
.wp-block-button.is-style-dark-outline .wp-block-button__link:hover {
	background: var( --wp--preset--color--midnight-navy ) !important;
	color: var( --wp--preset--color--warm-white ) !important;
	box-shadow: none;
}
.wp-block-button.is-style-dark-outline .wp-block-button__link:hover::after {
	background: rgba( 255, 255, 255, 0.12 );
}

/* Light Fill — warm-white background, navy text. For dark backgrounds. */
.wp-block-button.is-style-light-fill .wp-block-button__link {
	background: var( --wp--preset--color--warm-white ) !important;
	color: var( --wp--preset--color--midnight-navy ) !important;
}
.wp-block-button.is-style-light-fill .wp-block-button__link::after {
	background: rgba( 15, 31, 71, 0.08 );
}
.wp-block-button.is-style-light-fill .wp-block-button__link:hover {
	background: var( --wp--preset--color--core-white ) !important;
}

/* Light Outline — transparent, warm-white border. For dark backgrounds. */
.wp-block-button.is-style-light-outline .wp-block-button__link {
	background: transparent !important;
	color: var( --wp--preset--color--warm-white ) !important;
	box-shadow: inset 0 0 0 1px var( --wp--preset--color--warm-white );
}
.wp-block-button.is-style-light-outline .wp-block-button__link:hover {
	background: var( --wp--preset--color--warm-white ) !important;
	color: var( --wp--preset--color--midnight-navy ) !important;
	box-shadow: none;
}
.wp-block-button.is-style-light-outline .wp-block-button__link:hover::after {
	background: rgba( 15, 31, 71, 0.08 );
}

/* Scanbuy Login: light-outline base + Scanbuy "S" glyph (no arrow circle).
 * Glyph rendered via CSS mask so it inherits currentColor — switches from
 * white on dark surface to navy on hover-inverted surface automatically. */
.wp-block-button.is-style-scanbuy-login .wp-block-button__link {
	background: transparent !important;
	color: var( --wp--preset--color--warm-white ) !important;
	box-shadow: inset 0 0 0 1px var( --wp--preset--color--warm-white );
}
.wp-block-button.is-style-scanbuy-login .wp-block-button__link::before {
	display: none; /* hide the default arrow shape */
}
.wp-block-button.is-style-scanbuy-login .wp-block-button__link::after {
	background: currentColor;
	-webkit-mask-image: url( "../images/scanbuy-icon.svg" );
	mask-image: url( "../images/scanbuy-icon.svg" );
	-webkit-mask-size: 18px 18px;
	mask-size: 18px 18px;
	-webkit-mask-position: center;
	mask-position: center;
	-webkit-mask-repeat: no-repeat;
	mask-repeat: no-repeat;
	width: 18px;
	height: 18px;
}
.wp-block-button.is-style-scanbuy-login .wp-block-button__link:hover,
.wp-block-button.is-style-scanbuy-login .wp-block-button__link:focus-visible {
	background: var( --wp--preset--color--warm-white ) !important;
	color: var( --wp--preset--color--midnight-navy ) !important;
	box-shadow: none;
}

/* No Arrow — opt-out modifier. Hides the arrow glyph + white circle and
 * normalises padding to a symmetric pill. Composable with any colour
 * variation (default, dark-outline, light-fill, light-outline).
 *
 * Sized smaller (36px tall) since without the arrow circle the natural
 * "pill" looks tighter; matches the mobile pill button + footer-secondary
 * button treatments. */
.wp-block-button.is-style-no-arrow .wp-block-button__link {
	gap: 0;
	height: 36px;
	padding: 3px 18px !important;
	font-size: 15px;
}
.wp-block-button.is-style-no-arrow .wp-block-button__link::before,
.wp-block-button.is-style-no-arrow .wp-block-button__link::after {
	display: none;
}

/* ── Footer ──────────────────────────────────────────────────────────────────
 * The footer-inner group sets color: warm-white via textColor attribute.
 * These rules handle layout, typography, and the newsletter form.
 */

/* Top section — bottom border separator */
.footer-top {
	border-bottom: 0.5px solid rgba( 255, 255, 255, 0.4 );
	width: 100%;
	align-items: flex-start;
}

/* ISO certification + disclaimer text — subdued, smaller */
.footer-iso,
.footer-newsletter__disclaimer {
	font-size: 0.8125rem;
	opacity: 0.7;
	margin: 0;
	max-width: 42ch;
}

/* Newsletter heading — Figma 24px / leading 1.1 / cream / GT Standard L
 * Regular. Applied to the <h3> inside .footer-newsletter; identical
 * typography (downsized headline, not display) works across breakpoints. */
.footer-newsletter__heading {
	max-width: 21ch;
	font-size: 1.5rem;
	font-weight: 400;
	line-height: 1.1;
	letter-spacing: -0.01em;
	color: var( --wp--preset--color--cream );
	margin: 0;
}

/* Kiwa certification badges — two square images stacked between the logo
 * and the ISO disclaimer paragraph. Figma node I3:79408;3472:922866.
 * `__brand` blockGap is 24px (badges → ISO text); the extra margin-top
 * here lifts the gap above the badges to ~60px to match Figma. */
.footer-kiwa-badges {
	display: flex;
	gap: 25px;
	align-items: flex-start;
	/* `!important` defeats WP's `is-layout-flex > * { margin: 0 }` reset that
	 * the .footer-top__brand group emits. */
	margin: 36px 0 0 !important;
}

.footer-kiwa-badges img {
	display: block;
	width: 64px;
	height: 72px;
	object-fit: cover;
}

/* Nav section */
.footer-nav {
	width: 100%;
	align-items: flex-start;
}

/* Below 1000px the four-child row gets cramped — wrap the Scanbuy Login
 * button to its own row to stop the labels colliding. Solutions / Industries
 * / Quick links stay on the first row until the full mobile stack at 768px. */
@media ( max-width: 999.98px ) {
	.footer-nav {
		flex-wrap: wrap;
	}
	.footer-nav__login {
		flex-basis: 100%;
		width: 100%;
	}
}

.footer-nav__label {
	font-size: 1.25rem;
	letter-spacing: -0.02em;
	margin: 0;
}

/* Nav lists — semantic <ul><li> with link inside */
.footer-nav__list {
	list-style: none;
	margin: 0;
	padding: 0;
	display: flex;
	flex-direction: column;
	gap: var( --wp--preset--spacing--xs );
}

.footer-nav__list li {
	font-size: 1rem;
	line-height: 1.3;
	margin: 0;
	white-space: nowrap;
}

.footer-nav__list a {
	color: inherit;
	text-decoration: none;
	transition: color var( --wp--custom--transition--default );
}

.footer-nav__list a:hover,
.footer-nav__list a:focus-visible {
	color: var( --wp--preset--color--origin-orange );
}

/* Scanbuy Login button alignment in the nav row */
.footer-nav__login {
	align-self: flex-start;
	margin: 0;
}

/* Bottom bar */
.footer-bottom {
	font-size: 0.9375rem;
	opacity: 0.8;
	width: 100%;
}

.footer-bottom .footer-bottom__col {
	flex: 1;
	margin: 0;
}

/* ── Footer mobile (≤768px) ──────────────────────────────────────────────
 * Per Figma 1890:28903: vertical stack throughout. Sub-columns inside the
 * Solutions group also stack (By Category above By Industry). Quick Links
 * keeps its 2-col split since items are short. Bottom bar items each on
 * their own line, left-aligned. */
@media ( max-width: 768px ) {

	/* Logo smaller on mobile */
	.footer-logo img {
		width: 120px !important;
		max-width: 120px;
	}

	/* Top section: stack logo + newsletter. Padding/gap inherit from the
	 * fluid clamp on .footer-inner — no override needed. */
	.footer-top {
		flex-direction: column !important;
		gap: var( --wp--preset--spacing--pane );
	}

	.footer-top__brand,
	.footer-newsletter {
		width: 100%;
	}

	.footer-newsletter .newsletter-signup {
		max-width: none;
	}

	/* Nav section: stack Solutions / Industries / Quick links / Login */
	.footer-nav {
		flex-direction: column !important;
		gap: var( --wp--preset--spacing--pane );
		align-items: stretch !important;
	}

	/* Quick links 2-col: override core/columns default mobile-stack so the
	 * Learn/About/Contact column stays beside LinkedIn/Youtube. Items are
	 * short enough to fit side-by-side at 375px. */
	.footer-nav__columns {
		flex-direction: row !important;
		flex-wrap: nowrap !important;
		gap: var( --wp--preset--spacing--pane );
	}

	.footer-nav__columns .wp-block-column {
		flex: 1 1 0 !important;
		flex-basis: 0 !important;
	}

	/* Bottom bar: stack each line, left-aligned */
	.footer-bottom {
		flex-direction: column !important;
		align-items: flex-start !important;
		gap: var( --wp--preset--spacing--sm );
	}

	.footer-bottom .footer-bottom__col {
		text-align: left !important;
		flex: 0 0 auto;
	}
}

/* ── FAQ accordion (faq-accordion class on core/accordion) ───────────────────
 * Scoped to .faq-accordion so these overrides don't affect other accordion
 * blocks on the site.
 *
 * Layout intent (from Figma):
 *   - Border-top on first item, border-bottom on all items (grey-tint-dark keyline)
 *   - Generous vertical padding on the toggle button
 *   - Question text inherits h3 typography from theme.json elements.h3
 *   - CSS chevron replaces the default "+" character; rotates when open
 *   - Panel content has bottom padding before the border
 */

.faq-accordion .wp-block-accordion-item {
	border-bottom: var( --wp--custom--keyline--grey-tint-dark );
}


.faq-accordion .wp-block-accordion-heading__toggle {
	padding-bottom: 20px;
	gap: var( --wp--preset--spacing--md );
	align-items: center;
}

.faq-accordion .wp-block-accordion-panel {
	margin-block-start: 0;
	padding-bottom: var( --wp--preset--spacing--lg );
}

/* Replace "+" with a CSS border chevron */
.faq-accordion .wp-block-accordion-heading__toggle-icon {
	width: 24px;
	height: 24px;
	display: flex;
	align-items: center;
	justify-content: center;
	font-size: 0; /* hide the "+" character */
	flex-shrink: 0;
}

.faq-accordion .wp-block-accordion-heading__toggle-icon::before {
	content: '';
	display: block;
	width: 10px;
	height: 10px;
	border-right: 1.5px solid currentColor;
	border-bottom: 1.5px solid currentColor;
	transform: rotate( 45deg ) translate( -2px, -2px );
	transition: transform var( --wp--custom--transition--default );
}

.faq-accordion .wp-block-accordion-item.is-open .wp-block-accordion-heading__toggle-icon::before {
	transform: rotate( -135deg ) translate( -2px, 2px );
}

/* Neutralise Gutenberg's default rotate(45deg) on the icon container — our ::before handles rotation */
.faq-accordion .wp-block-accordion-item.is-open > .wp-block-accordion-heading .wp-block-accordion-heading__toggle-icon {
	transform: rotate( 0deg );
}

/* ── Solutions pattern ───────────────────────────────────────────────────
 * Header row (tag + heading + lead) + 4 alternating rows connected by a
 * vertical timeline line. Scanbuy items get the S-glyph subheading icon;
 * Traceability + Compliance get the orange dot.
 */
.solutions-section__heading {
	font-size: 2.8125rem; /* 45px */
	line-height: 1.1;
	letter-spacing: -0.02em;
	margin: 0;
	max-width: 760px;
}

.solutions-section__lead {
	font-size: 1.125rem; /* 18px */
	line-height: 1.3;
	max-width: 700px;
	margin: 0;
}

.solutions-section__rows {
	position: relative;
	margin-top: var( --wp--preset--spacing--section );
}

/* Vertical timeline connector through the centre — anchored absolutely so
 * row gaps don't break it. */
.solutions-section__rows::before {
	content: "";
	position: absolute;
	top: 0;
	bottom: 0;
	left: 50%;
	width: 1px;
	background-color: rgba( 15, 31, 71, 0.15 );
	pointer-events: none;
}

.solutions-section__row {
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: var( --wp--preset--spacing--section );
	align-items: center;
	padding-block: var( --wp--preset--spacing--pane );
	position: relative;
}

.solutions-section__row--text-left .solutions-section__text { grid-column: 1; order: 1; }
.solutions-section__row--text-left .solutions-section__illustration { grid-column: 2; order: 2; }
.solutions-section__row--text-right .solutions-section__illustration { grid-column: 1; order: 1; }
.solutions-section__row--text-right .solutions-section__text { grid-column: 2; order: 2; }

.solutions-section__text {
	display: flex;
	flex-direction: column;
	gap: var( --wp--preset--spacing--md );
	max-width: 540px;
}

.solutions-section__row--text-right .solutions-section__text {
	margin-left: auto;
}

.solutions-section__subheading {
	display: inline-flex;
	align-items: center;
	gap: 8px;
	font-family: var( --wp--preset--font-family--gt-standard-l );
	font-size: 2rem; /* 32px */
	line-height: 1.1;
	letter-spacing: -0.02em;
	color: var( --wp--preset--color--midnight-navy );
}

.solutions-section__subheading-dot {
	display: inline-block;
	width: 6px;
	height: 6px;
	border-radius: 50%;
	background: var( --wp--preset--color--origin-orange );
	flex-shrink: 0;
}

.solutions-section__subheading-icon {
	display: inline-flex;
	width: 24px;
	height: 24px;
	background-color: var( --wp--preset--color--midnight-navy );
	-webkit-mask-image: url( "../images/scanbuy-icon.svg" );
	mask-image: url( "../images/scanbuy-icon.svg" );
	-webkit-mask-size: contain;
	mask-size: contain;
	-webkit-mask-position: center;
	mask-position: center;
	-webkit-mask-repeat: no-repeat;
	mask-repeat: no-repeat;
	flex-shrink: 0;
}

.solutions-section__body {
	font-size: 1rem;
	line-height: 1.5;
	margin: 0;
	color: var( --wp--preset--color--midnight-navy );
}

.solutions-section__illustration {
	display: flex;
	align-items: center;
	justify-content: center;
}

.solutions-section__row--text-left .solutions-section__illustration {
	justify-content: flex-end;
}

.solutions-section__row--text-right .solutions-section__illustration {
	justify-content: flex-start;
}

.solutions-section__illustration img {
	width: 250px;
	height: auto;
	max-width: 100%;
	display: block;
}

@media ( max-width: 64em ) {
	.solutions-section__rows::before { display: none; }
	.solutions-section__row {
		grid-template-columns: 1fr;
		gap: var( --wp--preset--spacing--lg );
	}
	.solutions-section__row .solutions-section__text,
	.solutions-section__row .solutions-section__illustration {
		grid-column: 1;
		order: initial;
	}
	.solutions-section__row .solutions-section__illustration {
		justify-content: flex-start !important;
	}
	.solutions-section__row .solutions-section__text { margin-left: 0; max-width: none; }
}


/* ── Single post hero ───────────────────────────────────────────────────
 * Same chassis as solutions/learn hero (rounded warm-white cover, bottom-
 * left content stack). The post hero will get a gradient/halftone
 * background IMAGE applied later via Cover's existing background-image
 * control (client to supply the asset).
 *
 * Content order matches Figma 8:9910: small dot+label pill on top, large
 * post title in the middle, author + date meta at the bottom.
 */

/* Meta row — "Author NAME / Created DATE". 18px body. Each child of the
 * flex group renders as its own block (paragraph or dynamic core block);
 * we tighten the gap, strip margins, and dim the date pair to 60% per
 * Figma 8:9922.
 */
.kezzler-post-hero__meta {
	/* Outer container = pair / separator / pair. Column gap 1rem
	 * spaces the separator from each pair. Row gap kicks in on mobile
	 * when the pairs wrap to a column. */
	gap: 0.5rem 1rem !important;
	font-family: var( --wp--preset--font-family--gt-standard-l );
	font-size: 1.125rem; /* 18px */
	line-height: 1.3;
	color: var( --wp--preset--color--midnight-navy );
}

.kezzler-post-hero__meta > * {
	margin: 0;
}

/* Pair container = label + value tightly grouped (word-space gap). */
.kezzler-post-hero__meta-pair {
	gap: 0.35em !important; /* roughly one word space */
}

.kezzler-post-hero__meta-pair > * {
	margin: 0;
}

/* Mobile: stack the two pairs vertically and hide the separator. */
@media ( max-width: 767.98px ) {
	.kezzler-post-hero__meta {
		flex-direction: column;
		align-items: flex-start;
	}
	.kezzler-post-hero__meta-sep {
		display: none;
	}
}

.kezzler-post-hero__meta-label {
	display: inline;
}

.kezzler-post-hero__meta-label--muted,
.kezzler-post-hero__date {
	opacity: 0.6;
}

.kezzler-post-hero__author,
.kezzler-post-hero__date {
	display: inline;
}

.kezzler-post-hero__meta-sep {
	font-family: var( --wp--preset--font-family--gt-standard-l );
	font-size: 1.5rem; /* 24px */
	font-weight: 300;
	line-height: 1;
	color: var( --wp--preset--color--midnight-navy );
}

/* ── Single post — featured media (image OR video) ───────────────────────
 * Sits between the hero and post-content. Author can attach either a
 * featured image (core's Featured Image panel) or a featured video (the
 * "Featured Video" sidebar panel set by the kezzler-blocks plugin); when
 * a video is set, render_block swaps the figure for a core/video with
 * the play-overlay style. Both render paths land on this class so the
 * visual treatment is uniform.
 *
 * Sizing: 4:3 aspect at narrow widths; capped at 530px tall on wide
 * viewports so the media doesn't dominate the page (Figma cap).
 */
.kezzler-post-featured-media {
	aspect-ratio: 4 / 3;
	max-height: 530px;
	width: 100%;
	border-radius: var( --wp--custom--border-radius--md );
	overflow: clip;
	margin: 0;
}

.kezzler-post-featured-media img,
.kezzler-post-featured-media video {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
	border-radius: inherit;
}

/* Play-overlay style on the swapped video path adds its own
 * border-radius on .wp-block-video (the --lg = 38px default); the
 * featured-media slot wants the --md = 20px radius for both image
 * and video output. Bumped specificity (3 classes) so this rule
 * wins regardless of the play-overlay block's source position. */
.wp-block-video.kezzler-post-featured-media.is-style-kezzler-play-overlay {
	border-radius: var( --wp--custom--border-radius--md );
}

/* ── Single post — two-column body row ──────────────────────────────────
 * Sidebar CTA on the left (sticky on desktop), post-content on the right.
 * core/columns provides the stack-on-mobile behaviour for free at its
 * ≤ 781px breakpoint; here we tweak the gap, opt the left column out of
 * the default flex stretch so it can stick, and pin it below the floating
 * site header. Figma 8:9925.
 *
 * Selectors are pinned to `.wp-block-columns.kezzler-post-twocol` (2
 * classes) so they beat WP's global `:root :where(.is-layout-flex)` +
 * `.is-layout-flex` rules (both 0,1,0) which otherwise force `gap: lg`
 * and `align-items: center` from theme.json.
 */
.wp-block-columns.kezzler-post-twocol {
	gap: var( --wp--preset--spacing--pane );
}

/* Sticky CTA only applies above WP's columns-stack breakpoint (782px) —
 * below that, core/columns collapses to a stack and the CTA reads as a
 * normal block above the content. The 96px top offset clears the fixed
 * site-header pill (its visual height plus its top margin).
 *
 * The align-items override uses `!important` to match WP's inline
 * `.wp-block-columns { align-items: normal !important; }` rule which it
 * injects to keep columns equal-height by default. Without flex-start
 * here, the sticky aside is forced to stretch and `position: sticky`
 * has nothing to stick within. */
@media ( min-width: 48.875em ) { /* > 782px */
	.wp-block-columns.kezzler-post-twocol {
		align-items: flex-start !important;
	}

	.kezzler-post-twocol__aside {
		position: sticky;
		top: 96px;
		align-self: flex-start;
	}
}

/* Sidebar CTA card — pale-azure rounded panel containing heading + body
 * + button. Self-contained (the pattern emits its own bg/padding via
 * block attributes); these rules only fine-tune the inner typography
 * and tighten the gap inside the card. Figma 8:9927.
 */
.kezzler-sidebar-cta__heading {
	font-size: 1.625rem; /* 26px */
	line-height: 1.15;
	letter-spacing: -0.02em;
	margin: 0;
}

.kezzler-sidebar-cta__body {
	font-size: 1rem; /* 16px */
	line-height: 1.4;
	margin: 0;
}

/* ── Mid-page CTA pattern ───────────────────────────────────────────────
 * Dark-navy compact banner with heading + body + single light button.
 * Used on Solution pages between Business Impact and Customer Testimonial.
 * Subtle dot-pattern decoration on the right edge via ::after.
 */
.kezzler-mid-page-cta {
	position: relative;
	overflow: hidden;
}

/* Soft vertical bloom from the right edge — gives the dark navy a subtle
 * lift toward the dot pattern. Animatable: shift opacity / x-position
 * for a slow drift in Phase F. */
.kezzler-mid-page-cta::before {
	content: "";
	position: absolute;
	top: 0;
	right: 0;
	bottom: 0;
	width: 50%;
	pointer-events: none;
	background:
		radial-gradient(
			ellipse 60% 100% at right center,
			rgba( 198, 211, 231, 0.18 ) 0%,
			rgba( 198, 211, 231, 0.06 ) 40%,
			transparent 75%
		);
}

/* Sparse dot grid fading right→left + a single accent dot for the
 * pre-orange "tracking pulse" feel from Figma. */
.kezzler-mid-page-cta::after {
	content: "";
	position: absolute;
	top: 0;
	right: 0;
	bottom: 0;
	width: 45%;
	pointer-events: none;
	background:
		/* solitary accent dot near center-right */
		radial-gradient(
			circle at calc( 100% - 60px ) 50%,
			rgba( 255, 255, 255, 0.85 ) 0 2px,
			transparent 3px
		),
		/* sparse dot grid */
		radial-gradient( rgba( 255, 255, 255, 0.13 ) 1.2px, transparent 1.4px );
	background-size: auto auto, 14px 14px;
	background-position: 0 0, top right;
	background-repeat: no-repeat, repeat;
	mask-image: linear-gradient( to left, black 0%, black 30%, transparent 90% );
	-webkit-mask-image: linear-gradient( to left, black 0%, black 30%, transparent 90% );
}

.kezzler-mid-page-cta .wp-block-cover__inner-container > * {
	max-width: 800px;
}

.kezzler-mid-page-cta .wp-block-cover__inner-container > h2.wp-block-heading {
	font-size: clamp( 1.875rem, 3vw, 2.5rem );
	line-height: 1.15;
	letter-spacing: -0.02em;
	margin: 0 0 0.75rem;
}

.kezzler-mid-page-cta .wp-block-cover__inner-container p {
	font-size: 1rem;
	line-height: 1.5;
	margin: 0 0 1.75rem;
	max-width: 36rem;
	opacity: 0.85;
}


/* ── Posts archive: post grid cards ─────────────────────────────────────
 * Used by templates/home.html — core/query → core/post-template with the
 * `kezzler-post-grid` class wraps each post in `.kezzler-post-card`. Card
 * is a vertical stack: featured image on top (rounded top), text region
 * on bottom (warm-white, rounded bottom). Text region has the category
 * pill, title, and date.
 */

.kezzler-post-grid {
	display: grid;
	grid-template-columns: repeat( 3, minmax( 0, 1fr ) );
	gap: var( --wp--preset--spacing--lg );
	list-style: none;
	margin: 0;
	padding: 0;
}

.kezzler-post-card {
	display: flex;
	flex-direction: column;
	overflow: hidden;
	border-radius: var( --wp--custom--border-radius--md );
}

.kezzler-post-card__text {
	flex: 1 1 auto;
	display: flex !important;
	flex-direction: column;
	align-items: flex-start;
	gap: var( --wp--preset--spacing--sm ) !important;
	text-align: left;
	margin-top: 0 !important;
}

.kezzler-post-card__text > * {
	align-self: stretch;
	text-align: left;
}

/* Hide `kezzler/tag` blocks whose label resolved to empty (e.g. a
 * Block Bindings source returned null and the template fallback is
 * ""). Avoids a dangling orange dot in post cards when a post has
 * no asset_type term assigned. */
.kezzler-tag:has( .kezzler-tag__label:empty ) {
	display: none;
}

.kezzler-post-card .wp-block-post-title {
	margin: 0;
}

.kezzler-post-card .wp-block-post-title a {
	color: var( --wp--preset--color--midnight-navy );
	text-decoration: none;
}

.kezzler-post-card .wp-block-post-title a:hover,
.kezzler-post-card .wp-block-post-title a:focus-visible {
	color: var( --wp--preset--color--origin-orange );
}

.kezzler-post-card__date {
	font-size: var( --wp--preset--font-size--tags ) !important;
	color: rgba( 15, 31, 71, 0.6 );
	margin-top: auto;
}

/* Pagination — pill page numbers. Width/height are control-sized
 * (visual chip), not content spacing, so we keep them in rem for
 * a stable hit target across breakpoints. */
.kezzler-post-pagination {
	gap: var( --wp--preset--spacing--xs ) !important;
	margin-top: var( --wp--preset--spacing--card );
}

.kezzler-post-pagination .page-numbers {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 2.5rem;       /* 40px chip size */
	height: 2.5rem;
	border-radius: 50%;
	background: var( --wp--preset--color--warm-white );
	color: var( --wp--preset--color--midnight-navy );
	font-family: var( --wp--preset--font-family--gt-standard-l );
	font-size: var( --wp--preset--font-size--tags );
	text-decoration: none;
	transition: background-color 220ms ease;
}

.kezzler-post-pagination .page-numbers:hover,
.kezzler-post-pagination .page-numbers:focus-visible {
	background: rgba( 15, 31, 71, 0.08 );
}

.kezzler-post-pagination .page-numbers.current {
	background: var( --wp--preset--color--midnight-navy );
	color: var( --wp--preset--color--core-white );
}

.kezzler-post-pagination .wp-block-query-pagination-previous,
.kezzler-post-pagination .wp-block-query-pagination-next {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	color: var( --wp--preset--color--midnight-navy );
	text-decoration: none;
}

/* Posts archive newsletter CTA — pattern wrapper. */
.kezzler-posts-archive-cta {
	margin-top: var( --wp--preset--spacing--lg );
	min-height: 5rem;
}

@media ( max-width: 1023px ) {
	.kezzler-post-grid {
		grid-template-columns: 1fr;
		gap: var( --wp--preset--spacing--md );
	}

	.kezzler-post-card__text {
		padding: var( --wp--preset--spacing--md ) !important;
	}

	.kezzler-posts-archive-cta {
		flex-direction: column;
		align-items: flex-start !important;
		gap: var( --wp--preset--spacing--sm );
	}
}

/* ── Posts archive: first card = desktop hero treatment ────────────────
 * The query renders all posts with the same markup. On desktop the
 * first card spans the full grid width and lays out as image-left +
 * text-right with the excerpt visible. On mobile the first card is
 * identical to the rest (image-top + text-below, no excerpt).
 */

/* Hide the excerpt on all cards by default — only the hero shows it. */
.kezzler-post-card .wp-block-post-excerpt {
	display: none;
}

@media ( min-width: 1024px ) {
	/* First card spans the full row in the 3-col grid. Skipped when
	 * the grid is filtered — the first visible card after filtering is
	 * not guaranteed to still be the same post we picked at render. */
	.kezzler-post-grid > li:first-child {
		grid-column: 1 / -1;
	}

	/* First card flips to image-left + text-right. */
	.kezzler-post-grid > li:first-child .kezzler-post-card {
		display: flex;
		flex-direction: row;
		gap: 0;
	}

	/* Hero image: 1/3 of grid container minus 2 grid gaps. !important
	 * to win over the block's inline width:100% (set by the editor
	 * when aspectRatio is chosen) — needed only at desktop. */
	.kezzler-post-grid > li:first-child .kezzler-post-card__image {
		margin: 0;
		flex: 0 0 auto;
		width: calc( ( 100% - 2 * var( --wp--preset--spacing--lg ) ) / 3 ) !important;
		min-height: 300px;
	}

	.kezzler-post-grid > li:first-child .kezzler-post-card__text {
		flex: 1 1 auto;
		min-width: 0;
	}

	/* Show the excerpt on the hero only. */
	.kezzler-post-grid > li:first-child .kezzler-post-card .wp-block-post-excerpt {
		display: block;
	}
}

/* ── core/video — "Play overlay" block style ─────────────────────────────
 * Rounded corners + Figma-style centered "Video Placeholder" pill (frosted
 * white capsule with orange play-circle on the right edge). Click → JS
 * reveals native controls and starts playback (see assets/js/video-play-
 * overlay.js + the plugin's render_block filter).
 *
 * When the author enables Autoplay on the block, the PHP filter skips
 * overlay injection, so this style degrades to "just rounded corners".
 */
.wp-block-video.is-style-kezzler-play-overlay {
	position: relative;
	border-radius: var( --wp--custom--border-radius--lg, 16px );
	overflow: clip;
	margin: 0;
}

.wp-block-video.is-style-kezzler-play-overlay video {
	display: block;
	width: 100%;
	height: 100%;
	border-radius: inherit;
}

.wp-block-video.is-style-kezzler-play-overlay .kezzler-video-play {
	position: absolute;
	top: 50%;
	left: 50%;
	transform: translate( -50%, -50% );
	display: inline-flex;
	align-items: center;
	gap: 1rem;
	padding: 0.5rem 0.5rem 0.5rem 1.5rem;
	background-color: rgba( 255, 255, 255, 0.7 );
	backdrop-filter: blur( 4px );
	-webkit-backdrop-filter: blur( 4px );
	border: 0;
	border-radius: 6rem;
	cursor: pointer;
	font: inherit;
	color: inherit;
	transition: opacity 200ms ease, transform 200ms ease, background-color var( --wp--custom--transition--default, 200ms ease );
}

.wp-block-video.is-style-kezzler-play-overlay .kezzler-video-play:hover,
.wp-block-video.is-style-kezzler-play-overlay .kezzler-video-play:focus-visible {
	background-color: rgba( 255, 255, 255, 0.9 );
}

.wp-block-video.is-style-kezzler-play-overlay .kezzler-video-play[hidden] {
	display: none;
}

.wp-block-video.is-style-kezzler-play-overlay .kezzler-video-play__label {
	font-family: var( --wp--preset--font-family--gt-standard-l );
	font-size: 1.25rem;
	font-weight: 400;
	line-height: 1;
	letter-spacing: -0.01em;
	color: var( --wp--preset--color--midnight-navy );
	white-space: nowrap;
}

.wp-block-video.is-style-kezzler-play-overlay .kezzler-video-play__icon {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 4rem;
	height: 4rem;
	flex-shrink: 0;
	background-color: var( --wp--preset--color--origin-orange );
	color: var( --wp--preset--color--core-white );
	border-radius: 50%;
}

.wp-block-video.is-style-kezzler-play-overlay .kezzler-video-play__icon svg {
	margin-left: 2px; /* optical adjustment for the play triangle */
}

@media ( prefers-reduced-motion: reduce ) {
	.wp-block-video.is-style-kezzler-play-overlay .kezzler-video-play {
		transition: none;
	}
}

/* ── ch/carousel — Team grid (hover dim siblings) ──────────────────────────
 * Paired with kezzler/person-card slides + ch/carousel gridTablet+gridDesktop
 * toggles (codehouse-blocks v2.6.0+). When one card is hovered (or any of
 * its descendants focused), siblings' images get grayscale + dim down to
 * 23 % of source brightness — visually equivalent to a 77 % black overlay,
 * but achieved with a single `filter` chain so it works on `<img>` itself
 * (replaced elements can't host ::before / ::after pseudo-elements).
 *
 * Mechanism:
 *   • `:has( .kezzler-person-card:hover )` on the carousel root scopes the
 *     dim to "while any sibling is hovered".
 *   • `filter: grayscale(1) brightness(0.23)` on the image. Math:
 *     a black overlay at α=0.77 over a pixel x renders to x · (1 - 0.77)
 *     = x · 0.23 — identical to multiplying by brightness(0.23).
 *   • Hover query gates the effect on coarse-pointer devices so touch
 *     users don't get stuck in a dimmed state.
 *   • Hovered/focused card rises above siblings via z-index so it never
 *     reads as dimmed.
 *   • Keyboard parity via `:focus-within` + `:focus-visible` mirror.
 */

@media ( hover: hover ) {
	.ch-carousel.is-style-kezzler-team-grid:has( .kezzler-person-card:hover ) .kezzler-person-card:not( :hover ) .kezzler-person-card__image {
		filter: grayscale( 1 ) brightness( 0.23 );
	}

	.ch-carousel.is-style-kezzler-team-grid .kezzler-person-card:hover {
		position: relative;
		z-index: 1;
	}
}

/* Keyboard parity — focus-within mirrors the hover behaviour. */
.ch-carousel.is-style-kezzler-team-grid:focus-within:has( .kezzler-person-card:focus-visible ) .kezzler-person-card:not( :focus-visible ) .kezzler-person-card__image {
	filter: grayscale( 1 ) brightness( 0.23 );
}

/* Smooth filter transition both into and out of the dimmed state. */
.ch-carousel.is-style-kezzler-team-grid .kezzler-person-card__image {
	transition: filter 300ms ease;
}

@media ( prefers-reduced-motion: reduce ) {
	.ch-carousel.is-style-kezzler-team-grid .kezzler-person-card__image {
		transition: none;
	}
}


/* ── core/file — Kezzler card variants (Plain + Image) ─────────────────────
 * Server-side markup assembled in plugins/kezzler-blocks/inc/file-styles.php.
 * Both variants share the chassis (top icon → heading → pill button);
 * `kezzler-plain` paints a CSS halftone-dot decoration on a pale-azure
 * surface, `kezzler-image` swaps the decoration for an author-uploaded
 * background image rendered as a positioned <img>. Marketing heading lives
 * in the kezzlerHeading attribute (separate from core/file's fileName).
 *
 * Token usage:
 *   --kezzler-file-radius      → wraps the card; defaults to the project
 *                                "lg" radius if absent.
 *   --kezzler-file-padding     → inner padding; defaults to the fluid
 *                                "card" spacing preset.
 */
.wp-block-file.kezzler-file {
	--kezzler-file-radius:  var( --wp--custom--border-radius--md );
	--kezzler-file-padding: var( --wp--preset--spacing--card );

	position: relative;
	display: flex;
	align-items: center;
	justify-content: center;
	box-sizing: border-box;
	overflow: hidden;
	padding: var( --kezzler-file-padding );
	border-radius: var( --kezzler-file-radius );
	color: var( --wp--preset--color--midnight-navy );
	text-align: center;
	isolation: isolate;
}

/* Reset core/file's default link / button visuals — render filter ships our
 * own anchor + h3; any descendant from core's fallback path should hide if
 * something slips through (defensive, current render replaces wholesale). */
.wp-block-file.kezzler-file > a:not( .kezzler-file__button ),
.wp-block-file.kezzler-file > .wp-block-file__button-richtext-wrapper {
	display: none;
}

/* Plain — pale-azure surface + CSS halftone decoration. The decoration
 * sits absolute behind the content; its size + radial gradient produce a
 * fading-from-right dot field. Pure CSS (no raster) so it stays animatable
 * in Phase F. */
.wp-block-file.kezzler-file--kezzler-plain {
	background: var( --wp--preset--color--pale-azure );
}
.kezzler-file__decoration {
	position: absolute;
	inset: 0;
	z-index: -1;
	pointer-events: none;
	background-image:
		radial-gradient(
			circle 1.5px at center,
			rgba( 15, 31, 71, 0.32 ) 0 1.5px,
			transparent 1.6px
		);
	background-size: 16px 16px;
	background-repeat: repeat;
	/* Fade from full opacity on the right to zero on the left so the dots
	 * read as an accent rather than a flat texture. */
	mask-image: linear-gradient( to left, #000 0%, transparent 70% );
	-webkit-mask-image: linear-gradient( to left, #000 0%, transparent 70% );
}

/* Image — section layout: white surface with a soft side blur
 * decoration, centered text column (heading → subhead → button), then a
 * large rounded showcase image rendered as a separate block below. NOT
 * a background image — the image is its own composed element so the
 * heading reads on white above it. */
.wp-block-file.kezzler-file--kezzler-image {
	flex-direction: column;
	align-items: center;
	justify-content: flex-start;
	gap: 3.125rem;
	background: var( --wp--preset--color--core-white );
	padding-block: clamp( 4.5rem, calc( 3rem + 8vw ), 9.375rem );
	border-radius: 0;
}
/* Soft radial wash decoration — bleeds out the left + right sides. Pure
 * CSS, animatable in Phase F. Plain's `__decoration` rule above paints
 * a halftone dot field; this rule re-binds the same element to a faint
 * grey radial-gradient via specificity. */
.wp-block-file.kezzler-file--kezzler-image .kezzler-file__decoration {
	background-image:
		radial-gradient(
			ellipse 50% 60% at 0% 50%,
			rgba( 15, 31, 71, 0.12 ) 0%,
			transparent 70%
		),
		radial-gradient(
			ellipse 40% 60% at 100% 60%,
			rgba( 15, 31, 71, 0.10 ) 0%,
			transparent 70%
		);
	background-size: 100% 100%;
	background-repeat: no-repeat;
	mask-image: none;
	-webkit-mask-image: none;
}
.kezzler-file--kezzler-image .kezzler-file__content {
	gap: 2.8125rem;
	max-width: 36rem;
}
.kezzler-file__subheading {
	font-family: var( --wp--preset--font-family--gt-standard-l );
	font-size: 1.125rem;
	line-height: 1.3;
	letter-spacing: -0.02em;
	margin: 0;
	color: inherit;
	max-width: 34rem;
	text-wrap: balance;
}
.kezzler-file__media {
	width: 100%;
	max-width: 558px;
	margin: 0;
	border-radius: 20px;
	overflow: hidden;
	aspect-ratio: 558 / 500;
}
.kezzler-file__media img {
	display: block;
	width:  100%;
	height: 100%;
	object-fit: cover;
}

/* Shared content column. */
.kezzler-file__content {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 2.5rem;
	max-width: 100%;
	position: relative;
}

.kezzler-file__icon {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width:  46px;
	height: 46px;
	border-radius: 9999px;
	border: 1px solid currentColor;
	color: currentColor;
}
.kezzler-file__icon img {
	width:  18px;
	height: 18px;
	display: block;
}

.kezzler-file__heading {
	font-family: var( --wp--preset--font-family--gt-standard-l );
	font-size: clamp( 1.75rem, calc( 1.5rem + 1.5vw ), 2.8125rem );
	font-weight: 400;
	line-height: 1;
	letter-spacing: -0.02em;
	margin: 0;
	color: inherit;
	max-width: 18ch;
	text-wrap: balance;
}

/* Pill button — the file card emits standard core/button markup
 * (.wp-block-button > a.wp-block-button__link) via the render filter
 * in inc/file-styles.php, so the button's chrome (arrow + 12%-white
 * circle + hover) is inherited from the core/button rules above. The
 * `.kezzler-file__button` className stays as a positioning hook for
 * the card's flex layout but carries no chrome of its own. */

/* ── ch/carousel — Kezzler "Floating cards" style ────────────────────────
 *
 * Layout chassis for the customer-testimonials section: cards laid out in
 * a fixed 2-row grid with optional per-card pixel-offset transforms so
 * the composition reads as "scattered" rather than "tidy grid". Phase A
 * is static — Phase F adds per-card scroll-parallax via
 * `--floating-scroll-speed`.
 *
 * Activation: a block style on `ch/carousel` (registered in
 * `themes/kezzler/inc/block-styles.php`). When the style class
 * `is-style-kezzler-floating-cards` is on the carousel:
 *
 *   • DESKTOP (container ≥ 1024px): override ch/carousel's native
 *     scroll-snap viewport into a static stage. Slides become grid items
 *     in a 6-column / 2-row template:
 *
 *         ┌────────── a ──────────┬────────── b ──────────┐   row 1: 2 × 50 %
 *         ├─────── c ──────┼────── d ──────┼────── e ─────┤   row 2: 3 × 33 %
 *
 *     The template assumes exactly 5 slides (slot 1 → a, … slot 5 → e).
 *     Each card optionally translates by `--floating-x` / `--floating-y`
 *     pixels (raw numbers from the block's attrs, multiplied by 1px) so
 *     authors can nudge cards off-grid for a scattered feel without
 *     breaking the underlying structure. The h2 sits absolute-centred
 *     behind the cards.
 *
 *   • MOBILE + TABLET (< 1024px): style is INERT — ch/carousel's native
 *     scroll-snap mechanic takes over so users get a swipeable carousel.
 *     The h2 sits above in document flow (margin-bottom). `--floating-x`
 *     / `--floating-y` are ignored (no transform applied at this
 *     breakpoint).
 *
 * Cards have their own visual chrome in
 * `.kezzler-profile-quote-card.has-brand-logo` (glassy white-90% bg,
 * backdrop-blur, typography swap). The stage doesn't style the cards
 * themselves — only their position. */
/* Floating-cards title — single h2 injected server-side by
 * `inc/carousel-floating-cards.php`. Visible above the carousel on
 * mobile/tablet, visually-hidden on desktop (where the
 * `.ch-carousel__track::before` pseudo paints the same text in the
 * middle grid row). */
.ch-carousel.is-style-kezzler-floating-cards .kezzler-floating-cards-title {
	margin:     0 0 var( --wp--preset--spacing--lg );
	text-align: center;
}

@container ( min-width: 1024px ) {
	.ch-carousel.is-style-kezzler-floating-cards {
		position: relative;
	}
	/* Desktop: hide the real h2 visually, keep it in the accessibility
	 * tree. Standard visually-hidden recipe. Do NOT use `display: none`
	 * — that drops the heading from the AT tree entirely. */
	.ch-carousel.is-style-kezzler-floating-cards .kezzler-floating-cards-title {
		position:    absolute;
		width:       1px;
		height:      1px;
		padding:     0;
		margin:      -1px;
		overflow:    hidden;
		clip:        rect( 0, 0, 0, 0 );
		white-space: nowrap;
		border:      0;
	}
	.ch-carousel.is-style-kezzler-floating-cards .ch-carousel__viewport {
		overflow:              visible;
		scroll-snap-type:      none;
		margin-inline:         0;
		width:                 auto;
		position:              relative;
		z-index:               1;
		-webkit-overflow-scrolling: auto;
		overscroll-behavior-x: auto;
	}
	.ch-carousel.is-style-kezzler-floating-cards .ch-carousel__track {
		display:               grid;
		grid-template-columns: repeat( 6, 1fr );
		grid-template-areas:
			"a a a b b b"
			"h h h h h h"
			"c c d d e e";
		gap:                   var( --wp--preset--spacing--md ); /* 24px — theme block-gap */
		align-items:           start;
	}
	/* Title rendered as a grid-placed pseudo on the track. Reserves
	 * vertical space in the layout AND sits at z-index 0 so Phase F
	 * per-card parallax (cards default to z-index 1, set below) can
	 * translate them over the title. Content is purely decorative —
	 * the real <h2> (visually-hidden direct child above) stays in the
	 * accessibility tree.
	 *
	 * Per-pattern customisation: the title string can be overridden by
	 * patterns that need a different label via a `--floating-title`
	 * custom property on the carousel (see pattern). */
	.ch-carousel.is-style-kezzler-floating-cards .ch-carousel__track::before {
		content:        var( --floating-title, "What our customers say" );
		grid-area:      h;
		display:        block;
		flex:           none; /* defeat ch/carousel.is-peek-bleed spacer */
		justify-self:   center;
		align-self:     center;
		padding-inline: var( --wp--preset--spacing--margin );
		text-align:     center;
		color:          var( --wp--preset--color--midnight-navy );
		font-family:    var( --wp--preset--font-family--gt-standard-l );
		font-size:      clamp( 3rem, 1.5rem + 4.5vw, 6.25rem ); /* up to 100px */
		line-height:    1;
		letter-spacing: -0.03em;
		z-index:        0;
		pointer-events: none;
	}
	/* Map the 5 slides to the 5 named areas in document order. Soft
	 * constraint — author should keep exactly 5 cards. Slot 6+ falls
	 * back to auto-placement and stacks below.
	 *
	 * Card widths cap at 400px (row 1) / 380px (row 2) so the row-1 vs
	 * row-2 cell-width disparity (3-col vs 2-col spans absorb different
	 * gap counts) doesn't make row-1 cards visibly larger. justify-self
	 * centres each card inside its cell. */
	.ch-carousel.is-style-kezzler-floating-cards .ch-carousel__track > * {
		flex:              none;
		min-width:         0;
		scroll-snap-align: none;
		width:             100%;
		justify-self:      center;
		/* Optional per-card pixel nudge. `--floating-x` / `--floating-y`
		 * are raw numbers from the card's block attrs — the `* 1px`
		 * multiplier converts them to lengths without needing the PHP to
		 * emit unit suffixes. Default 0 = perfectly on-grid. */
		transform:         translate(
			calc( var( --floating-x, 0 ) * 1px ),
			calc( var( --floating-y, 0 ) * 1px )
		);
	}
	/* Slot order in ch/carousel-slides: 5 cards (heading is the track's
	 * `::before` pseudo). Cards get z-index 1 so they ride above the
	 * pseudo title (z-index 0) when scroll-timeline parallax translates
	 * them (Phase F rules below). */
	.ch-carousel.is-style-kezzler-floating-cards .ch-carousel__track > *:nth-child( 1 ) { grid-area: a; max-width: 400px; z-index: 1; }
	.ch-carousel.is-style-kezzler-floating-cards .ch-carousel__track > *:nth-child( 2 ) { grid-area: b; max-width: 400px; z-index: 1; }
	.ch-carousel.is-style-kezzler-floating-cards .ch-carousel__track > *:nth-child( 3 ) { grid-area: c; max-width: 380px; z-index: 1; }
	.ch-carousel.is-style-kezzler-floating-cards .ch-carousel__track > *:nth-child( 4 ) { grid-area: d; max-width: 380px; z-index: 1; }
	.ch-carousel.is-style-kezzler-floating-cards .ch-carousel__track > *:nth-child( 5 ) { grid-area: e; max-width: 380px; z-index: 1; }

	/* Suppress the native ch/carousel `is-peek-bleed` right-spacer
	 * pseudo. The left-spacer (::before) is REPURPOSED as our title
	 * (see rule above) so we cannot hide it here. */
	.ch-carousel.is-style-kezzler-floating-cards .ch-carousel__track::after {
		display: none !important;
	}

	/* ── Phase F: per-card scroll-driven parallax ─────────────────────
	 * CSS scroll-timeline (animation-timeline: view()) drives a
	 * vertical-translate keyframe per card scoped to the SECTION'S
	 * pass through the viewport — not the entire page scroll.
	 *
	 * How `view()` works here:
	 *   • A named `view-timeline` on `.ch-carousel.is-style-kezzler-
	 *     floating-cards` (the section) defines the animation's
	 *     progress source. 0% = section just entering viewport from
	 *     below; 100% = section just exited from above.
	 *   • Each card animates between its `from` (positive scroll-speed
	 *     × +120px) and `to` (× -120px) keyframes as that pass
	 *     progresses.
	 *   • `animation-range: cover 0% cover 100%` — the full
	 *     viewport-cover window, so cards drift continuously while
	 *     the section is on screen.
	 *
	 * Each card's `--floating-scroll-speed` (emitted inline by
	 * render.php) multiplies the max translate range (±120px). Sign
	 * convention: positive speed → card drifts upward as the user
	 * scrolls down (parallax-back, slower than scroll). Negative
	 * speed → card drifts in the opposite direction (parallax-
	 * forward).
	 *
	 * Gated three ways:
	 *   • `@media (prefers-reduced-motion: no-preference)` — skip for
	 *     users who've opted out of motion.
	 *   • `@supports (animation-timeline: view())` — Chrome 115+,
	 *     Safari 26+, Edge 115+. Firefox lacks support; cards stay in
	 *     their static grid positions (graceful degradation).
	 *   • Inside the existing `@container (min-width: 1024px)` — mobile
	 *     and tablet keep the swipeable carousel mechanic; parallax is
	 *     desktop-only. */
	@media ( prefers-reduced-motion: no-preference ) {
		@supports ( animation-timeline: view() ) {
			/* `view-timeline-name` on the section LIVES OUTSIDE this
			 * @container block (see rule further down) — `.ch-carousel`
			 * is its own container, and `@container` only styles
			 * descendants, never the container element itself. The
			 * animation references the name from out here, which is
			 * fine — the source declaration is global-scope. */
			.ch-carousel.is-style-kezzler-floating-cards .ch-carousel__track > * {
				animation:          kezzler-floating-cards-parallax linear forwards;
				animation-timeline: --kezzler-floating-cards;
				animation-range:    cover 0% cover 100%;
			}
			@keyframes kezzler-floating-cards-parallax {
				from {
					transform: translate(
						calc( var( --floating-x, 0 ) * 1px ),
						calc( ( var( --floating-y, 0 ) + var( --floating-scroll-speed, 0 ) * 120 ) * 1px )
					);
				}
				to {
					transform: translate(
						calc( var( --floating-x, 0 ) * 1px ),
						calc( ( var( --floating-y, 0 ) - var( --floating-scroll-speed, 0 ) * 120 ) * 1px )
					);
				}
			}
		}
	}
}

/* View-timeline source declared at top level — must NOT be inside the
 * `@container` query that gates the rest of the floating-cards desktop
 * rules, because the carousel is its own container and @container only
 * applies to descendants. The name is referenced by the per-card
 * `animation-timeline: --kezzler-floating-cards` rule (inside @container
 * above). The animation only fires when both the timeline source is
 * declared AND the per-card animation rule is active. */
@media ( prefers-reduced-motion: no-preference ) {
	@supports ( animation-timeline: view() ) {
		.ch-carousel.is-style-kezzler-floating-cards {
			view-timeline-name: --kezzler-floating-cards;
			view-timeline-axis: block;
		}
	}
}

/* ── Quote — Kezzler callout style ──────────────────────────────────────
 * Figma Qzo5VkeGI5BYBqTIRViukp node 78:414.
 *
 * Warm-white card with a 3px origin-orange left rule (border, not bg) +
 * top-right + bottom-right border-radius. Large GT-Standard double quote
 * marks (Unicode U+201C / U+201D pairs) anchored top-left + bottom-right.
 * The opening pair is two left-double-quotes; the closing pair is two
 * right-double-quotes — together they read as the "66" / "99" mark pairs
 * shown in the design without inline SVG.
 *
 * "Hide quotation marks" toggle (see src/plugins/quote-options) adds
 * `has-no-quote-marks` className; both pseudo-elements collapse so the
 * card doubles as a generic callout box.
 */
.wp-block-quote.is-style-kezzler {
	position:      relative;
	background:    var( --wp--preset--color--warm-white );
	border:        0;
	border-left:   3px solid var( --wp--preset--color--origin-orange );
	border-radius: 0 var( --wp--custom--border-radius--md ) var( --wp--custom--border-radius--md ) 0;
	padding:       30px 30px 30px 60px;
	margin:        0;

	font-family: var( --wp--preset--font-family--gt-standard-l );
	font-size:   1.5rem;             /* 24px per Figma */
	line-height: 1.3;
	color:       var( --wp--preset--color--midnight-navy );

	/* Inner paragraphs inherit the quote's typography — strip core/quote
	 * defaults (italic, oversize) so the callout reads as plain text. */
	p {
		font-family: inherit;
		font-size:   inherit;
		line-height: inherit;
		color:       inherit;
		font-style:  normal;
		margin:      0;
	}
	p + p { margin-top: 1em; }

	cite {
		display:      block;
		margin-top:   1rem;
		font-style:   normal;
		font-size:    0.875rem;
		opacity:      0.7;
	}

	/* Top-left opening marks + bottom-right closing marks (the same SVG
	 * pair, rotated 180° for the closing side). Painted via mask-image
	 * + background: currentColor so the marks inherit the quote's
	 * text colour (midnight-navy by default; per-instance overridable). */
	&::before,
	&::after {
		content:          '';
		position:         absolute;
		display:          block;
		width:            33px;
		height:           19px;
		background:       currentColor;
		mask-image:       url( '../images/quote/quote-mark.svg' );
		mask-size:        contain;
		mask-repeat:      no-repeat;
		mask-position:    center;
		-webkit-mask-image:    url( '../images/quote/quote-mark.svg' );
		-webkit-mask-size:     contain;
		-webkit-mask-repeat:   no-repeat;
		-webkit-mask-position: center;
		pointer-events:   none;
	}

	&::before {
		top:  30px;
		left: 60px;
	}

	&::after {
		bottom:    30px;
		right:     30px;
		transform: rotate( 180deg );
	}

	/* Body paragraphs need vertical breathing room below the opening
	 * marks + above the closing marks. Pseudo-elements are 19px tall;
	 * push first/last paragraph by mark height + a small gap. */
	> p:first-of-type {
		margin-top: calc( 19px + 1rem );
	}
	> p:last-of-type {
		margin-bottom: calc( 19px + 1rem );
	}

	/* Hide-marks modifier — author opt-in via the inspector toggle. Drops
	 * both pseudo-elements + restores the paragraph margins. */
	&.has-no-quote-marks {
		&::before,
		&::after { content: none; }
		> p:first-of-type { margin-top: 0; }
		> p:last-of-type  { margin-bottom: 0; }
	}
}


/* =============================================================
 * kezzler/logo-card — default surface + logo color
 *
 * WP supports.color drives picked colors via has-X-background-color /
 * has-X-color or inline style. These fallbacks paint the pill when the
 * author hasn't picked — they don't get a class/style, so neither
 * `has-background` nor `has-text-color` is present and the selector
 * matches.
 * ============================================================= */

.wp-block-kezzler-logo-card:not( .has-background ) {
	background-color: #f8f8f8;
}

.wp-block-kezzler-logo-card:not( .has-text-color ) {
	color: #b2b3c2;
}

/* ── core/navigation-link — Kezzler inline icon ─────────────────────────
 * Generic icon glyph appended after the link label when a navigation
 * link carries `kezzlerIcon: <preset-slug>`. Paints via mask-image +
 * currentColor so it inherits the link's text color (handles hover /
 * focus / dim-on-hover patterns for free). Preset → mask-image binding
 * lives below; add a new modifier rule per new preset slug.
 * ====================================================================== */

.kezzler-nav-link-icon {
	display:                inline-block;
	vertical-align:         middle;
	margin-left:            0.4em;
	/* Track the host text size so the glyph always matches the line-height
	 * of the link it sits next to. */
	width:                  1em;
	height:                 1em;
	background-color:       currentColor;
	-webkit-mask-size:      contain;
	mask-size:              contain;
	-webkit-mask-repeat:    no-repeat;
	mask-repeat:            no-repeat;
	-webkit-mask-position:  center;
	mask-position:          center;
	flex-shrink:            0;
}

.kezzler-nav-link-icon--scanbuy-s {
	-webkit-mask-image: var( --kezzler-icon-scanbuy-s );
	mask-image:         var( --kezzler-icon-scanbuy-s );
}

/* ── core/accordion — Hover-to-expand style defaults ─────────────────────
 * Visual defaults when the `kezzler-hover-expand` style is picked. Pure
 * CSS so an existing accordion that flips to this style immediately
 * gets the chassis. Author-picked attributes (background, border-radius,
 * spacing) emit inline styles which win by specificity — overrides
 * still work.
 *
 * Padding sits on the outer wrapper. Items sit inset inside; heading
 * + panel inherit the wrapper's padding via their own padding-block to
 * keep the toggle row sized correctly. Heading/panel rules use lower
 * specificity than `.kezzler-faqs__accordion` overrides, so the FAQ
 * block remains unaffected.
 * ====================================================================== */

.wp-block-accordion.is-style-kezzler-hover-expand {
	/* Gap between items. `gap` covers flex/grid layouts; the custom
	 * property feeds WP's flow-layout `margin-block` injection so the
	 * default-flow accordion gets the same spacing. */
	--wp--style--block-gap: 15px;
	gap: 15px;
}

.wp-block-accordion.is-style-kezzler-hover-expand .wp-block-accordion-item {
	background-color: var( --wp--preset--color--warm-white );
	border-radius: var( --wp--custom--border-radius--md );
	padding-block: 20px;
	padding-inline: var( --wp--preset--spacing--lg );
}

.wp-block-accordion.is-style-kezzler-hover-expand .wp-block-accordion-panel {
	margin-top: 0;
}

.wp-block-accordion.is-style-kezzler-hover-expand h3.wp-block-accordion-heading,
.wp-block-accordion.is-style-kezzler-hover-expand h3.wp-block-accordion-heading .wp-block-accordion-heading__toggle {
	font-size: 20px;
}

.wp-block-accordion.is-style-kezzler-hover-expand .wp-block-accordion-heading__toggle,
.wp-block-accordion.is-style-kezzler-hover-expand .wp-block-accordion-heading__toggle:hover,
.wp-block-accordion.is-style-kezzler-hover-expand .wp-block-accordion-heading__toggle:focus,
.wp-block-accordion.is-style-kezzler-hover-expand .wp-block-accordion-heading__toggle .wp-block-accordion-heading__toggle-title {
	text-decoration: none;
}

/* Smooth open/close — grid-template-rows 0fr↔1fr trick.
 * Core declares `transition: grid-template-rows 0.3s ease-out` on the
 * item (under prefers-reduced-motion: no-preference) but never sets
 * the actual rows. Panel uses display: none when inert, which can't
 * animate. Override both: item becomes a 2-row grid, panel stays in
 * DOM and clips via overflow:hidden. */
.wp-block-accordion.is-style-kezzler-hover-expand .wp-block-accordion-item {
	display: grid;
	grid-template-rows: auto 0fr;
}

.wp-block-accordion.is-style-kezzler-hover-expand .wp-block-accordion-item.is-open {
	grid-template-rows: auto 1fr;
}

.wp-block-accordion.is-style-kezzler-hover-expand .wp-block-accordion-panel,
.wp-block-accordion.is-style-kezzler-hover-expand .wp-block-accordion-panel[inert] {
	display: block;
	overflow: hidden;
	min-height: 0;
	opacity: 0;
}

.wp-block-accordion.is-style-kezzler-hover-expand .wp-block-accordion-item.is-open .wp-block-accordion-panel {
	opacity: 1;
}

@media ( prefers-reduced-motion: no-preference ) {
	.wp-block-accordion.is-style-kezzler-hover-expand .wp-block-accordion-panel {
		transition: opacity 0.3s ease-out;
	}
}

/* ── Form controls — default chrome ─────────────────────────────────────
 * Theme-wide default for native text-like inputs and textareas. Pill
 * radius on inputs (single-line); md radius on textareas (multi-line).
 * Border uses the project's midnight-navy hairline keyline.
 *
 * For Gravity Forms forms, these bare rules tie or lose on specificity
 * against the GF framework selector. We handle GF the right way below
 * by overriding its own custom properties at the wrapper scope.
 */
input[type="text"],
input[type="email"],
input[type="tel"],
input[type="url"],
input[type="number"],
input[type="search"],
input[type="password"],
input[type="date"],
input[type="datetime-local"],
input[type="time"],
input[type="month"],
input[type="week"] {
	border:        var( --wp--custom--keyline--midnight-navy );
	border-radius: var( --wp--custom--border-radius--input );
	padding:       16px;
	height:        56px;
	font-size:     16px;
}

textarea {
	border:        var( --wp--custom--keyline--midnight-navy );
	border-radius: var( --wp--custom--border-radius--md );
	padding:       16px;
	font-size:     16px;
}

/* ── Gravity Forms — theme integration ──────────────────────────────────
 * GF 2.7+ exposes a `--gf-ctrl-*` custom-property surface that the
 * framework's own selectors consume. Overriding these at the form
 * wrapper scope is the best-practice path documented by Gravity Forms
 * — it routes the theme's tokens through GF's existing rules, so we
 * keep their focus / hover / error / disabled chrome intact and only
 * swap the visual primitives (radius, border color, border width).
 *
 * Selector chains two GF classes (`.gform_wrapper.gform-theme--framework`
 * → specificity 0,2,0) so this beats the framework's own
 * `.gform-theme--framework { --gf-ctrl-radius: var(--gf-radius); }`
 * declaration (specificity 0,1,0) regardless of stylesheet load order.
 * Legacy 2.5 markup uses `.gform_wrapper.gravity-theme`.
 *
 * Reference: docs.gravityforms.com/css-custom-properties
 */
/* `!important` is intentional. GF emits a per-form `<style>` block at
 * `#gform_wrapper_NN[data-form-index="0"].gform-theme, [data-parent-form="NN_0"]`
 * (specificity 1,3,0) carrying the form's admin Theme Settings. Without
 * `!important` those per-form inline declarations always beat a class-
 * level theme override, and authors would have to manually re-enter the
 * theme defaults inside every new form's settings panel. Marking these
 * declarations as the theme's inviolable defaults keeps form chrome
 * consistent across the site; an editor who genuinely needs to override
 * per-form can still do so with their own `!important`.
 */
.gform_wrapper.gform-theme--framework,
.gform_wrapper.gravity-theme {
	--gf-ctrl-radius:             var( --wp--custom--border-radius--input ) !important;
	--gf-ctrl-textarea-radius:    var( --wp--custom--border-radius--md ) !important;
	--gf-ctrl-border-color:       var( --wp--preset--color--midnight-navy ) !important;
	--gf-ctrl-border-width:       0.5px !important;
	--gf-ctrl-padding-x:          16px !important;
	--gf-ctrl-padding-y:          16px !important;
	--gf-ctrl-size:               56px !important;
	--gf-ctrl-font-size:          16px !important;
	--gf-ctrl-textarea-padding-y: 16px !important;
	--gf-form-gap-x:              16px !important;
	--gf-form-gap-y:              16px !important;
}

/* Submit button — keep GF's native `<input type="submit">` markup
 * (pseudos / arrow chrome can't paint on replaced elements without
 * markup-swap hacks) and just apply the Kezzler colour + radius so
 * the button reads as part of the brand. `!important` again to win
 * over GF's per-form inline `<style>` emission. */
.gform_wrapper .gform_button {
	background:    var( --wp--preset--color--midnight-navy ) !important;
	color:         var( --wp--preset--color--warm-white ) !important;
	border:        none !important;
	border-radius: var( --wp--custom--border-radius--full ) !important;
}

.gform_wrapper .gform_button:hover {
	background:    var( --wp--preset--color--origin-orange ) !important;
	color:         var( --wp--preset--color--warm-white ) !important;
}

/* GF legacy mode (`.gravity-theme.gform-theme--no-framework`) doesn't
 * consume the `--gf-ctrl-*` custom-property surface — the legacy
 * stylesheet hardcodes radius/border/padding directly via selectors
 * like `.gform_wrapper.gravity-theme input[type="text"]` (specificity
 * 0,3,1). To win cleanly we chain `.gform-theme--no-framework` for an
 * extra class (specificity 0,4,1). `:is()` keeps the input-type list
 * compact without losing per-selector specificity. */
.gform_wrapper.gravity-theme.gform-theme--no-framework :is(
	input[type="text"],
	input[type="email"],
	input[type="tel"],
	input[type="url"],
	input[type="number"],
	input[type="search"],
	input[type="password"],
	input[type="date"],
	input[type="datetime-local"],
	input[type="time"],
	input[type="month"],
	input[type="week"]
) {
	border:        var( --wp--custom--keyline--midnight-navy );
	border-radius: var( --wp--custom--border-radius--input );
	padding:       16px;
}

.gform_wrapper.gravity-theme.gform-theme--no-framework textarea {
	border:        var( --wp--custom--keyline--midnight-navy );
	border-radius: var( --wp--custom--border-radius--md );
	padding:       16px;
}


/* ── Count-up format ──────────────────────────────────────────────────
 * `kezzler/count-up` RichText format wraps numbers in
 * <span class="kezzler-count" data-duration="…">. Frontend script
 * animates 0 → target on viewport entry. `tabular-nums` keeps digit
 * widths consistent during the animation; per-instance min-width is
 * set inline by the script using the final-text length so layout
 * doesn't reflow as digits grow.
 */
/* Per-instance opt-in via the format popover toggle. Default off
 * because the GT Standard digits read more naturally in proportional
 * form. Authors enable on instances where the digit jitter during
 * animation is visible enough to be distracting. */
.kezzler-count[data-tabular="1"] {
	font-variant-numeric: tabular-nums;
}

/* Editor-only marker so authors can see which numbers carry the format
 * (the frontend should render visually identical to plain text). */
.editor-styles-wrapper .kezzler-count {
	box-shadow: inset 0 -2px 0 0
		color-mix( in srgb, var( --wp--preset--color--origin-orange ) 40%, transparent );
}

/* ── core/list — "Pills" block style ────────────────────────────────────
 * Renders each <li> as a pale-azure pill with the midnight-navy label
 * and full corner radius (Figma `1:424`, pills-container reference
 * frame). The default list bullet + indent is reset so the items lay
 * out as a flex-wrap row with an 8px gap.
 */
.wp-block-list.is-style-kezzler-pills {
	list-style:     none;
	margin:         0;
	padding:        0;
	display:        flex;
	flex-wrap:      wrap;
	gap:            8px;
	align-items:    flex-start;

	/* Author-picked colours (Styles panel → Color) target the LIST
	 * element. The PHP filter `inc/list-pills-color-route.php` reroutes
	 * the picked values into `--kezzler-pill-*` custom properties on
	 * this same element, and we neutralise the list-level painting so
	 * the background/text only show on each <li>. */
	background-color: transparent !important;
}

.wp-block-list.is-style-kezzler-pills > li {
	display:        inline-flex;
	align-items:    center;
	min-height:     30px;
	padding:        4px 12px;
	background:     var( --kezzler-pill-bg,    var( --wp--preset--color--pale-azure   ) );
	color:          var( --kezzler-pill-color, var( --wp--preset--color--midnight-navy ) );
	font-family:    var( --wp--preset--font-family--gt-standard-l );
	font-size:      14px;
	line-height:    1.07;
	letter-spacing: -0.02em;
	border-radius:  34px;

	/* Pills containing nested lists / paragraphs would break the layout;
	 * keep the chassis tight to a single text run by stripping margins
	 * off any direct text children. */
	> p { margin: 0; }
}
