/*
 * HAViK scroll effects
 *
 * Optional motion on scroll, in CSS only: scroll-driven animations
 * (animation-timeline) run by the browser off the main thread. No
 * JavaScript, no scroll listener, and only transform is animated, so
 * nothing is laid out again while scrolling.
 *
 * Every effect is off, and the page renders exactly as it does without
 * this file, when any of these is true:
 *   - the viewer has asked for reduced motion (prefers-reduced-motion)
 *   - the screen is below 768px
 *   - the browser doesn't support scroll-driven animations
 *
 * PARALLAX: .hv-parallax
 *   Drifts the element's ::after layer against the page as it scrolls
 *   through the viewport. The component using it puts what should drift on
 *   that layer (see .hv-topo in components.css); ::before is left alone
 *   because Elementor uses it for container background overlays.
 *
 *   --hv-parallax-speed  how fast the layer moves, as a fraction of scroll
 *                        speed. 0.15 = the layer moves 15% as fast as the
 *                        page, so it lags behind the content.
 *
 *   The layer is exactly in its normal position when the element's centre
 *   is at the viewport's centre, and moves speed × (half the viewport +
 *   half the element) either side of it across the element's pass through
 *   the viewport. It's clipped to the element, with space-24 to spare
 *   (decorations inside the element can reach a little past its edges).
 */

:root .hv-parallax {
	--hv-parallax-speed: 0.15;
}

@media (min-width: 768px) and (prefers-reduced-motion: no-preference) {
	@supports (animation-timeline: view()) {
		:root .hv-parallax {
			position: relative;
			isolation: isolate;
			overflow: clip;
			overflow-clip-margin: var(--hv-space-24);
			view-timeline: --hv-parallax block;
		}

		:root .hv-parallax::after {
			content: "";
			position: absolute;
			inset: 0;
			z-index: -1;
			pointer-events: none;
			will-change: transform;
			animation: hv-parallax-drift linear both;
			/* After the shorthand, which would reset it. */
			animation-timeline: --hv-parallax;
			animation-range: cover;
		}
	}
}

@keyframes hv-parallax-drift {
	from {
		transform: translate3d(0, calc(var(--hv-parallax-speed) * (-50vh - 50%)), 0);
	}
	to {
		transform: translate3d(0, calc(var(--hv-parallax-speed) * (50vh + 50%)), 0);
	}
}

/*
 * SCROLL REVEALS: [data-hv-reveal] (motion.js)
 *   Each plays once, the first time its element comes on screen. The before
 *   state applies only while motion.js has armed the element (.is-armed and
 *   not yet .is-revealed), so with JavaScript off, reduced motion, or an
 *   element already on screen at load, everything is in its final place and
 *   fully visible. Transform and opacity only; --hv-i (set by motion.js,
 *   reading order) staggers the items.
 *
 *   Badge icons (.hv-badge-icon, Badge Card component): fade and rise
 *     space-12 (12px), 550ms each, 140ms apart in reading order (left to
 *     right; 2×2 on phones, top-left through bottom-right), the first one
 *     200ms after the section comes on screen so the heading and copy
 *     register first.
 */

@media (prefers-reduced-motion: no-preference) {
	:root [data-hv-reveal].is-armed:not(.is-revealed) .hv-badge-icon {
		opacity: 0;
		transform: translateY(var(--hv-space-12));
	}

	:root [data-hv-reveal].is-revealed .hv-badge-icon {
		animation: hv-reveal-rise 550ms ease-out both;
		animation-delay: calc(200ms + var(--hv-i, 0) * 140ms);
	}
}

@keyframes hv-reveal-rise {
	from {
		opacity: 0;
		transform: translateY(var(--hv-space-12));
	}
	to {
		opacity: 1;
		transform: none;
	}
}

/*
 * PAINT LAYERS: [data-hv-paint] (motion.js)
 *   Artwork lifted out of a photo so it can be animated: two layers over the
 *   picture, entering in two beats. The homepage hero's elk and drips are the
 *   first pair; other pages use their own artwork and their own placement,
 *   and only the two roles below are shared.
 *
 *   1. [data-hv-paint-in="fade"] fades up: opacity in and 0.96 to 1 over
 *      500ms on a plain ease-out, so it settles rather than arrives.
 *   2. [data-hv-paint-in="plop"] plops: once the fade has finished, it drops
 *      in from 6% above its place over 340ms, on an easing that overshoots
 *      slightly and comes back, which is what reads as a plop rather than a
 *      slide.
 *
 *   The plop's delay is the fade's duration, both from --hv-paint-fade, so
 *   the two can't drift apart if the timing is retuned. `both` holds the
 *   plopping layer out of sight until its turn.
 *
 *   Transform and opacity only — nothing here can move the page.
 *
 *   Both rules need .is-armed, which motion.js adds only when it is about to
 *   play. With JavaScript off, reduced motion, or the script failing, neither
 *   applies and both layers are simply there, final and fully visible. These
 *   layers are above the fold, so that is the case that matters: nothing here
 *   may be what makes the artwork appear.
 */

:root {
	--hv-paint-fade: 500ms;
}

@media (prefers-reduced-motion: no-preference) {
	:root [data-hv-paint].is-armed:not(.is-revealed) [data-hv-paint-in="fade"] {
		opacity: 0;
		transform: scale(0.96);
	}

	:root [data-hv-paint].is-armed:not(.is-revealed) [data-hv-paint-in="plop"] {
		opacity: 0;
		transform: translateY(-6%) scale(0.98);
	}

	:root [data-hv-paint].is-revealed [data-hv-paint-in="fade"] {
		animation: hv-paint-fade var(--hv-paint-fade) cubic-bezier(0.2, 0.6, 0.2, 1) both;
	}

	:root [data-hv-paint].is-revealed [data-hv-paint-in="plop"] {
		animation: hv-paint-plop 340ms cubic-bezier(0.34, 1.4, 0.64, 1) both;
		animation-delay: var(--hv-paint-fade);
	}
}

@keyframes hv-paint-fade {
	from {
		opacity: 0;
		transform: scale(0.96);
	}
	to {
		opacity: 1;
		transform: none;
	}
}

/* Opaque well before it lands, so the overshoot is a movement and not a
   flicker at the end of a fade. */
@keyframes hv-paint-plop {
	from {
		opacity: 0;
		transform: translateY(-6%) scale(0.98);
	}
	55% {
		opacity: 1;
	}
	to {
		opacity: 1;
		transform: none;
	}
}
