.cl-vho {
	position: relative;
	background-color: var(--cl-vho-bg, #0b0b0c);
	padding-top: 80px;
}

.cl-vho__heading {
	position: relative;
	z-index: 5;
	margin: 0 auto 32px;
	text-align: center;
	max-width: var(--cl-vho-heading-max-width, 900px);
	padding: 0 24px;
	font-weight: var(--cl-vho-heading-font-weight, 700);
	font-family: var(--cl-vho-heading-font-family, inherit);
	line-height: var(--cl-vho-heading-line-height, 1.15);
	color: var(--cl-vho-heading-color, #f5f4f0);
	/* No font-size here on purpose — the theme's own styling for
	   whichever tag is picked in "Heading tag" (h1/h2/h3) applies. The
	   "Heading font size" field below still overrides it when set, via
	   its own conditional !important rule in this element's inline
	   <style> block — this is only the unconfigured default. */
	will-change: filter, opacity;
}

/* Pre-reveal state for the heading's one-time blur+fade entrance.
   Removed (via the .is-revealed class, added by JS) once triggered —
   see script.js. */
.cl-vho__heading:not(.is-revealed) {
	opacity: 0;
	filter: blur(var(--cl-vho-reveal-blur, 12px));
}

.cl-vho__heading.is-revealed {
	opacity: 1;
	filter: blur(0);
	transition: opacity var(--cl-vho-reveal-duration, 900ms) ease, filter var(--cl-vho-reveal-duration, 900ms) ease;
}

/**
 * The spacer provides the scroll runway the pin effect plays out over.
 * Its own height is set inline by script.js to `100vh + scroll range`
 * — tall enough that the video-wrap (pinned to the viewport bottom
 * for the duration) has room to grow before the page naturally
 * continues scrolling past it. Kept a sensible height here as a
 * no-JS/no-scroll-effects fallback.
 */
.cl-vho__spacer {
	/* Pure height reserver — deliberately NOT position: relative, so the
	   absolutely-positioned video-wrap inside it resolves against the
	   section itself. All of the pin geometry below is computed in
	   section coordinates; a positioned spacer would silently shift
	   every one of those calculations by the heading block's height. */
	height: 100vh;
}

.cl-vho__video-wrap {
	/* Resting state: bottom-anchored to the section's own first 100vh
	   screenful, offset by the configured bottom spacing, horizontally
	   centred. This exactly mirrors the pinned state's resting layout —
	   the video sits `bottom spacing` above the screen's bottom edge
	   while the heading sits above it — and, critically, it means that
	   at the precise moment the section's top reaches the viewport top,
	   this position is IDENTICAL to the pinned (fixed, bottom: spacing)
	   position — so engaging the pin causes zero visual change. Any
	   mismatch here between the resting top and the pinned bottom
	   offset reappears as a visible jump right when the pin engages —
	   which is exactly what happened when bottom-spacing was added to
	   the pinned rule below without updating this one to match. */
	position: absolute;
	top: calc(100vh - var(--cl-vho-bottom-spacing, 0px) - var(--cl-vho-start-height, 550px));
	left: 50%;
	transform: translateX(-50%);
	z-index: 1;
	width: var(--cl-vho-start-width, 75%);
	height: var(--cl-vho-start-height, 550px);
	border-radius: var(--cl-vho-start-radius, 24px);
	overflow: hidden;
	will-change: width, height, border-radius, transform;
}

/**
 * Pinned state: only applied by script.js once the section's top has
 * actually reached the viewport top — never on page load regardless of
 * where the section sits on the page. Geometrically identical to the
 * resting state at that instant (see above), so the switch is
 * invisible.
 */
/**
 * Promoted to its own compositor layer while pinned, on touch devices only.
 *
 * iOS repaints a position: fixed element on the main thread while the page
 * scrolls on the compositor, so a JS-driven pin visibly lags the finger.
 * will-change is used rather than a translateZ(0) hint because script.js
 * writes the transform property on these elements itself, and a CSS
 * transform here would either be overwritten or fight it.
 *
 * Scoped to pointer: coarse — a needless layer costs memory and can soften
 * text rendering on a desktop.
 */
@media (pointer: coarse) {
	.cl-vho__video-wrap.cl-vho--pinned { will-change: transform; }
}

.cl-vho__video-wrap.cl-vho--pinned {
	position: fixed;
	top: auto;
	/* Defaults to 0 (touching the viewport bottom) exactly as before —
	   set "Bottom spacing when expanded" above 0 to leave a gap here so
	   the video doesn't run flush into whatever comes right after it. */
	bottom: var(--cl-vho-bottom-spacing, 0px);
	left: 50%;
	transform: translateX(-50%);
}

/**
 * Once scroll progress completes (see script.js), the wrap is switched
 * from `position: fixed` to sitting statically inside its now
 * full-height spacer — releasing it back into normal document flow so
 * it scrolls away with the rest of the page exactly like any other
 * content, rather than staying pinned forever.
 */
.cl-vho__video-wrap.cl-vho--released {
	/* Positioned (in section coordinates) exactly where the pinned wrap
	   was at the instant progress completed: scroll-range below the
	   section top, full viewport size (minus any configured expanded
	   padding) — so the hand-off from fixed back into the document is
	   seamless and the video then scrolls away with the page like any
	   other content. script.js also clears its inline width/height/
	   radius when applying this class; inline styles from the grow
	   would otherwise override these declarations entirely. */
	position: absolute;
	top: var(--cl-vho-scroll-range, 700px);
	bottom: auto;
	left: var(--cl-vho-h-padding, 0px);
	right: var(--cl-vho-h-padding, 0px);
	width: auto;
	height: calc(100vh - var(--cl-vho-bottom-spacing, 0px));
	transform: none;
	border-radius: var(--cl-vho-expanded-radius, 0px);
}

/**
 * border-radius is set here too, not just on .cl-vho__video-wrap's own
 * overflow: hidden clip. An autoplaying <video> gets its own hardware-
 * accelerated compositing layer, and some browsers don't reliably clip
 * that layer to an ANIMATED ancestor radius — corners can render square
 * (often inconsistently, not all four the same) while the grow is in
 * progress and script.js is rewriting the wrap's border-radius every
 * frame. `inherit` tracks the wrap's radius — including that per-frame
 * animated value — without needing a second source of truth in JS.
 */
.cl-vho__video {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
	display: block;
	border-radius: inherit;
}

/* Salient sizes every image in a column to its own file size
   (`.row .col img { height: auto }`, `.row .col img:not([srcset])
   { width: auto }`), which beats the rule above. A photo smaller than
   the card then sat at 1024x497 in a 1103x534 box, leaving a strip
   along two edges. Forced here so the image always fills the card and
   crops to fit, whatever size the upload is. */
.cl-vho .cl-vho__video-wrap .cl-vho__video {
	width: 100% !important;
	height: 100% !important;
	max-width: none !important;
	max-height: none !important;
}

.cl-vho__overlay {
	position: absolute;
	inset: 0;
	background-color: var(--cl-vho-overlay-color, rgba(0, 0, 0, 1));
	opacity: 0;
	pointer-events: none;
	will-change: opacity;
}

.cl-vho__content {
	position: absolute;
	inset: 0;
	z-index: 2;
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: 24px;
	padding: 40px;
	text-align: center;
	will-change: transform, opacity;
	/* Matches the default "Paragraph travel distance" setting — a
	   reasonable pre-JS fallback so there's no flash of fully-centred
	   content before script.js takes over and sets the real value from
	   the configured setting. */
	transform: translateY(40vh);
	/* Belt and braces alongside the translateY above: that offset only
	   hides this content because it happens to push it below the small
	   resting video card's clipped (overflow: hidden) bounds — which
	   depends on the content's own height relative to the card. A large
	   enough font-size makes the content block taller, and it can start
	   peeking back into the visible clipped area despite the offset,
	   causing a flash before script.js takes over. opacity: 0 hides it
	   unconditionally regardless of text size. script.js fades this in
	   via inline opacity, tied to the same scroll progress as the slide. */
	opacity: 0;
}

.cl-vho__paragraph {
	margin: 0;
	max-width: var(--cl-vho-paragraph-max-width, 640px);
	line-height: 1.5;
	color: var(--cl-vho-paragraph-color, #f5f4f0);
	/* No font-size here on purpose — see .cl-vho__heading above. */
}

/* The WYSIWYG editor wraps its own output in <p> tags (and possibly
   more than one, if the person adds a paragraph break) — reset the
   browser's default ~1em top/bottom margin on those so a single
   paragraph sits flush inside .cl-vho__paragraph exactly as the old
   plain-text version did, and add a modest gap only between multiple
   paragraphs rather than around every one of them. */
.cl-vho__paragraph p {
	margin: 0;
}

.cl-vho__paragraph p + p {
	margin-top: 0.75em;
}

.cl-vho__button {
	display: inline-flex;
	align-items: center;
	gap: 8px;
	padding: 12px 22px;
	border-radius: 999px;
	background-color: var(--cl-vho-button-bg, #f5f4f0);
	color: var(--cl-vho-button-text, #0b0b0c);
	text-decoration: none;
	font-weight: 600;
	transition: opacity 0.2s ease;
}

.cl-vho__button:hover {
	opacity: 0.85;
}

.cl-vho__button svg {
	flex-shrink: 0;
}

/**
 * Scroll effects disabled (or reduced-motion, or JS didn't run): settle
 * everything into its final state outright — full-bleed video, full
 * overlay, content centred with no travel remaining. Applied via a
 * class script.js adds immediately in these cases, so nothing is stuck
 * mid-transition or, worse, invisible.
 */
.cl-vho.cl-vho--settled .cl-vho__spacer {
	height: auto;
}

.cl-vho.cl-vho--settled .cl-vho__video-wrap {
	position: relative;
	left: 0;
	width: auto;
	margin-left: var(--cl-vho-h-padding, 0px);
	margin-right: var(--cl-vho-h-padding, 0px);
	height: calc(100vh - var(--cl-vho-bottom-spacing, 0px));
	transform: none;
	border-radius: var(--cl-vho-expanded-radius, 0px);
}

.cl-vho.cl-vho--settled .cl-vho__overlay {
	opacity: var(--cl-vho-overlay-target-opacity, 0.5);
}

.cl-vho.cl-vho--settled .cl-vho__content {
	transform: translateY(0) !important;
	opacity: 1 !important;
}

/**
 * Small screens.
 *
 * 100vh is the wrong unit on iOS Safari: it means the viewport with the
 * URL bar HIDDEN, so a 100vh element is taller than what you can
 * actually see until you scroll, which is the single most common cause
 * of a pinned hero looking wrong on an iPhone. svh is the
 * small-viewport unit — the bar visible, i.e. what is on screen at
 * rest — with a vh fallback for anything that lacks it.
 *
 * The starting width/height themselves need no special handling here
 * — video_start_width and video_start_height's own mobile tab already
 * controls both directly at this breakpoint the same way it does at
 * every other one, including a starting shape taller than it is wide
 * if that's what's wanted (confirmed against the actual reference this
 * element was built from: it deliberately starts as a tall portrait
 * frame that grows into a landscape one as the page scrolls).
 */
@media (max-width: 900px) {
	.cl-vho__spacer { height: 100vh; height: 100svh; }

	/**
	 * The resting-state top calculation right above needs the exact
	 * same svh correction as the spacer does — its own comment already
	 * explains why: at the instant the section reaches the viewport
	 * top, this position must be geometrically identical to the pinned
	 * state's `bottom: spacing`, or the difference shows up as a
	 * visible jump the moment the pin engages. `position: fixed;
	 * bottom: X` always anchors to whatever is genuinely visible right
	 * now; a resting position computed against the taller,
	 * address-bar-hidden 100vh does not match that on a phone at rest,
	 * which is exactly the jump this adds back.
	 */
	.cl-vho__video-wrap {
		top: calc(100vh - var(--cl-vho-bottom-spacing, 0px) - var(--cl-vho-start-height, 550px));
		top: calc(100svh - var(--cl-vho-bottom-spacing, 0px) - var(--cl-vho-start-height, 550px));
	}

	.cl-vho__video-wrap.cl-vho--released {
		height: calc(100vh - var(--cl-vho-bottom-spacing, 0px));
		height: calc(100svh - var(--cl-vho-bottom-spacing, 0px));
	}
}

@media (max-width: 600px) {
	.cl-vho__heading { padding: 0 18px; }
	.cl-vho__content { padding: 0 18px; }
}

@media (prefers-reduced-motion: reduce) {
	.cl-vho__heading,
	.cl-vho__video-wrap,
	.cl-vho__overlay,
	.cl-vho__content {
		transition: none !important;
	}
}
