/* CSS scroll-behavior:smooth fights GSAP ScrollTrigger's scrub -- the browser
   tries to animate every scroll change itself while ScrollTrigger expects to
   read raw, instant scroll position, producing exactly the jumpy/laggy feel
   this is meant to prevent. A stylesheet !important rule beats any inline
   style.scrollBehavior a script sets later, regardless of load order.
   Gated on a class the JS adds only while a scrubbed telescope is live
   (buildTelescope, reference-counted) instead of applying to every page that
   merely loads this stylesheet: mobile skips the telescope entirely, so it has
   no reason to lose the site's smooth anchor scrolling. */
html.d500-hts-no-smooth-scroll {
	scroll-behavior: auto !important;
}

/* Provides the scroll distance for the whole sequence -- its height (stage height + the
   animation's scroll distance, set by JS) is what .d500-hts-stage's position:sticky sticks *within*.
   Native sticky positioning, not a GSAP transform-based pin, is what keeps the stage genuinely
   locked with zero rounding drift while scrolling through this. */
.d500-hts-scroll-track {
	position: relative;
}

.d500-hts-stage {
	/* relative + top:0 is the pre-JS fallback (normal flow, no stickiness). JS sets
	   position:sticky and --d500-hts-lock-top together once it has measured the stage's true natural
	   distance from the viewport top, so there's no window where sticky is active with a wrong
	   top value. */
	position: relative;
	top: var(--d500-hts-lock-top, 0px);
	overflow: hidden;
	min-height: 100vh;
}

.d500-hts-thumbs {
	position: absolute;
	inset: 0;
	max-width: 1140px;
	margin: 0 auto;
}

/* GSAP owns .d500-hts-thumb's transform for both the scroll explosion and pointer parallax -- they
   never run at once (see buildTelescope()'s EXPLOSION_START threshold, which resets and disables
   parallax before the explosion's own tween ever touches this element), so there's no conflict
   sharing it. No overflow:hidden needed here: since the whole tile (frame + image) moves as one
   rigid unit for parallax, there's nothing to crop -- unlike moving just the inner content within
   a static frame, which would need an overscan to avoid exposing an edge. */
.d500-hts-thumb {
	position: absolute;
}

/* The reveal animation runs on this element so its CSS keyframe never fights GSAP's inline
   transform on .d500-hts-thumb.
   position:relative + overflow:hidden turn it into the tile's *frame*: it's what positions
   .d500-hts-thumb-overlay and what clips the hover effects that scale the image past the tile edge
   (zoom, focus, tilt) plus the shine sweep, and what makes a border-radius actually round the
   image's corners. Nothing overflowed here before these effects existed, so adding the clip
   changes nothing for a thumbnail whose Hover Effect is "None". */
.d500-hts-thumb-inner {
	width: 100%;
	height: 100%;
	position: relative;
	overflow: hidden;
}

.d500-hts-thumb-inner img,
.d500-hts-thumb-inner video {
	width: 100%;
	height: 100%;
	object-fit: cover;
	display: block;
}

/* -- Per-thumbnail hover effects (driven by data-d500-hts-hover on .d500-hts-thumb) --------------------

   Every rule below targets .d500-hts-thumb-img or .d500-hts-thumb-overlay, NEVER .d500-hts-thumb (GSAP owns its
   transform for parallax + the explosion) and never .d500-hts-thumb-inner's transform/opacity (its
   entrance keyframe uses animation-fill-mode:forwards, and a CSS animation's fill state outranks
   normal author declarations -- even inline ones -- so anything we set there would be ignored the
   moment a reveal animation is chosen). The image and the overlay are unowned layers, so these
   effects compose with the existing parallax and explosion instead of competing with them.

   The data attribute is only rendered when an effect is actually selected, so a thumbnail left on
   "None" is byte-for-byte the previous behaviour.

   Gating:
   - @media (hover:hover) and (pointer:fine) -- on touch, :hover latches after a tap and never
     clears, which would leave a thumbnail stuck mid-effect.
   - :not(.d500-hts-hover-lock) on the stage -- JS adds .d500-hts-hover-lock at the same instant it suspends
     pointer parallax (EXPLOSION_START), so a cursor parked over a thumbnail can't hold it zoomed
     or tinted while it flies off screen. Dropping the hover rule lets it transition back out
     normally instead of snapping. */
.d500-hts-thumb[data-d500-hts-hover] .d500-hts-thumb-img {
	transition:
		transform var(--d500-hts-hover-duration, 450ms) var(--d500-hts-hover-ease, cubic-bezier(0.22, 0.61, 0.36, 1)),
		filter var(--d500-hts-hover-duration, 450ms) linear;
}

/* Tilt is written frame-by-frame by GSAP (initHoverTilt); a CSS transform transition layered on
   top of per-frame inline writes smears the cursor-follow into a laggy drift. */
.d500-hts-thumb[data-d500-hts-hover="tilt"] .d500-hts-thumb-img {
	transition: none;
	will-change: transform;
	backface-visibility: hidden;
}

.d500-hts-thumb-overlay {
	position: absolute;
	inset: 0;
	z-index: 2;
	pointer-events: none;
}

.d500-hts-thumb-overlay--tint {
	background: var(--d500-hts-hover-tint, rgba(147, 2, 54, 0.45));
	opacity: 0;
	transition: opacity var(--d500-hts-hover-duration, 450ms) ease;
}

/* A diagonal band parked entirely off the left edge; on hover it travels to entirely off the
   right edge, and the frame's overflow:hidden is what turns that into a glare crossing the image.
   Slower than the other effects (1.8x) -- a sweep at zoom speed reads as a flicker. */
.d500-hts-thumb-overlay--shine {
	background: linear-gradient(
		105deg,
		rgba(255, 255, 255, 0) 35%,
		rgba(255, 255, 255, 0.45) 50%,
		rgba(255, 255, 255, 0) 65%
	);
	transform: translateX(-110%);
	transition: transform calc(var(--d500-hts-hover-duration, 450ms) * 1.8) ease;
}

/* Resting states for the effects that start away from neutral and return to it on hover.
   --d500-hts-hover-zoom is a plain number (110 = 110%) so it can be divided inside calc(); scale()
   takes no percentage. */
.d500-hts-thumb[data-d500-hts-hover="zoom-out"] .d500-hts-thumb-img {
	transform: scale(calc(var(--d500-hts-hover-zoom, 110) / 100));
}

.d500-hts-thumb[data-d500-hts-hover="grayscale"] .d500-hts-thumb-img {
	filter: grayscale(1);
}

/* The 1.06 scale isn't decorative: blur() bleeds transparent pixels in from the edges, and
   overscanning the image past the frame is what hides them. */
.d500-hts-thumb[data-d500-hts-hover="focus"] .d500-hts-thumb-img {
	filter: blur(6px) brightness(0.88);
	transform: scale(1.06);
}

/* Safe to put on .d500-hts-thumb itself precisely because it names only box-shadow -- a `transition:
   all` here would try to interpolate the transform GSAP writes every frame. */
.d500-hts-thumb[data-d500-hts-hover="glow"] {
	transition: box-shadow var(--d500-hts-hover-duration, 450ms) ease;
}

@media (hover: hover) and (pointer: fine) {
	.d500-hts-stage:not(.d500-hts-hover-lock) .d500-hts-thumb[data-d500-hts-hover="zoom-in"]:hover .d500-hts-thumb-img {
		transform: scale(calc(var(--d500-hts-hover-zoom, 110) / 100));
	}

	.d500-hts-stage:not(.d500-hts-hover-lock) .d500-hts-thumb[data-d500-hts-hover="zoom-out"]:hover .d500-hts-thumb-img {
		transform: scale(1);
	}

	.d500-hts-stage:not(.d500-hts-hover-lock) .d500-hts-thumb[data-d500-hts-hover="grayscale"]:hover .d500-hts-thumb-img {
		filter: grayscale(0);
	}

	.d500-hts-stage:not(.d500-hts-hover-lock) .d500-hts-thumb[data-d500-hts-hover="desaturate"]:hover .d500-hts-thumb-img {
		filter: grayscale(1);
	}

	.d500-hts-stage:not(.d500-hts-hover-lock) .d500-hts-thumb[data-d500-hts-hover="focus"]:hover .d500-hts-thumb-img {
		filter: none;
		transform: scale(1);
	}

	.d500-hts-stage:not(.d500-hts-hover-lock) .d500-hts-thumb[data-d500-hts-hover="tint"]:hover .d500-hts-thumb-overlay--tint {
		opacity: 1;
	}

	.d500-hts-stage:not(.d500-hts-hover-lock) .d500-hts-thumb[data-d500-hts-hover="shine"]:hover .d500-hts-thumb-overlay--shine {
		transform: translateX(110%);
	}

	.d500-hts-stage:not(.d500-hts-hover-lock) .d500-hts-thumb[data-d500-hts-hover="glow"]:hover {
		box-shadow: 0 24px 60px -18px var(--d500-hts-hover-glow, rgba(0, 0, 0, 0.6));
	}
}

/* The three effects above whose RESTING state is the "before" (zoom-out sitting at 110%, grayscale
   sitting desaturated, focus sitting blurred) only resolve on :hover -- which never fires on a
   touch device, so they'd be stuck in the "before" state permanently, with a blurred hero image as
   the worst case. Reset those to neutral wherever hover isn't available; the effects that rest at
   neutral (zoom-in, desaturate, shine, tint, glow) simply do nothing there, which is correct. */
@media (hover: none), (pointer: coarse) {
	.d500-hts-thumb[data-d500-hts-hover="zoom-out"] .d500-hts-thumb-img,
	.d500-hts-thumb[data-d500-hts-hover="focus"] .d500-hts-thumb-img {
		transform: none;
		filter: none;
	}

	.d500-hts-thumb[data-d500-hts-hover="grayscale"] .d500-hts-thumb-img {
		filter: none;
	}
}

/* Motion-based hover effects off; colour-based ones (grayscale/desaturate/tint) stay, since a
   cross-fade of colour isn't motion. Placed after the block above so it wins on source order at
   equal specificity -- no !important needed. `focus` loses its blur here too: without the 1.06
   overscan (killed by transform:none) the blur would expose transparent edges. */
@media (prefers-reduced-motion: reduce) {
	.d500-hts-thumb[data-d500-hts-hover] .d500-hts-thumb-img,
	.d500-hts-stage:not(.d500-hts-hover-lock) .d500-hts-thumb[data-d500-hts-hover]:hover .d500-hts-thumb-img {
		transform: none;
	}

	.d500-hts-thumb[data-d500-hts-hover="focus"] .d500-hts-thumb-img {
		filter: none;
	}

	.d500-hts-thumb-overlay--shine {
		display: none;
	}
}

/* padding-top and text-align are set via Elementor's Style > Main Text controls
   (Position & Width) so they're editable per-instance without touching CSS.
   max-width uses a fluid clamp() by default so the text column shrinks smoothly
   between the scattered images on any screen width (not just at Elementor's fixed
   breakpoints) -- an explicit value set in that control still overrides this. */
.d500-hts-text {
	position: relative;
	z-index: 5;
	margin: 0 auto;
	max-width: clamp(320px, 45vw, 700px);
}

/* The theme's flat 64px h1 size doesn't shrink between breakpoints and crowds the
   scattered images at mid-width screens (~1024-1400px); clamp() keeps it fluid. */
.d500-hts-heading {
	font-size: clamp(2.25rem, 1.5rem + 1.6vw, 4rem);
}

/* Held at opacity:0 until initEntrance() fades them in (heading -> subheading -> buttons,
   staggered) -- keeps this in CSS rather than a JS-only gsap.set() so there's no flash of full
   opacity content before the script runs, matching the .d500-hts-reveal convention above. */
.d500-hts-heading,
.d500-hts-subheading,
.d500-hts-buttons {
	opacity: 0;
}

.d500-hts-buttons {
	display: flex;
	gap: 1rem;
	justify-content: center;
	margin-top: 1.5rem;
}

.d500-hts-button {
	display: inline-block;
	padding: 0.75em 1.75em;
	border-radius: 999px;
	text-decoration: none;
	font-weight: 600;
}

/* No `background: currentColor` fallback here on purpose: it makes the label the exact same colour
   as the fill, so clearing the Background Color control in the Style tab produced an invisible
   button instead of a transparent one. The control ships its own default (#930236), which is what
   fills this in practice. */
.d500-hts-button--primary {
	background: transparent;
}

.d500-hts-button--secondary {
	background: transparent;
	border: 2px solid currentColor;
}

.d500-hts-imagebreak {
	position: absolute;
	top: 50%;
	left: 50%;
	width: 100%;
	height: 100%;
	transform: translate(-50%, -50%);
	z-index: -1;
	visibility: hidden;
}

.d500-hts-zoom-caption {
	position: absolute;
	top: 50%;
	left: 50%;
	max-width: 700px;
	padding: 0 1.5rem;
	transform: translate(-50%, -50%);
	z-index: 6;
	text-align: center;
	color: #fff;
	font-size: clamp(1.1rem, 1rem + 1vw, 1.75rem);
	visibility: hidden;
}

.d500-hts-imagebreak img,
.d500-hts-imagebreak video {
	width: 100%;
	height: 100%;
	object-fit: cover;
	display: block;
}

/* -- Entrance reveal animations, played once via IntersectionObserver adding .d500-hts-in -- */
.d500-hts-reveal {
	opacity: 0;
	animation-duration: 0.7s;
	animation-timing-function: ease;
	animation-fill-mode: forwards;
}

.d500-hts-reveal--scale-up {
	transform: scale(0.6);
}
.d500-hts-reveal--scale-up.d500-hts-in {
	animation-name: d500-hts-scale-up;
}
@keyframes d500-hts-scale-up {
	to { transform: scale(1); opacity: 1; }
}

.d500-hts-reveal--fade.d500-hts-in {
	animation-name: d500-hts-fade;
}
@keyframes d500-hts-fade {
	to { opacity: 1; }
}

.d500-hts-reveal--slide-up {
	transform: translateY(40px);
}
.d500-hts-reveal--slide-up.d500-hts-in {
	animation-name: d500-hts-slide-up;
}
@keyframes d500-hts-slide-up {
	to { transform: translateY(0); opacity: 1; }
}

/* Placed last so these overrides win the cascade against the unconditional rules above
   (same specificity, source order decides) without needing !important. Mobile drops the
   pinned scroll-telescope entirely (see the isMobile branch in hero-telescope.js, which skips
   buildTelescope() and never sets position:sticky or the fixed stage height) -- so this just
   lays the same markup out as a normal stacked section: heading/text, then thumbnails as a
   native CSS-columns masonry grid, then the break image, in that visual order via flexbox
   `order` regardless of their source order in the DOM. */
@media (max-width: 767px) {
	.d500-hts-thumb.d500-hts-hide-mobile {
		display: none;
	}

	.d500-hts-stage {
		display: flex;
		flex-direction: column;
		overflow: visible;
	}

	.d500-hts-text {
		order: 1;
	}

	.d500-hts-thumbs {
		order: 2;
		position: static;
		max-width: none;
		margin: 2rem 0;
		padding: 0 1rem;
		columns: 2;
		column-gap: 12px;
	}

	/* !important: beats Elementor's per-item responsive width/height/top/left controls
	   (.elementor-element-{id} .elementor-repeater-item-{id}), which are more specific than a
	   single class selector here and are meant for the desktop/tablet absolute-position layout. */
	.d500-hts-thumb {
		position: static !important;
		width: 100% !important;
		height: auto !important;
		margin: 0 0 12px !important;
		break-inside: avoid;
	}

	.d500-hts-imagebreak {
		order: 3;
		position: static;
		width: 100%;
		height: auto;
		transform: none;
		z-index: auto;
		visibility: visible;
	}

	.d500-hts-imagebreak img,
	.d500-hts-imagebreak video {
		height: auto;
		object-fit: initial;
	}

	.d500-hts-zoom-caption {
		order: 4;
		position: static;
		transform: none;
		visibility: visible;
		margin: 1rem auto 0;
	}
}
