/*
 * Hero video (Figma "1. Hero Video"; markup in render.php, behaviour in view.js).
 *
 * WordPress only loads this on pages where the block renders.
 *
 * Colours come from theme.json as --wp--preset--* custom properties, with one
 * deliberate exception: the scrim is pure black, because that is what the
 * design specifies and it is a photographic treatment rather than a brand
 * colour. Its strength is an inline --es-hero-scrim from render.php so it can
 * be tuned per instance when real footage lands.
 */

.es-hero {
	position: relative;
	/*
	 * Above the Intro (z-index: var( --es-z-header )), which pulls itself up
	 * under the hero with a negative margin. The hero has to win that overlap
	 * or the Intro's opaque stage covers it from the first frame and the
	 * curtain never happens.
	 */
	z-index: calc( var( --es-z-header ) + 1 );
	/*
	 * The admin bar for logged-in users is measured by view.js. A plain
	 * 100svh section that starts 92px down the page is 92px taller than the
	 * viewport, which pushes the scroll indicator below the fold and breaks
	 * the dev notes' "each main section should fill the visible screen
	 * height". Subtracting the offset makes the section exactly one viewport
	 * tall wherever it starts.
	 *
	 * Stays 0px without JavaScript, which is the old behaviour: correct when
	 * the hero is genuinely the first thing in the document, and merely
	 * over-tall when it is not.
	 */
	--es-hero-offset: 0px;
	/*
	 * svh, not dvh: dvh re-measures as the mobile URL bar hides and the hero
	 * would resize mid-scroll. (The loading screen uses dvh deliberately — it
	 * is a fixed overlay with nothing to reflow.)
	 */
	min-height: calc( 100vh - var( --es-hero-offset ) );
	min-height: calc( 100svh - var( --es-hero-offset ) );
	overflow: hidden;
	/* Espresso rather than black, so any frame before the image paints is on-brand. */
	background-color: var( --wp--preset--color--contrast, #3d251d );
}

.es-hero--preview {
	min-height: 0;
	aspect-ratio: 16 / 9;
}

/*
 * <picture> is a non-replaced inline wrapper with no box of its own, so the
 * <img> inside it is positioned exactly as if it were a direct child.
 */
.es-hero picture {
	display: contents;
}

.es-hero__fallback,
.es-hero__video {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
	object-position: center;
}

/*
 * The video sits over the fallback image and is visible by default, so with
 * JavaScript disabled it simply plays. view.js adds `is-js`, which hides it
 * until the first frame is decoded and then cross-fades it in over the still —
 * the same inverse gate the loading screen uses, for the same reason: the
 * no-JS path must be the correct one, not the degraded one.
 */
.es-hero.is-js .es-hero__video {
	opacity: 0;
	transition: opacity 0.6s ease;
}

.es-hero.is-js.is-playing .es-hero__video {
	opacity: 1;
}

.es-hero__scrim {
	position: absolute;
	inset: 0;
	background-color: #000;
	opacity: var( --es-hero-scrim, 0.52 );
}

/* ---- Controls ---- */

.es-hero__sound,
.es-hero__scroll {
	position: absolute;
	left: 50%;
	z-index: 1;
	transform: translateX( -50% );
	color: var( --wp--preset--color--base, #f0eeed );
	/* Stops the inline SVGs adding descender space inside their box. */
	line-height: 0;
}

/*
 * Espresso focus rings (main.css) vanish against the footage. Zero-specificity
 * :where() there means this plain selector wins without !important.
 */
.es-hero :focus-visible {
	outline: 2px solid var( --wp--preset--color--base, #f0eeed );
	outline-offset: 6px;
}

/* ---- Sound toggle ---- */

.es-hero__sound {
	/*
	 * Safe-area inset so the control clears a notch or Dynamic Island when the
	 * page runs edge-to-edge (needs viewport-fit=cover on the viewport meta;
	 * resolves to 0 otherwise).
	 */
	top: calc( clamp( 1.5rem, 3vw, 2.5rem ) + env( safe-area-inset-top, 0px ) );
	/* Explicit artwork dimensions also reserve the button box in iOS browsers. */
	width: 100px;
	height: 65.2809px;
	padding: 0;
	border: 0;
	background: none;
	cursor: pointer;
}

.es-hero__sound svg {
	display: block;
	width: 100px;
	height: 65.2809px;
}

/*
 * Both states live in one SVG sharing a single ring, so the ring cannot shift
 * as they swap. The words describe the next action; the dot shows the current
 * sound state. 0.6s matches the motion language in the dev notes.
 */
.es-hero__sound-state > path,
.es-hero__sound-state > circle {
	transition: opacity 0.6s ease;
}

.es-hero__sound[ aria-pressed="false" ] .es-hero__sound-state--off > path,
.es-hero__sound[ aria-pressed="true" ] .es-hero__sound-state--on > path,
.es-hero__sound[ aria-pressed="false" ] .es-hero__sound-state--on > circle,
.es-hero__sound[ aria-pressed="true" ] .es-hero__sound-state--off > circle {
	opacity: 0;
}

/* ---- Scroll indicator ---- */

.es-hero__scroll {
	/* Desktop frame: ~32px above the bottom edge. */
	bottom: calc( 2rem + env( safe-area-inset-bottom, 0px ) );
	transition: opacity 0.6s ease;
}

.es-hero__scroll svg {
	display: block;
	width: 14px;
	height: 30px;
}

/* Reversible, so it returns as the video resumes when the visitor scrolls back. */
.es-hero.is-scrolled .es-hero__scroll {
	opacity: 0;
}

.es-hero__scroll-dot {
	animation: es-hero-scroll-dot 2.4s ease-in-out infinite alternate;
}

@keyframes es-hero-scroll-dot {
	from {
		opacity: 1;
		transform: translateY( 0 );
	}

	to {
		opacity: 0.45;
		transform: translateY( 8px );
	}
}

/*
 * The global reduced-motion rule in main.css collapses durations to 0.01ms,
 * which would leave the dot parked at the far end of its travel and faded.
 * Stopping the animation outright keeps it where the design puts it.
 */
@media ( prefers-reduced-motion: reduce ) {
	.es-hero__scroll-dot {
		animation: none;
	}
}

/*
 * Mobile (Figma "02-Home", 402×745, read 04/09). The theme's mobile layout
 * breakpoint is 768px (main.css), with wider layouts starting at that width.
 *
 * What the mobile frame settles: the sound artwork stays at 100×65, the same
 * size as desktop, so the 80px step-down is gone; the scroll pill sits 22px
 * above the bottom edge rather than desktop's 32px; the footage is a portrait
 * crop, which is a media matter (render.php) rather than a style one — the
 * existing object-fit: cover already centres the landscape file where no
 * portrait cut has been supplied.
 *
 * What it does not settle: the frame carries no scrim layer at all. The scrim
 * is kept on mobile because the controls are unreadable without it on this
 * footage (see the contrast note in the plan); the editor's Overlay control
 * remains the way to lower it. The sound component set is parked at the top
 * of the frame, not placed, so the top offset is unchanged.
 */
@media ( width < 768px ) {
	.es-hero__scroll {
		bottom: calc( 22px + env( safe-area-inset-bottom, 0px ) );
	}
}
