/*
 * Motion. Ports the reference build's two moving parts: the homepage hero's
 * load-in sequence, and the blur-fade every other section plays as it enters
 * view. The reference drives both with GSAP; this does not ship GSAP.
 *
 * The hero sequence is a fixed timeline with no input, which is what CSS
 * animations are for — it runs whether or not motion.js loads. Only the scroll
 * work needs script: the entrance trigger, and the portrait parallax that
 * tracks scroll position frame by frame.
 *
 * Nothing here can strand content invisible. The section rules are scoped to
 * html.cobell-motion, a class written by an inline snippet in wp_head — so it
 * is in place before the body paints, it is never added when scripting is off,
 * and that same snippet removes it if motion.js has not reported in after a
 * few seconds. Losing the class un-matches every rule below and the page is
 * simply a static page.
 *
 * Everything is inside prefers-reduced-motion: no-preference. Reduced-motion
 * visitors match none of it and get the final state at first paint.
 *
 * The selector list under "Section entrance" is duplicated in motion.js. The
 * two have to agree: this file decides what starts hidden, that one decides
 * what gets revealed, and anything in one but not the other either never
 * animates or never comes back.
 */

@media ( prefers-reduced-motion: no-preference ) {

	/*
	 * Hero — load-in sequence.
	 *
	 * Timing is the reference's, in order: the headline's first phrase, a
	 * pause, its second phrase, a longer pause, the four portraits, then the
	 * supporting copy. The pauses are the point. The headline is meant to land
	 * as a pair and hold before anything else arrives, so the gaps are longer
	 * than they would be if this were only about sequencing.
	 *
	 * Scoped to the surround layout. The other hero shells are typographic page
	 * headers in the reference too, and they paint statically there.
	 */
	.cobell-hero--surround .cobell-hero__heading-lead,
	.cobell-hero--surround .cobell-hero__heading .cobell-accent,
	.cobell-hero--surround .cobell-hero__eyebrow,
	.cobell-hero--surround .cobell-hero__summary,
	.cobell-hero--surround .cobell-hero__buttons,
	.cobell-hero--surround .cobell-hero__meta-text {
		opacity: 0;
		filter: blur( 14px );
		transform: translateY( 8px );
		animation: cobell-hero-intro 1.1s cubic-bezier( 0.165, 0.84, 0.44, 1 ) forwards;
	}

	.cobell-hero--surround .cobell-hero__heading-lead {
		animation-delay: 0.15s;
	}

	.cobell-hero--surround .cobell-hero__heading .cobell-accent {
		animation-delay: 1.5s;
	}

	.cobell-hero--surround .cobell-hero__eyebrow {
		animation-delay: 3.55s;
	}

	.cobell-hero--surround .cobell-hero__summary {
		animation-delay: 3.67s;
	}

	.cobell-hero--surround .cobell-hero__buttons {
		animation-delay: 3.79s;
	}

	/* The reference's `text` group is one 0.12s stagger in DOM order from 3.55s,
	   and the meta foot is its fourth member. */
	.cobell-hero--surround .cobell-hero__meta-text {
		animation-delay: 3.91s;
	}

	/*
	 * Portraits fade without moving. The reference nudges them 8px as they
	 * arrive; that cannot be done here. motion.js owns the transform on these
	 * tiles for the scroll parallax, and an animation holding its end state
	 * outranks an inline style — a transform in this keyframe would pin the
	 * parallax at zero for good. Eight pixels is not worth a third wrapper
	 * element to animate on.
	 *
	 * The stagger runs in no particular order. The reference shuffles it per
	 * load so the four tiles do not resolve as a sweep; a fixed scramble reads
	 * the same and needs no script.
	 */
	.cobell-hero--surround .cobell-hero__portrait {
		opacity: 0;
		filter: blur( 14px );
		animation: cobell-hero-portrait 1.3s cubic-bezier( 0.165, 0.84, 0.44, 1 ) forwards;
	}

	.cobell-hero--surround .cobell-hero__portrait--right-top {
		animation-delay: 3.1s;
	}

	.cobell-hero--surround .cobell-hero__portrait--left-bottom {
		animation-delay: 3.32s;
	}

	.cobell-hero--surround .cobell-hero__portrait--right-bottom {
		animation-delay: 3.54s;
	}

	.cobell-hero--surround .cobell-hero__portrait--left-top {
		animation-delay: 3.76s;
	}

	@keyframes cobell-hero-intro {
		to {
			opacity: 1;
			filter: blur( 0 );
			transform: none;
		}
	}

	@keyframes cobell-hero-portrait {
		to {
			opacity: 1;
			filter: blur( 0 );
		}
	}

	/*
	 * Section entrance.
	 *
	 * One shared blur-fade, staggered across the pieces of each section as it
	 * crosses three-quarters of the way up the viewport. The reference writes
	 * this per component; here it is one rule matched by class, so a new block
	 * inherits it by naming its parts the way every other block does and needs
	 * no motion code of its own.
	 *
	 * Heroes are excluded — the surround one has its own sequence above, and
	 * the rest sit above the fold, where an entrance would fire on load and
	 * read as a second, competing intro. The footer is outside #cobell-main and
	 * so is never matched.
	 *
	 * Keep in sync with REVEAL_SELECTOR in motion.js.
	 *
	 * `[data-no-reveal]` is the opt-out, honoured by both files. It exists for
	 * elements whose visibility is owned by something else — the manifesto's
	 * stat note, which a hover state crossfades. The entrance rules here are
	 * far more specific than any block stylesheet and load after it, so without
	 * the opt-out they pin such an element at opacity 1 and the block's own
	 * state can never take effect.
	 */
	html.cobell-motion body:not( .cobell-static ) #cobell-main .cobell-section:not( .cobell-hero ) :is(
		.cobell-eyebrow,
		.cobell-prose,
		.cobell-buttons,
		[class*="__heading"],
		[class*="__intro"],
		[class*="__summary"],
		[class*="__body"],
		[class*="__item"],
		[class*="__figure"],
		[class*="__image"],
		[class*="__stat"],
		[class*="__note"],
		[class*="__footnote"],
		[class*="__table"],
		[class*="__content"]
	):not( [data-no-reveal] ) {
		opacity: 0;
		filter: blur( 12px );
		transform: translateY( 14px );
		transition:
			opacity 1.15s cubic-bezier( 0.165, 0.84, 0.44, 1 ),
			filter 1.15s cubic-bezier( 0.165, 0.84, 0.44, 1 ),
			transform 1.15s cubic-bezier( 0.165, 0.84, 0.44, 1 );
		transition-delay: var( --cobell-reveal-delay, 0s );
	}

	/*
	 * Revealed state. Same specificity as the rule above and placed after it,
	 * so it wins on source order. motion.js sets the attribute on every element
	 * it hid, nested ones included, so nothing can be left behind.
	 *
	 * The :not([data-no-reveal]) is load-bearing and has to stay on both rules.
	 * It is what keeps the two at equal specificity: adding it to the hiding
	 * rule alone makes that rule one class heavier, which lets it beat this one
	 * and strands every animated element on the site at opacity 0.
	 */
	html.cobell-motion body:not( .cobell-static ) #cobell-main .cobell-section:not( .cobell-hero ) [data-revealed]:not( [data-no-reveal] ) {
		opacity: 1;
		filter: blur( 0 );
		transform: none;
	}
}

/*
 * Alumni rail entrance.
 *
 * Deliberately outside the `body:not( .cobell-static )` gate above, and that is
 * the whole point of it. The six scholarship detail pages are static in the
 * reference — no section entrances anywhere on them — but the alumni rail is the
 * one thing on those pages that does animate, alongside the process rail's fill.
 * So this cannot ride the shared section reveal; it is its own pair.
 *
 * The state hangs off `[data-rail-in]` on the item rather than tying two rules at
 * equal specificity the way the section-entrance pair does, so there is no
 * ordering trap here — the hidden rule stops matching outright once the attribute
 * lands.
 *
 * The travel is on the FIGURE, never the item. motion.js owns `transform` on the
 * item for the per-column offset and the parallax; an entrance transform there
 * would pin the column at its start position for good, which is the same trap the
 * homepage hero portraits carry a note about.
 */
@media ( prefers-reduced-motion: no-preference ) {
	html.cobell-motion .cobell-people--rail .cobell-people__item:not( [data-rail-in] ) .cobell-people__figure {
		opacity: 0;
		transform: translateY( 28px );
	}

	html.cobell-motion .cobell-people--rail .cobell-people__item[ data-rail-in ] .cobell-people__figure {
		transition:
			opacity 0.8s cubic-bezier( 0.165, 0.84, 0.44, 1 ),
			transform 0.8s cubic-bezier( 0.165, 0.84, 0.44, 1 );
		transition-delay: var( --cobell-rail-delay, 0s );
	}
}

/*
 * Year dial entrance — the how-to-apply calendar.
 *
 * The reference drives this as one GSAP timeline. Every offset below is its,
 * to the millisecond, but expressed as a per-element animation-delay written
 * server-side in blocks/ledger/render.php: the order is known at render time,
 * so a stagger costs no script and cannot flash before it is applied.
 *
 * Four phases. The wedges bloom outward from the pivot, innermost band first.
 * The chrome resolves under them — rim, ticks, month names, needle, pivot.
 * The names on the bands arrive late so they do not compete with the bloom.
 * Then the callouts fade up and their leader lines draw out to meet them.
 *
 * Wedges scale rather than fade because opacity is doing the work everywhere
 * else; a fading wedge in a stack of four reads as a colour shift, not an
 * arrival.
 *
 * Gated the same way as everything else here: scoped to html.cobell-motion,
 * which the wp_head snippet removes if motion.js never reports in, and wrapped
 * in no-preference. Both mean the dial simply renders finished.
 */
@media ( prefers-reduced-motion: no-preference ) {

	/*
	 * Starting state. Scoped to the dial that has not been triggered yet, so
	 * the moment motion.js sets data-dial-in the rules below stop matching and
	 * the animations — which carry their own from-states — take over.
	 */
	html.cobell-motion .cobell-ledger__dial svg:not( [data-dial-in] ) :is(
		.cobell-ledger__rim,
		.cobell-ledger__tick,
		.cobell-ledger__month,
		.cobell-ledger__needle,
		.cobell-ledger__pivot,
		.cobell-ledger__arc-label,
		.cobell-ledger__callout
	) {
		opacity: 0;
	}

	/* view-box, not the fill-box default: the transform origin has to be the
	   dial's centre in user units, not each wedge's own bounding box. */
	html.cobell-motion .cobell-ledger__dial svg:not( [data-dial-in] ) .cobell-ledger__wedge {
		transform: scale( 0 );
		transform-box: view-box;
		transform-origin: 200px 200px;
	}

	html.cobell-motion .cobell-ledger__dial svg[data-dial-in] .cobell-ledger__wedge {
		transform-box: view-box;
		transform-origin: 200px 200px;
		animation: cobell-dial-bloom 620ms cubic-bezier( 0.25, 1, 0.5, 1 ) both;
	}

	html.cobell-motion .cobell-ledger__dial svg[data-dial-in] .cobell-ledger__rim {
		animation: cobell-dial-in 500ms cubic-bezier( 0.33, 1, 0.68, 1 ) both;
	}

	html.cobell-motion .cobell-ledger__dial svg[data-dial-in] .cobell-ledger__tick {
		animation: cobell-dial-in 450ms cubic-bezier( 0.5, 1, 0.89, 1 ) both;
	}

	html.cobell-motion .cobell-ledger__dial svg[data-dial-in] .cobell-ledger__month {
		animation: cobell-dial-in 550ms cubic-bezier( 0.5, 1, 0.89, 1 ) both;
	}

	html.cobell-motion .cobell-ledger__dial svg[data-dial-in] .cobell-ledger__needle {
		/* A fade, not a draw. Drawing this by dashoffset orphans the arrowhead
		   for the length of the stroke, which reads worse than the fade. */
		animation: cobell-dial-needle 550ms cubic-bezier( 0.33, 1, 0.68, 1 ) both;
	}

	html.cobell-motion .cobell-ledger__dial svg[data-dial-in] .cobell-ledger__pivot {
		animation: cobell-dial-in 500ms cubic-bezier( 0.33, 1, 0.68, 1 ) both;
	}

	html.cobell-motion .cobell-ledger__dial svg[data-dial-in] .cobell-ledger__arc-label {
		animation: cobell-dial-in 450ms cubic-bezier( 0.5, 1, 0.89, 1 ) both;
	}

	html.cobell-motion .cobell-ledger__dial svg[data-dial-in] .cobell-ledger__callout {
		animation: cobell-dial-in 400ms cubic-bezier( 0.33, 1, 0.68, 1 ) both;
	}

	/*
	 * The leader segments draw out from the wedge edge. Each carries its own
	 * length as a custom property, measured in PHP — they are straight lines,
	 * so it is a hypotenuse rather than a getTotalLength() call in the browser.
	 *
	 * The group fades up first and the line follows about a beat behind, so the
	 * stroke arrives carrying its label rather than landing on top of it.
	 */
	html.cobell-motion .cobell-ledger__dial svg[data-dial-in] .cobell-ledger__leader {
		stroke-dasharray: var( --cobell-leader-length );
		animation: cobell-dial-draw 550ms cubic-bezier( 0.33, 1, 0.68, 1 ) both;
	}

	html.cobell-motion .cobell-ledger__dial svg[data-dial-in] :is(
		.cobell-ledger__wedge,
		.cobell-ledger__rim,
		.cobell-ledger__tick,
		.cobell-ledger__month,
		.cobell-ledger__needle,
		.cobell-ledger__pivot,
		.cobell-ledger__arc-label,
		.cobell-ledger__callout,
		.cobell-ledger__leader
	) {
		animation-delay: var( --cobell-dial-delay, 0s );
	}

	@keyframes cobell-dial-bloom {
		from {
			transform: scale( 0 );
		}

		to {
			transform: scale( 1 );
		}
	}

	@keyframes cobell-dial-in {
		from {
			opacity: 0;
		}

		to {
			opacity: 1;
		}
	}

	/* The needle sits at 0.78 at rest, so its keyframe has to land there rather
	   than at 1 or it brightens as it arrives. */
	@keyframes cobell-dial-needle {
		from {
			opacity: 0;
		}

		to {
			opacity: 0.78;
		}
	}

	@keyframes cobell-dial-draw {
		from {
			stroke-dashoffset: var( --cobell-leader-length );
		}

		to {
			stroke-dashoffset: 0;
		}
	}
}
