/**
 * Najam Relief: motion layer (spec E). Owned by MOTION, enqueued on every page, loaded last.
 *
 * "Settle, don't bounce." Motion explains content and never decorates. Only transform
 * properties (translate, scale), opacity and clip-path are animated. Nothing lifts on hover.
 *
 * Gating:
 * - Hidden start states exist only under html.js-motion, which motion.js sets once JS runs and
 *   prefers-reduced-motion is no-preference, after marking everything already on screen .is-in.
 *   Without JS, with reduced motion, or after any motion.js failure, everything is visible.
 * - The hero entrance and scroll-driven parallax are CSS only. They run under html.js (set by the
 *   inline head script) and prefers-reduced-motion: no-preference, so they start at first paint.
 *
 * Contract (F3): [data-reveal], [data-stagger], [data-enter] + --enter-i, [data-parallax] +
 * --parallax, .count, .progress__bar > span (--p), ::view-transition-*, .lenis*.
 * Classes set by motion.js: .js-motion (html), .is-in and .is-settled (revealed elements).
 */

/* 1. Scrolling (E8) ------------------------------------------------------- */
html {
	scroll-padding-top: calc(var(--header-h, 4rem) + 16px);
}

@media (prefers-reduced-motion: no-preference) {
	html {
		scroll-behavior: smooth;
	}
}

/* Lenis 1.3.23 recommended CSS, plus the native smooth-scroll override. */
html.lenis,
html.lenis body {
	height: auto;
}

.lenis.lenis-smooth {
	scroll-behavior: auto !important;
}

.lenis.lenis-smooth iframe {
	pointer-events: none;
}

.lenis.lenis-stopped {
	overflow: clip;
}

.lenis [data-lenis-prevent] {
	overscroll-behavior: contain;
}

/* 2. Reveals (E2) --------------------------------------------------------- */
:is([data-reveal="unmask"], [data-enter="media"]) {
	--reveal-r: var(--radius-m);
}

[data-enter="media"] {
	--reveal-r: var(--radius-l);
}

:is([data-reveal="unmask"], [data-enter="media"]).media--r-s {
	--reveal-r: var(--radius-s);
}

:is([data-reveal="unmask"], [data-enter="media"]).media--r-m {
	--reveal-r: var(--radius-m);
}

:is([data-reveal="unmask"], [data-enter="media"]).media--r-l {
	--reveal-r: var(--radius-l);
}

/* Start states: below-the-fold only, never while something inside has focus. */
.js-motion [data-reveal]:not([data-reveal="unmask"], .is-in, :focus-within) {
	opacity: 0;
}

.js-motion :is([data-reveal=""], [data-reveal="up"]):not(.is-in, :focus-within) {
	translate: 0 28px;
}

.js-motion [data-reveal="scale"]:not(.is-in, :focus-within) {
	scale: 0.96;
}

.js-motion [data-reveal="unmask"]:not(.is-in, :focus-within) {
	clip-path: inset(0 0 12% 0 round var(--reveal-r));
}

.js-motion [data-reveal="unmask"]:not(.is-in, :focus-within) > img {
	scale: calc(var(--img-base, 1) * 1.06);
}

.js-motion [data-reveal="unmask"].is-in:not(.is-settled) {
	clip-path: inset(0 round var(--reveal-r));
}

/* Transitions exist only between .is-in and .is-settled: the hide is never animated and
   later hover transitions are never delayed by the stagger. */
.js-motion [data-reveal].is-in:not(.is-settled) {
	transition: opacity var(--dur-reveal) var(--ease-out), translate var(--dur-reveal) var(--ease-out);
	transition-delay: calc(min(var(--i, 0), 5) * var(--stagger));
}

.js-motion [data-reveal="fade"].is-in:not(.is-settled) {
	transition-duration: 500ms;
}

.js-motion [data-reveal="scale"].is-in:not(.is-settled) {
	transition-property: opacity, scale;
	transition-duration: 600ms;
}

.js-motion [data-reveal="unmask"].is-in:not(.is-settled) {
	transition-property: clip-path;
	transition-duration: var(--dur-slow);
}

.js-motion [data-reveal="unmask"].is-in:not(.is-settled) > img {
	transition: scale var(--dur-slow) var(--ease-out);
	transition-delay: calc(min(var(--i, 0), 5) * var(--stagger));
}

/* 3. Progress fills (E5) -------------------------------------------------- */
.js-motion .progress__bar > span {
	transform-origin: 0 50%;
}

.js-motion .progress__bar.is-in > span {
	transition: scale 1000ms var(--ease-out) calc(200ms + min(var(--i, 0), 5) * 80ms);
}

.js-motion .progress__bar:not(.is-in) > span,
.js-motion [data-reveal]:not(.is-in, :focus-within) .progress__bar > span {
	scale: 0 1;
}

/* 4. Counters (E4) -------------------------------------------------------- */
/* motion.js locks min-width to the final width while counting, so nothing shifts. */
.count {
	display: inline-block;
	font-variant-numeric: tabular-nums;
}

/* 5. Hero entrance (E3), CSS only ----------------------------------------- */
/* Text never starts at opacity 0, so any hero text block can still be the LCP element. */
:where([data-enter="line"]) {
	display: inline-block;
}

@media (prefers-reduced-motion: no-preference) {
	.js [data-enter] {
		animation: najam-enter 700ms var(--ease-out) backwards;
		animation-delay: calc(var(--enter-i, 0) * 90ms);
	}

	.js [data-enter="line"] {
		animation-name: najam-enter-line;
	}

	.js [data-enter="card"] {
		animation-name: najam-enter-card;
	}

	/* Hero media is painted from frame 1: the frame unclips while the image settles. */
	.js [data-enter="media"] {
		animation: najam-unclip 1.1s var(--ease-out) backwards;
		animation-delay: calc(var(--enter-i, 0) * 90ms);
	}

	.js [data-enter="media"] > img {
		animation: najam-settle 1.6s var(--ease-out) backwards;
	}

	/* Progress inside an entering element (live card, board rows) fills once it has arrived. */
	.js [data-enter] .progress__bar > span {
		transform-origin: 0 50%;
		animation: najam-fill 1000ms var(--ease-out) backwards;
		animation-delay: calc(var(--enter-i, 0) * 90ms + 700ms);
	}
}

@keyframes najam-enter {
	from {
		opacity: 0.01;
		translate: 0 16px;
	}
}

@keyframes najam-enter-line {
	from {
		opacity: 0.01;
		translate: 0 0.35em;
	}
}

@keyframes najam-enter-card {
	from {
		opacity: 0.01;
		translate: 0 24px;
	}
}

@keyframes najam-unclip {
	from {
		clip-path: inset(3% round var(--reveal-r));
	}

	to {
		clip-path: inset(0 round var(--reveal-r));
	}
}

@keyframes najam-settle {
	from {
		scale: calc(var(--img-base, 1) * 1.06);
	}

	to {
		scale: var(--img-base, 1);
	}
}

@keyframes najam-fill {
	from {
		scale: 0 1;
	}
}

/* 6. Parallax (E6), scroll-driven CSS only; Firefox keeps static images ---- */
/* Images drift inside their fixed .media frame; frames and text never move. Clamped to ±6%. */
@supports (animation-timeline: view()) {
	@media (prefers-reduced-motion: no-preference) {
		.js [data-parallax] > img {
			--img-base: 1.12;
			scale: 1.12;
			animation: najam-drift linear both;
			animation-timeline: view();
			animation-range: cover;
		}

		.js [data-enter="media"][data-parallax] > img {
			animation: najam-settle 1.6s var(--ease-out) backwards, najam-drift linear both;
			animation-timeline: auto, view();
			animation-range: normal, cover;
		}
	}
}

@keyframes najam-drift {
	from {
		translate: 0 calc(clamp(-6, var(--parallax, 4), 6) * -1%);
	}

	to {
		translate: 0 calc(clamp(-6, var(--parallax, 4), 6) * 1%);
	}
}

/* 7. Press feedback (E9) -------------------------------------------------- */
/* Zero specificity: component rules win. A keyframe press leaves the owners' transition lists alone. */
@media (prefers-reduced-motion: no-preference) {
	:where(.button:not([aria-disabled="true"]):active) {
		animation: najam-press 80ms var(--ease-out) forwards;
	}

	:where(.icon-btn:not(:disabled, [aria-disabled="true"]):active) {
		animation: najam-press-icon 80ms var(--ease-out) forwards;
	}
}

@keyframes najam-press {
	to {
		scale: 0.98;
	}
}

@keyframes najam-press-icon {
	to {
		scale: 0.94;
	}
}

/* 8. Page transitions (E7), cross-document View Transitions ---------------- */
@media (prefers-reduced-motion: no-preference) {
	@view-transition {
		navigation: auto;
	}
}

/* The header is captured on its own and swaps in place, so it never fades or slides. */
.site-header {
	view-transition-name: site-header;
}

::view-transition-group(*) {
	animation-duration: 420ms;
	animation-timing-function: var(--ease-out);
}

::view-transition-group(site-header) {
	animation: none;
}

::view-transition-old(root) {
	animation: 180ms var(--ease-in-out) both najam-fade-out;
}

::view-transition-new(root) {
	animation: 320ms var(--ease-out) both najam-fade-up;
}

/* Program prev/next links (data-vt-dir): motion.js adds the type on pagereveal. */
html:active-view-transition-type(next)::view-transition-new(root) {
	animation-name: najam-slide-from-right;
}

html:active-view-transition-type(prev)::view-transition-new(root) {
	animation-name: najam-slide-from-left;
}

@keyframes najam-fade-out {
	to {
		opacity: 0;
	}
}

@keyframes najam-fade-up {
	from {
		opacity: 0;
		translate: 0 8px;
	}
}

@keyframes najam-slide-from-right {
	from {
		opacity: 0;
		translate: 24px 0;
	}
}

@keyframes najam-slide-from-left {
	from {
		opacity: 0;
		translate: -24px 0;
	}
}

/* 9. Reduced motion safety net (E1) --------------------------------------- */
@media (prefers-reduced-motion: reduce) {
	*,
	*::before,
	*::after {
		animation-duration: 0.01ms !important;
		animation-iteration-count: 1 !important;
		transition-duration: 0.01ms !important;
		scroll-behavior: auto !important;
	}

	/* Same-document transitions (Appeals filter) become instant too. */
	::view-transition-group(*),
	::view-transition-old(*),
	::view-transition-new(*) {
		animation: none !important;
	}
}

/* 10. Print: never print a hidden or half-drawn state --------------------- */
@media print {
	.js-motion [data-reveal]:not(.is-in),
	.js-motion [data-reveal]:not(.is-in) > img,
	.js-motion :is(.progress__bar:not(.is-in), [data-reveal]:not(.is-in) .progress__bar) > span {
		opacity: 1 !important;
		translate: none !important;
		scale: none !important;
		clip-path: none !important;
	}

	[data-enter],
	[data-enter] > img,
	[data-enter] .progress__bar > span {
		animation: none !important;
	}

	[data-parallax] > img {
		animation: none !important;
		scale: none !important;
	}
}
