/* =========================================================================
   MOTION
   =========================================================================

   Loaded second, depends on nothing in style.css, and removing it leaves a
   complete page. Everything is behind prefers-reduced-motion, and the
   scroll-driven work additionally behind @supports — where view timelines
   are missing the page sits in its finished state rather than a degraded
   one.
   ========================================================================= */

/* ------------------------------------------------------------- entrance */

@media (prefers-reduced-motion: no-preference) {
	.hero__copy > * {
		opacity: 0;
		animation: rise 1s var(--ease) forwards;
	}

	.hero__copy > *:nth-child(1) { animation-delay: 80ms; }
	.hero__copy > *:nth-child(2) { animation-delay: 170ms; }
	.hero__copy > *:nth-child(3) { animation-delay: 280ms; }
	.hero__copy > *:nth-child(4) { animation-delay: 390ms; }
	.hero__copy > *:nth-child(5) { animation-delay: 500ms; }

	@keyframes rise {
		from { opacity: 0; transform: translateY(1.6rem); }
		to { opacity: 1; transform: none; }
	}

	/* The three plates arrive one after another. Each keyframe has to END on the
	   exact transform the stylesheet lays it out with, or the stack snaps flat the
	   moment the animation finishes — which is what happened the first time. */
	.stack__item { opacity: 0; }
	.stack__item:nth-child(1) { animation: plate-1 1.15s var(--ease) 240ms forwards; }
	.stack__item:nth-child(2) { animation: plate-2 1.15s var(--ease) 380ms forwards; }
	.stack__item:nth-child(3) { animation: plate-3 1.15s var(--ease) 520ms forwards; }

	@keyframes plate-1 {
		from { opacity: 0; transform: rotateY(10deg) rotateX(2deg) translate3d(3rem, 2rem, -140px); }
		to { opacity: 1; transform: rotateY(10deg) rotateX(2deg); }
	}
	@keyframes plate-2 {
		from { opacity: 0; transform: rotateY(-8deg) translate3d(4rem, 3rem, -180px); }
		to { opacity: 1; transform: rotateY(-8deg); }
	}
	@keyframes plate-3 {
		from { opacity: 0; transform: rotateY(6deg) translate3d(-2rem, 3rem, -20px); }
		to { opacity: 1; transform: rotateY(6deg) translateZ(70px); }
	}
}

/* ------------------------------------------------------- scroll reveals */

@media (prefers-reduced-motion: no-preference) {
	@supports (animation-timeline: view()) {
		.reveal {
			animation: reveal linear both;
			animation-timeline: view();
			animation-range: entry 6% cover 30%;
		}
		@keyframes reveal {
			from { opacity: 0; transform: translateY(2.2rem); }
			to { opacity: 1; transform: none; }
		}

		/* Glass panes arrive with depth rather than with a slide. */
		.tile, .step, .cta {
			animation: pane linear both;
			animation-timeline: view();
			animation-range: entry 4% cover 26%;
		}
		@keyframes pane {
			from { opacity: 0; transform: perspective(1400px) translateY(2.4rem) translateZ(-90px); }
			to { opacity: 1; transform: perspective(1400px) translateY(0) translateZ(0); }
		}

		/* The hero recedes as the page leaves it, rather than simply scrolling off. */
		.hero__grid {
			animation: hero-out linear both;
			animation-timeline: view();
			animation-range: exit 0% exit 85%;
		}
		@keyframes hero-out {
			to { opacity: 0; transform: translateY(-3rem) scale(0.97); }
		}

		.progress {
			animation: progress linear;
			animation-timeline: scroll(root block);
		}
		@keyframes progress { to { transform: scaleX(1); } }
	}
}

.progress {
	position: fixed;
	inset: 0 0 auto 0;
	height: 2px;
	z-index: 95;
	background: linear-gradient(90deg, var(--bronze), var(--violet));
	transform-origin: left;
	transform: scaleX(0);
	pointer-events: none;
}

/* ---------------------------------------------------------------- grain

   One inlined SVG turbulence tile. It is what stops a dark interface from
   reading as flat digital black, and it costs no request.                 */

body::after {
	content: '';
	position: fixed;
	inset: -50%;
	z-index: 60;
	pointer-events: none;
	opacity: 0.036;
	background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='200' height='200' filter='url(%23n)'/%3E%3C/svg%3E");
	will-change: transform;
}

@media (prefers-reduced-motion: no-preference) {
	body::after { animation: grain 7s steps(7) infinite; }
	@keyframes grain {
		0% { transform: translate(0, 0); }
		14% { transform: translate(-2%, -3%); }
		28% { transform: translate(-4%, 2%); }
		42% { transform: translate(2%, -4%); }
		57% { transform: translate(-1%, 3%); }
		71% { transform: translate(3%, 1%); }
		85% { transform: translate(-3%, -1%); }
		100% { transform: translate(0, 0); }
	}
}

/* ------------------------------------------------- cross-document motion */

@view-transition { navigation: auto; }

::view-transition-old(root),
::view-transition-new(root) {
	animation-duration: 420ms;
	animation-timing-function: var(--ease);
}

@media (prefers-reduced-motion: reduce) {
	::view-transition-old(root),
	::view-transition-new(root) { animation: none; }
}

/* The phone version of the stack is a flat grid, so the plates' 3D entrance has nothing to
   land on — `transform: none !important` in style.css overrides the keyframe's end state and
   the movement would simply vanish. A short rise replaces it: same arrival, one axis. */
@media (prefers-reduced-motion: no-preference) and (max-width: 640px) {
	.stack__item:nth-child(1),
	.stack__item:nth-child(2),
	.stack__item:nth-child(3) { animation: plate-flat 900ms var(--ease) both; }
	.stack__item:nth-child(2) { animation-delay: 110ms; }
	.stack__item:nth-child(3) { animation-delay: 220ms; }

	@keyframes plate-flat {
		from { opacity: 0; translate: 0 1.4rem; }
		to { opacity: 1; translate: none; }
	}
}
