/*
 * Hardcoded replacement for the "Mountain Parallax Header" Slider Revolution
 * slider, which was rendering as broken raw shortcode text in production
 * (ppb_revslider/ppb_rev_slider shortcode name mismatch bug).
 * Values verified against the slider's own official export (SR7_56_1.json):
 * 5-photo crossfade (910ms, power2.inOut), 9s per slide, a Ken Burns intro
 * (5000ms, power4.out, 300ms start delay), fullHeight sizing with a 100px
 * bottom offset and an 860px cap, plus a subtle mouse-parallax drift on the
 * background (the actual "parallax" the slider's name refers to). One
 * deliberate departure: the export's Ken Burns is blur+zoom; this file's is
 * scale-only - see the note on @keyframes heroKenBurns.
 *
 * Two-layer structure: .hero-slideshow__slide handles the opacity crossfade
 * and the JS-driven mouse-parallax translateY; .hero-slideshow__bg (nested
 * inside) handles the Ken Burns scale keyframe animation. Split into
 * two elements because both would otherwise fight over the transform
 * property on the same node.
 */
/*
 * No border-radius on this element: it is full-bleed at every viewport
 * width (width:100%, no side margin, x:0 against the page edge), with no
 * surrounding margin for a "card" treatment to read against - a radius
 * would only clip the image short of the page's actual (square) corners,
 * exposing whatever's behind it there instead of a flush edge-to-edge
 * photo.
 *
 * box-shadow IS kept, deliberately: every node in this homepage's saved
 * tree has margin/padding of exactly 0 (per the real
 * `_kedynsierra_builder_tree` postmeta), so this shadow's downward blur is
 * the only thing creating visible breathing room between the hero and the
 * "Portfolio" heading right below it - removing it would leave the two
 * touching, with no real margin anywhere in the data to fall back on.
 */
.hero-slideshow {
	position: relative;
	width: 100%;
	height: calc(100vh - 100px);
	max-height: 860px;
	min-height: 480px;
	overflow: hidden;
	background: #0a0a0a; /* matches the theme's one established near-black (DESIGN.md, footer text) rather than an uncataloged one-off */
	perspective: 700px;
	box-shadow: 0 20px 40px -20px rgba(0, 0, 0, 0.5);
}

.hero-slideshow__slide {
	position: absolute;
	inset: 0;
	opacity: 0;
	transition:
		opacity 910ms cubic-bezier(0.45, 0, 0.55, 1),
		transform 300ms cubic-bezier(0.215, 0.61, 0.355, 1);
}

.hero-slideshow__slide.is-active {
	opacity: 1;
	z-index: 1;
}

.hero-slideshow__bg {
	/* width/height are load-bearing, not redundant with inset:0: <img> is a
	   replaced element, so auto width/height under absolute positioning
	   resolves from its own intrinsic pixel size, not the container - unlike
	   .hero-slideshow__slide (a plain div) above, which inset:0 alone does
	   stretch correctly. Without these, object-fit has no correctly-sized
	   box to crop into and the image renders at native size, anchored
	   top-left, leaving a visible gap on any narrower-than-photo viewport. */
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
}

.hero-slideshow__slide.is-active .hero-slideshow__bg {
	/* will-change scoped to the active slide only - the other 4 never
	   animate, so there's no reason to keep all 5 permanently promoted to
	   their own compositor layer. */
	will-change: transform;
	animation: heroKenBurns 5000ms cubic-bezier(0.25, 1, 0.5, 1) 300ms both;
}

/*
 * Scale-only settle - deliberately not the SR7 export's blur+zoom intro
 * (scale 1.5 + 20px gaussian blur resolving over the same window). On a
 * photography portfolio the hero photos are the product; presenting each
 * one out of focus for its first seconds - re-running on every 9s autoplay
 * tick, since hero-slideshow.js restarts this keyframe per slide - works
 * against exactly what the page exists to show. Dropping filter also drops
 * the one per-frame paint cost here (an animated gaussian blur re-convolves
 * the full image every frame; transform stays on the compositor), and the
 * narrow 1.08 start replaces the export's 1.5: that range only existed to
 * give the blur room to resolve. Same blur-free shape as the content
 * builder's own kcb-kenburns keyframe (blocks.css).
 *
 * `both` fill, not `forwards`: with the 300ms start delay, a forwards-only
 * fill renders the image un-transformed until the delay elapses, then snaps
 * it to the `from` state - imperceptible when `from` carried a 20px blur
 * (the snap hid inside the blur), a visible pop on a sharp image. Backwards
 * fill holds scale(1.08) through the delay so the settle begins from what
 * is already on screen.
 */
@keyframes heroKenBurns {
	from {
		transform: scale(1.08);
	}
	to {
		transform: scale(1);
	}
}

/*
 * Cinematic entry (Field Plan Phase 2, 2026-08-23) - the very first thing a
 * visitor sees settles in from black+slightly-zoomed rather than just
 * appearing. Scoped to .pm-hero-first-reveal, a static class only the first
 * slide's <img> carries (see hero-slideshow.php's own note) - a one-time
 * page-load moment, deliberately separate from the recurring per-slide Ken
 * Burns above so it never repeats when the rotation cycles back to slide 1.
 * Opacity-only: transform stays entirely owned by heroKenBurns above (both
 * animations run together via the comma-separated list below), avoiding two
 * keyframes fighting over the same transform property.
 */
@keyframes heroFirstReveal {
	from {
		opacity: 0;
	}
	to {
		opacity: 1;
	}
}

.hero-slideshow__slide.is-active .hero-slideshow__bg.pm-hero-first-reveal {
	animation:
		heroKenBurns 5000ms cubic-bezier(0.25, 1, 0.5, 1) 300ms both,
		heroFirstReveal 1400ms ease-out both;
}

@media (prefers-reduced-motion: reduce) {
	.hero-slideshow__slide.is-active .hero-slideshow__bg {
		animation: none;
	}
	.hero-slideshow__slide.is-active .hero-slideshow__bg.pm-hero-first-reveal {
		animation: none;
	}
	.hero-slideshow__slide {
		transition: opacity 300ms linear;
	}
	/* !important beats hero-slideshow.js's inline parallax transform
	 * (set on mousemove via element.style.transform) - a stylesheet rule
	 * alone can't outrank an inline style, and reduced-motion needs to
	 * unconditionally cancel that transform regardless of mouse position. */
	.hero-slideshow__slide,
	.hero-slideshow__bg {
		transform: none !important;
	}
}

/*
 * Per-slide progress indicator. Duration is set by hero-slideshow.js via the
 * --hero-progress-ms custom property (read from the same SLIDE_DURATION_MS
 * constant the slide-advance timer uses), so the bar can't drift out of sync
 * with the actual slide timing. Auto-plays for slide 1 on load like the Ken
 * Burns effect does; hero-slideshow.js restarts it on every later goTo().
 */
.hero-slideshow__progress {
	position: absolute;
	left: 0;
	right: 0;
	bottom: 0;
	height: 3px;
	background: rgba(255, 255, 255, 0.25);
	z-index: 2;
}
.hero-slideshow__progress-bar {
	height: 100%;
	width: 100%;
	background: #fff;
	transform: scaleX(0);
	transform-origin: left center;
	animation: heroProgress var(--hero-progress-ms, 9000ms) linear forwards;
}
@keyframes heroProgress {
	from {
		transform: scaleX(0);
	}
	to {
		transform: scaleX(1);
	}
}
@media (prefers-reduced-motion: reduce) {
	.hero-slideshow__progress {
		display: none;
	}
}

/*
 * Position dots + pause control - closes the WCAG 2.2.2 gap the progress
 * bar alone didn't: a moving indicator isn't a pause control. Kept as
 * plain buttons styled with pseudo-elements (no icon font dependency).
 */
.hero-slideshow::after {
	/* Guarantees the controls below stay legible regardless of which of the
	   5 real photos is showing - some may have a light area right where the
	   dots/pause button sit. Same gradient-scrim technique as
	   portfolio-grid.css's title treatment, but always-on (not hover-gated)
	   since these controls are always visible, not a hover reveal. z-index 1
	   deliberately sits above the (also z-index:1) active slide via paint
	   order, but below the progress bar/controls at z-index 2/3. */
	content: '';
	position: absolute;
	inset: auto 0 0 0;
	height: 110px;
	background: linear-gradient(to top, rgba(0, 0, 0, 0.6), rgba(0, 0, 0, 0) 100%);
	pointer-events: none;
	z-index: 1;
}
.hero-slideshow__controls {
	position: absolute;
	left: 0;
	right: 0;
	bottom: 16px;
	z-index: 3;
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 14px;
}
/* gap: 0 is deliberate - the 44px buttons below ARE the spacing now. Keeping
   the old 10px gap on top of them would push the visual dots 54px apart and
   read as a disconnected row. */
.hero-slideshow__dots { display: flex; gap: 0; }
.hero-slideshow__dot {
	position: relative;
	/* 44px, not the previous 24px: this is the hit target (the visible dot is
	   the 9px ::before below, unchanged). 24px was under the 44px touch
	   minimum on the site's most prominent gallery - measured live at 414px.
	   The target has to grow via the button box rather than an invisible
	   overlay, because at the old 34px pitch a 44px overlay would have
	   overlapped its neighbours and caused mis-taps. */
	width: 44px;
	height: 44px;
	padding: 0;
	border: none;
	background: transparent;
	cursor: pointer;
}
.hero-slideshow__dot::before {
	content: '';
	position: absolute;
	top: 50%;
	left: 50%;
	width: 9px;
	height: 9px;
	border: 1.5px solid rgba(255, 255, 255, 0.7);
	border-radius: 50%;
	transform: translate(-50%, -50%);
	transition: background-color 0.25s ease, transform 0.25s ease;
}
/*
 * :hover split out from :focus-visible and gated behind (hover: hover):
 * these dots don't navigate away on click (they switch slides in place), so
 * a touchscreen tap would otherwise leave the tapped dot's :hover style
 * stuck applied - most mobile browsers only clear synthetic :hover on the
 * next tap elsewhere, not automatically - which reads as a lingering,
 * confusing highlight on a dot that isn't actually .is-active. Real-hover
 * pointers (mouse) are unaffected; keyboard users keep the same feedback via
 * :focus-visible, ungated.
 */
.hero-slideshow__dot:focus-visible::before {
	background: rgba(255, 255, 255, 0.5);
}
@media (hover: hover) {
	.hero-slideshow__dot:hover::before {
		background: rgba(255, 255, 255, 0.5);
	}
}
.hero-slideshow__dot.is-active::before {
	background: #fff;
	transform: translate(-50%, -50%) scale(1.3);
}

.hero-slideshow__pause {
	position: relative;
	/* 44px for the same touch-minimum reason as the dots above; the glyph
	   offsets below are re-centred to match (they were tuned to the old 28px
	   box). */
	width: 44px;
	height: 44px;
	padding: 0;
	border: none;
	background: transparent;
	cursor: pointer;
	opacity: 0.85;
	transition: opacity 0.2s ease;
}
/* Same stuck-:hover-on-tap reasoning as the dots above - this button
 * toggles play/pause in place rather than navigating away. */
.hero-slideshow__pause:focus-visible { opacity: 1; }
@media (hover: hover) {
	.hero-slideshow__pause:hover { opacity: 1; }
}
.hero-slideshow__pause::before,
.hero-slideshow__pause::after {
	content: '';
	position: absolute;
	top: 50%;
	width: 3px;
	height: 12px;
	background: #fff;
	transform: translateY(-50%);
}
/* Re-centred for the 44px box: the two 3px bars span 18-27px, so their
   midpoint (22.5) sits on the box centre, exactly as 10/16px did in the old
   28px box. */
.hero-slideshow__pause::before { left: 18px; }
.hero-slideshow__pause::after { left: 24px; }
.hero-slideshow__pause.is-paused::before {
	width: 0;
	height: 0;
	background: transparent;
	border-style: solid;
	border-width: 7px 0 7px 11px;
	border-color: transparent transparent transparent #fff;
	/* The 11px-wide triangle needs its own offset - it inherits the bar's
	   left above, which would sit it off-centre in the 44px box. */
	left: 17px;
}
.hero-slideshow__pause.is-paused::after { content: none; }

/*
 * Shorter hero on phones (2026-08-26, Kedyn's call).
 *
 * The base rule above is calc(100vh - 100px), which on a 375x812 phone
 * renders a 375x712 box - an aspect ratio of 0.53 holding photographs whose
 * own ratios run 1.48 to 2.21. Because object-fit is cover and the box is
 * portrait, the image scales to fill the HEIGHT and the width overflows, so
 * only a narrow centre strip survives. Measured on a live 375px render, the
 * five slides showed just 28 / 31 / 34 / 34 / 23 percent of their width -
 * which is why the green statue on the Venice dome was sliced, the lit rock
 * formation lost its left side, and the Florence duomo (the worst case, a
 * 2.21 panorama) vanished off the right edge entirely.
 *
 * 62vh re-measured on the same render: the box becomes 375x503, ratio 0.72,
 * and the slides show 40 / 44 / 48 / 48 / 32 percent - between 9 and 14
 * points more of every photograph, before any focal point is applied. The
 * two compose: this widens the window, per-photo focal points aim it.
 *
 * A near-full-viewport hero also pushes every bit of real content below the
 * fold on a phone, so this is worth doing on its own merits.
 *
 * min-height drops from the base rule's 480px because on a short phone
 * (a 667px-tall SE) 480 would be 72% of the screen and would simply
 * re-impose the problem this rule exists to fix; 360 keeps a floor without
 * overriding the intent. max-height is untouched - 860 cannot bind here.
 */
@media (max-width: 767px) {
	.hero-slideshow {
		height: 62vh;
		min-height: 360px;
	}
}
