/*
 * Motion states. GSAP (ax-motion.js) animates; this file only provides:
 *  1. pre-reveal states for above-the-fold content (avoids a flash before JS),
 *  2. a CSS failsafe that reveals content if JS never runs,
 *  3. split-line structure, and
 *  4. cross-document View Transitions for page changes.
 * Everything here is disabled under prefers-reduced-motion.
 */

/* Split-text lines created by ax-motion.js. */
.ax-line {
	display: block;
	overflow: hidden;
	/* Room for descenders inside the mask. */
	padding-bottom: 0.1em;
	margin-bottom: -0.1em;
}

.ax-line__inner {
	display: block;
	will-change: transform;
}

@media (prefers-reduced-motion: no-preference) {
	/* Above-the-fold elements start hidden only when JS is present. */
	.ax-js [data-ax-hero] [data-reveal],
	.ax-js .ax-article__header [data-reveal],
	.ax-js .ax-header {
		visibility: hidden;
	}

	/* Failsafe: if motion has not initialised after 2.5s, show everything. */
	.ax-js:not(.ax-motion-ready) [data-ax-hero] [data-reveal],
	.ax-js:not(.ax-motion-ready) .ax-article__header [data-reveal],
	.ax-js:not(.ax-motion-ready) .ax-header {
		animation: ax-failsafe 0s 2.5s forwards;
	}

	/* Page transitions between documents (Chrome/Edge 126+, Safari 18.2+). */
	@view-transition {
		navigation: auto;
	}

	::view-transition-old(root) {
		animation: ax-vt-out 420ms var(--ax-ease-in-out) both;
	}

	::view-transition-new(root) {
		animation: ax-vt-in 620ms var(--ax-ease-out) both;
	}

	/* The header stays put while the page underneath changes. */
	::view-transition-group(ax-header) {
		animation: none;
	}
}

/*
 * Editorial heading text fill (ax-motion.js → scrollFill). Without the
 * `.ax-fill-ready` class (no JS, no GSAP, reduced motion) the heading keeps its
 * normal solid colour, so it always reads correctly.
 *   base  = the real heading text, recoloured pale
 *   layer = aria-hidden copies of each rendered line (CSS-generated text),
 *           revealed by a soft-edged mask whose position is --ax-fx.
 */
[data-scroll-fill] {
	position: relative;
	--ax-fill-base-color: var(--ax-fill-base);
	--ax-fill-color: var(--ax-heading);
}

.ax-surface--navy [data-scroll-fill],
.ax-surface--deep [data-scroll-fill] {
	--ax-fill-base-color: var(--ax-fill-base-dark);
}

[data-scroll-fill].ax-fill-ready {
	color: var(--ax-fill-base-color);
}

/* Fully filled: show only the real text in its final colour and hide the copy,
   so the finished heading renders exactly like a plain heading (no pale fringe,
   no doubled glyph edges). */
[data-scroll-fill].ax-fill-ready.ax-fill-complete {
	color: var(--ax-fill-color);
}

[data-scroll-fill].ax-fill-complete .ax-fill__layer {
	visibility: hidden;
}

.ax-fill__layer {
	position: absolute;
	inset: 0;
	color: var(--ax-fill-color);
	pointer-events: none;
	user-select: none;
}

.ax-fill__line {
	--ax-fx: 0px;
	position: absolute;
	white-space: nowrap;
	/* Same font, size, weight, tracking and leading as the heading (inherited). */
	-webkit-mask-image: linear-gradient(90deg, #000 calc(var(--ax-fx) - 48px), transparent var(--ax-fx));
	mask-image: linear-gradient(90deg, #000 calc(var(--ax-fx) - 48px), transparent var(--ax-fx));
}

.ax-fill__line::before {
	content: attr(data-text);
}

/* Forced-colours / high-contrast: show the real text only. */
@media (forced-colors: active) {
	.ax-fill__layer {
		display: none;
	}

	[data-scroll-fill].ax-fill-ready {
		color: CanvasText;
	}
}

/* GSAP failed to load: nothing may stay hidden. */
.ax-motion-off .ax-header,
.ax-motion-off [data-reveal],
.ax-motion-off [data-reveal] > * {
	visibility: visible !important;
	opacity: 1 !important;
}

@keyframes ax-failsafe {
	to {
		visibility: visible;
	}
}

@keyframes ax-vt-out {
	to {
		opacity: 0;
		transform: translateY(-1.5rem);
	}
}

@keyframes ax-vt-in {
	from {
		opacity: 0;
		transform: translateY(2.5rem);
	}
}
