/* ===========================================================================
 * rush-motion.css — the shared motion pattern library (spec Part 3 §3.1).
 *
 * RULE ONE: every at-rest-HIDDEN state is scoped to html.rush-motion. That
 * class is written by the head hydrator only when the visitor has NOT asked
 * for reduced motion, so without it every pattern below resolves to its final
 * state and nothing moves. Without JS, without the class, the page is whole.
 *
 * Patterns (numbered as in the spec):
 *   M1  push-in         .m-pushin
 *   M2  gold rule draws .m-rule / .m-rule-v / .m-rule-top
 *   M3  slate rise      .m-rise (+ --m-i for stagger)
 *   M3b type rise       .rk-line > .rk-line-in
 *   M4  rack focus      .m-rack-out / .m-rack-in (driven by RushMotion.rackFocus)
 *   M5  card lift       .m-lift (pointer: fine only)
 *   M7  foil catch      .m-foil (+ --foil-x)
 *   M11 status pulse    .m-pulse
 * The ScrollTrigger-driven patterns (M6 curtain/dolly, M8 baton) are pure
 * JS + inline transforms and have no rules here.
 * ======================================================================== */

:root {
	--ease-out-cine: cubic-bezier(0.16, 1, 0.3, 1);
	--ease-inout-cine: cubic-bezier(0.65, 0, 0.35, 1);
	--ease-in-cine: cubic-bezier(0.4, 0, 1, 1);
	--ease-mark: cubic-bezier(0.2, 0.7, 0.2, 1);
	--ease-pop: cubic-bezier(0.34, 1.56, 0.64, 1);
	--rk-slash: 30deg;
	--slash-l: calc(50% + 28.9vw);
	--slash-r: calc(50% - 28.9vw);
	--rush-gold-hair: var(--t-gold-hover);
	--rush-carbon-soft: var(--t-bg-3);
	--m-micro: 150ms;
	--m-state: 300ms;
	--m-element: 500ms;
	--m-reveal: 700ms;
	--m-scene: 900ms;
	--m-pushin: 1600ms;
}

/* ── Lenis (the plugin's 5 rules, inlined so its stylesheet can be dropped) ── */
html.lenis,
html.lenis body { height: auto; }
.lenis.lenis-smooth { scroll-behavior: auto !important; }
.lenis.lenis-smooth [data-lenis-prevent] { overscroll-behavior: contain; }
.lenis.lenis-stopped { overflow: hidden; }
.lenis.lenis-smooth iframe { pointer-events: none; }

/* ── M3 slate rise ─────────────────────────────────────────────────────── */
html.rush-motion .m-rise {
	opacity: 0;
	transform: translate3d(0, 40px, 0);
	/* r2: children CUT in — a 30° slit opens from the left as they rise. */
	clip-path: polygon(0 0, 0 0, -57.7% 100%, -57.7% 100%);
	transition:
		opacity var(--m-reveal) var(--ease-out-cine),
		transform var(--m-reveal) var(--ease-out-cine),
		clip-path var(--m-state) var(--ease-inout-cine);
	transition-delay: calc(var(--m-i, 0) * 70ms);
	/* r2 fix (P1 perf): no will-change at rest — 70+ below-fold reveals were
	 * permanently promoted (~60 MB of layers at 390@2x). The transition
	 * promotes for its own duration. */
}
html.rush-motion .m-rise.is-in {
	opacity: 1;
	transform: none;
	clip-path: polygon(0 0, 157.7% 0, 100% 100%, -57.7% 100%);
}
/* Once landed the clip is released entirely (hover lifts / tilts may spill). */
html.rush-motion .m-rise.is-in.is-settled { clip-path: none; }
/* Grid rhythm: 70ms × (index % 4) — set --m-i on the item, the modulo is the
 * author's job (PHP prints it), so a 12-card grid still reads as rows. */
html.rush-motion .m-rise[data-m-grid] { transition-delay: calc(var(--m-i, 0) * 70ms); }

/* ── M3b type rise ─────────────────────────────────────────────────────── */
.rk-line {
	display: block;
	overflow: hidden;
	/* The wrapper reserves the line box whether or not the inner has risen,
	 * so the LCP-adjacent headings never shift. */
	padding-bottom: 0.06em;
	margin-bottom: -0.06em;
}
.rk-line-in { display: block; }
.rk-line { min-height: 1lh; }
html.rush-motion .rk-line-in {
	transform: translate3d(0, 110%, 0);
	transition: transform var(--m-reveal) var(--ease-out-cine);
	transition-delay: calc(var(--m-base, 0ms) + var(--m-i, 0) * var(--m-step, 90ms));
}
html.rush-motion .is-in > .rk-line > .rk-line-in,
html.rush-motion .is-in .rk-line-in,
html.rush-motion .rk-line.is-in > .rk-line-in { transform: none; }

/* ── M2 gold rule draws ────────────────────────────────────────────────── */
.m-rule { position: relative; }
.m-rule::after {
	content: "";
	position: absolute;
	left: 0;
	right: 0;
	bottom: 0;
	height: 2px;
	background: var(--t-gold);
	transform-origin: left center;
	transform: scaleX(0);
	transition: transform 240ms var(--ease-mark);
	pointer-events: none;
}
.m-rule.m-rule-thin::after { height: 1px; background: var(--t-gold-hover); }
.m-rule.is-drawn::after,
.m-rule.is-in::after { transform: scaleX(1); transition-duration: 480ms; }
.m-rule:not(.is-drawn):not(.is-in)::after { transform-origin: right center; }
/* Hover variant: 150 ms, draws left→right, un-draws right→left. */
@media (hover: hover) and (pointer: fine) {
	.m-rule-hover::after { transition-duration: var(--m-micro); }
	.m-rule-hover:hover::after,
	.m-rule-hover:focus-visible::after { transform: scaleX(1); transform-origin: left center; }
}
/* Vertical variant (self-sufficient: usable without .m-rule). */
.m-rule-v { position: relative; }
.m-rule-v::after {
	content: "";
	position: absolute;
	background: var(--t-gold);
	pointer-events: none;
	left: 0;
	right: auto;
	top: 0;
	bottom: 0;
	width: 3px;
	height: auto;
	transform-origin: top center;
	transform: scaleY(0);
	transition: transform 300ms var(--ease-mark);
}
.m-rule-v.is-drawn::after,
.m-rule-v.is-in::after { transform: scaleY(1); }
/* Section-top 1px hairline, 800 ms. */
.m-rule-top::before {
	content: "";
	position: absolute;
	left: 0;
	right: 0;
	top: 0;
	height: 1px;
	background: var(--t-gold);
	transform-origin: left center;
	transform: scaleX(1);
	pointer-events: none;
	z-index: 2;
}
html.rush-motion .m-rule-top::before {
	transform: scaleX(0);
	transition: transform 800ms var(--ease-mark);
}
html.rush-motion .m-rule-top.is-in::before { transform: scaleX(1); }
/* r2 "cut rule": the hairline is the surface's strong line and a 48×8 foil
 * tab, skewed to the brand angle, sits at its left end. */
.m-rule-top::before { background: var(--t-line-strong); }
.m-rule-top::after {
	content: "";
	position: absolute;
	left: max(16px, calc((100% - var(--rush-max-width, 1220px)) / 2));
	top: 0;
	width: 48px; height: 8px;
	background: var(--t-gold) var(--rush-foil, none) center / cover;
	transform: skewX(-30deg);
	transform-origin: left top;
	z-index: 3;
	pointer-events: none;
}
[data-surface="gold"].m-rule-top::after { background: var(--t-on-gold); }
html.rush-motion .m-rule-top::after { transform: skewX(-30deg) scaleX(0); transition: transform 480ms var(--ease-mark) 500ms; }
html.rush-motion .m-rule-top.is-in::after { transform: skewX(-30deg) scaleX(1); }
/* No motion / no class → every REVEAL rule is drawn; hover marks stay
 * hover-only (they are state, not motion). */
html:not(.rush-motion) .m-rule:not(.m-rule-hover)::after,
html:not(.rush-motion) .m-rule-v:not(.m-rule-hover)::after { transform: none; }

/* ── M1 push-in ────────────────────────────────────────────────────────── */
html.rush-motion .m-pushin {
	transform: scale(1.06);
	transition: transform var(--m-pushin) var(--ease-out-cine);
}
html.rush-motion .m-pushin.is-in { transform: scale(1); }

/* ── M4 rack focus ─────────────────────────────────────────────────────── */
html.rush-motion .m-rack-out {
	filter: blur(8px);
	opacity: 0;
	transition:
		filter var(--m-state) var(--ease-in-cine),
		opacity var(--m-state) var(--ease-in-cine);
}
html.rush-motion .m-rack-in {
	filter: blur(0);
	opacity: 1;
	transition:
		filter var(--m-element) var(--ease-out-cine),
		opacity var(--m-element) var(--ease-out-cine);
}
html.rush-motion .m-text-out { opacity: 0; transition: opacity 200ms var(--ease-in-cine); }
html.rush-motion .m-text-in { opacity: 1; transition: opacity 200ms var(--ease-out-cine); }
/* Type swap (350 ms rise) for a single text node that changed. */
html.rush-motion .m-swap {
	animation: rushTypeSwap 350ms var(--ease-out-cine) both;
}
@keyframes rushTypeSwap {
	from { opacity: 0; transform: translate3d(0, 0.35em, 0); }
	to { opacity: 1; transform: none; }
}

/* ── M5 card lift ──────────────────────────────────────────────────────── */
@media (hover: hover) and (pointer: fine) {
	.m-lift { transition: transform 200ms var(--ease-out-cine); }
	.m-lift:hover,
	.m-lift:focus-within { transform: translate3d(0, -4px, 0); }
	.m-lift .m-lift-img { transition: transform var(--m-reveal) var(--ease-out-cine); }
	.m-lift:hover .m-lift-img,
	.m-lift:focus-within .m-lift-img { transform: scale(1.04); }
}

/* ── M7 foil catch ─────────────────────────────────────────────────────── */
.m-foil { position: relative; --foil-x: -40%; }
.m-foil-text {
	background-color: var(--t-gold);
	background-image:
		linear-gradient(100deg, transparent calc(var(--foil-x) - 15%), rgba(255, 255, 255, 0.55) /* fixed: foil highlight */ var(--foil-x), transparent calc(var(--foil-x) + 15%)),
		var(--rush-foil, url("../img/rush-gold-texture.webp?v=1.23.2"));
	background-size: 200% 100%, cover;
	background-position: center;
	-webkit-background-clip: text;
	background-clip: text;
	color: transparent;
	-webkit-text-fill-color: transparent;
}
html.rush-motion .m-foil-text.is-in { animation: rushFoilCatch 1200ms var(--ease-inout-cine) 1 both; }
/* r2: on a GOLD surface (data-surface="gold") foil-on-foil marks nothing —
 * the word becomes ink, the one text colour that is legal on gold. */
[data-surface="gold"] .m-foil-text {
	background: none;
	color: var(--t-on-gold);
	-webkit-text-fill-color: var(--t-on-gold);
	animation: none;
}
@keyframes rushFoilCatch {
	from { --foil-x: -40%; }
	to { --foil-x: 140%; }
}
/* The gradient stop can only animate through a registered property; where
 * @property is unsupported the sweep degrades to the static foil. */
@property --foil-x {
	syntax: "<percentage>";
	inherits: true;
	initial-value: -40%;
}

/* ── M11 status pulse ──────────────────────────────────────────────────── */
html.rush-motion .m-pulse { animation: rushPulse 900ms var(--ease-pop) 1; }
html.rush-motion .m-pulse-3 { animation: rushPulse 900ms var(--ease-pop) 3; }
@keyframes rushPulse {
	0% { transform: scale(1); }
	50% { transform: scale(1.5); }
	100% { transform: scale(1); }
}

/* ── Safety valve: a reveal must enhance an already-visible default ──────
 * html.rush-motion is written by the head hydrator (inline, always runs);
 * rush-motion.js adds html.rush-motion-live when it boots. If that never
 * happens — gsap.min.js 404, a CSP, an ad-blocker, a stale service-worker
 * chunk — every hidden-at-rest state below resolves to its final state 3 s
 * after this sheet applies, via a 1 ms animation with a 3 s delay. While the
 * live class is present the rules never match, so below-fold reveals keep
 * their choreography. */
html.rush-motion:not(.rush-motion-live) .m-rise:not(.is-in),
html.rush-motion:not(.rush-motion-live) .m-pushin:not(.is-in),
html.rush-motion:not(.rush-motion-live) .m-clipin:not(.is-in),
html.rush-motion:not(.rush-motion-live) .m-stamp:not(.is-in) { animation: rushMotionValve 1ms linear 3s forwards; }
html.rush-motion:not(.rush-motion-live) [data-rush-intro],
html.rush-motion:not(.rush-motion-live) .rush-shot-slab,
html.rush-motion:not(.rush-motion-live) .rush-shot-bar { animation: rushMotionValve 1ms linear 3s forwards; }
html.rush-motion:not(.rush-motion-live) .rk-line-in { animation: rushMotionValveT 1ms linear 3s forwards; }
html.rush-motion:not(.rush-motion-live) .m-rule-top:not(.is-in)::before { animation: rushMotionValveX 1ms linear 3s forwards; }
@keyframes rushMotionValve { to { opacity: 1; transform: none; clip-path: none; } }
@keyframes rushMotionValveT { to { transform: none; } }
@keyframes rushMotionValveX { to { transform: scaleX(1); } }

/* ── Reduced motion / no-JS backstop ───────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
	html.rush-motion .m-rise,
	html.rush-motion .rk-line-in,
	html.rush-motion .m-clipin,
	html.rush-motion .m-clipin > *,
	html.rush-motion .m-stamp,
	html.rush-motion .m-pushin { opacity: 1; transform: none; clip-path: none; transition: none; }
	html.rush-motion .m-rule::after,
	html.rush-motion .m-rule-v::after,
	html.rush-motion .m-rule-top::before { transform: none; transition: none; }
	html.rush-motion .m-foil-text.is-in,
	html.rush-motion .m-pulse,
	html.rush-motion .m-pulse-3,
	html.rush-motion .m-swap { animation: none; }
	/* M5 lift is state, but a transform TRANSITION is still motion: hold the
	 * lifted/scaled state without tweening into it. */
	.m-lift,
	.m-lift .m-lift-img { transition: none; }
	.m-lift:hover,
	.m-lift:focus-within { transform: none; }
	.m-lift:hover .m-lift-img,
	.m-lift:focus-within .m-lift-img { transform: none; }
}

/* ══ r2 additions (D1: M12 – M29) ═══════════════════════════════════════════ */

/* ── M12 blade layers: a full-cover 30° band wipe driven by RushMotion.blade()
 * (WAAPI clip-path). At rest a blade layer is parked as a zero band. ── */
.m-blade {
	position: absolute;
	inset: 0;
	pointer-events: none;
	clip-path: polygon(0 0, 0 0, 0 100%, 0 100%);
	z-index: 40;
}
.m-blade--gold { background: var(--t-gold) var(--rush-foil, none) center / cover; }
.m-blade--ink { background: var(--t-fg); opacity: 0.92; }
.m-blade--bg { background: var(--t-bg); }
/* Scrubbed blade: --bp 0→1 draws a band from the left edge across the box
 * (used as a ScrollTrigger scrub on the Rush Hour band and the reel slab). */
.m-blade-scrub {
	clip-path: polygon(
		0 0,
		calc(var(--bp, 0) * 160%) 0,
		calc(var(--bp, 0) * 160% - 57.7%) 100%,
		0 100%
	);
}
@property --bp { syntax: "<number>"; inherits: false; initial-value: 0; }

/* ── M16 clip-in: the container enters as a 30° parallelogram and squares
 * at centre; the inner media scales 1.15 → 1. Desktop scrubs it (JS sets
 * --ci 0→1); phones get the one-shot .is-in (900 ms). ── */
.m-clipin { position: relative; overflow: hidden; }
html.rush-motion .m-clipin {
	clip-path: polygon(calc(12% * (1 - var(--ci, 0))) 0, 100% 0, calc(100% - 12% * (1 - var(--ci, 0))) 100%, 0 100%);
}
html.rush-motion .m-clipin > img,
html.rush-motion .m-clipin > video,
html.rush-motion .m-clipin > .m-clipin-inner {
	transform: scale(calc(1.15 - 0.15 * var(--ci, 0)));
	transform-origin: 50% 50%;
}
html.rush-motion .m-clipin.is-in:not([data-scrub]) {
	transition: clip-path 900ms var(--ease-out-cine);
	--ci: 1;
}
html.rush-motion .m-clipin.is-in:not([data-scrub]) > img,
html.rush-motion .m-clipin.is-in:not([data-scrub]) > video,
html.rush-motion .m-clipin.is-in:not([data-scrub]) > .m-clipin-inner { transition: transform 900ms var(--ease-out-cine); }
@property --ci { syntax: "<number>"; inherits: false; initial-value: 0; }

/* ── M18 gold sweep: a 30° band crosses a pill on hover / focus-visible. Ink
 * pills sweep gold, gold pills sweep paper at .35. ── */
.m-sweep { position: relative; overflow: hidden; isolation: isolate; }
.m-sweep::before {
	content: "";
	position: absolute;
	top: -20%; bottom: -20%;
	left: 0;
	width: 40%;
	background: var(--t-gold);
	opacity: 0.85;
	transform: translateX(-160%) skewX(-30deg);
	pointer-events: none;
	z-index: 0;
}
.m-sweep > * { position: relative; z-index: 1; }
.rush-cta-gold.m-sweep::before,
[data-surface="gold"] .m-sweep:not(.rush-btn-ink)::before { background: #fbfaf4; opacity: 0.35; } /* fixed: paper sweep on a gold fill */
@media (hover: hover) and (pointer: fine) {
	html.rush-motion .m-sweep:hover::before,
	html.rush-motion .m-sweep:focus-visible::before { animation: rushSweep 450ms var(--ease-inout-cine) 1 both; }
}
@keyframes rushSweep {
	from { transform: translateX(-160%) skewX(-30deg); }
	to { transform: translateX(320%) skewX(-30deg); }
}

/* ── M19 cut underline: 2px currentColor, draws left→right, right end cut at
 * 30°; un-draws from the left. ── */
.m-ul { position: relative; text-decoration: none; }
.m-ul::after {
	content: "";
	position: absolute;
	left: 0; right: 0; bottom: -2px;
	height: 2px;
	background: currentColor;
	clip-path: polygon(0 0, 100% 0, calc(100% - 3px) 100%, 0 100%);
	transform: scaleX(0);
	transform-origin: left center;
	transition: transform 300ms var(--ease-mark);
	pointer-events: none;
}
.m-ul.is-drawn::after,
.m-ul[aria-current]::after { transform: scaleX(1); }
@media (hover: hover) and (pointer: fine) {
	.m-ul:hover::after,
	.m-ul:focus-visible::after,
	a:hover > .m-ul::after,
	a:focus-visible > .m-ul::after { transform: scaleX(1); transform-origin: left center; }
}

/* ── M27 stamp: scale 1.35 rotate(-3deg) opacity 0 → identity, 420 ms. ── */
html.rush-motion .m-stamp {
	opacity: 0;
	transform: scale(1.35) rotate(-3deg);
	transform-origin: 40% 60%;
	transition: opacity 420ms var(--ease-out-cine), transform 420ms var(--ease-out-cine);
	transition-delay: calc(var(--m-i, 0) * 60ms);
}
html.rush-motion .m-stamp.is-in { opacity: 1; transform: none; }

/* ── M28 foil shimmer: a highlight band across a foil surface, 8 s loop in
 * dark, hover-only in light; only after load; paused when hidden. ── */
.m-shimmer { position: relative; overflow: hidden; }
.m-shimmer::after {
	content: "";
	position: absolute;
	top: -30%; bottom: -30%;
	left: 0;
	width: 34%;
	background: linear-gradient(100deg, transparent 0%, rgba(255, 255, 255, 0.55) 50%, transparent 100%); /* fixed: foil highlight */
	mix-blend-mode: soft-light;
	transform: translateX(-160%) skewX(-30deg);
	pointer-events: none;
	opacity: 0;
	z-index: 2;
}
html.rush-loaded.rush-motion .m-shimmer::after { opacity: 1; }
html.rush-loaded.rush-motion[data-theme="dark"] .m-shimmer::after { animation: rushShimmer 8s var(--ease-inout-cine) infinite; }
@media (hover: hover) and (pointer: fine) {
	html.rush-loaded.rush-motion[data-theme="light"] .m-shimmer:hover::after { animation: rushShimmer 1.4s var(--ease-inout-cine) 1; }
}
html.rush-hidden .m-shimmer::after { animation-play-state: paused; }
@keyframes rushShimmer {
	0% { transform: translateX(-160%) skewX(-30deg); }
	18% { transform: translateX(420%) skewX(-30deg); }
	100% { transform: translateX(420%) skewX(-30deg); }
}

/* ── M14 counter slots ── */
.rk-num { display: inline-flex; align-items: flex-start; white-space: nowrap; font-variant-numeric: tabular-nums; min-width: calc(var(--digits, 1) * 1ch + var(--suffix-w, 0ch)); }
.rk-slot { display: inline-block; height: 1lh; overflow: hidden; vertical-align: top; }
.rk-col { display: flex; flex-direction: column; }
.rk-col > span { display: block; height: 1lh; line-height: inherit; }
.rk-suffix { display: inline-block; }

/* ── Reduced-motion backstops for the r2 patterns ── */
@media (prefers-reduced-motion: reduce) {
	.m-blade { clip-path: none !important; display: none; }
	.m-sweep::before { animation: none !important; }
	.m-ul::after { transition: none; }
	.m-shimmer::after { animation: none !important; opacity: 0 !important; }
	html.rush-motion .m-clipin { clip-path: none; }
	html.rush-motion .m-clipin > img,
	html.rush-motion .m-clipin > video,
	html.rush-motion .m-clipin > .m-clipin-inner { transform: none; }
}
