/* ===========================================================================
   RED CENTRENATS - HOMEPAGE SCROLL CHOREOGRAPHY
   (scroll-fx.css - loaded after site.css from views/partials/head.ejs)

   One motion language, layered ON TOP of the house [data-reveal] system
   without touching it: everything on this page arrives with lateral "speed"
   energy in its direction of travel, photography breathes inside fixed
   frames, and the brand red is drawn - never splashed - across the page as
   you move through it.

   Rules of engagement (CLAUDE.md + design/MOTION-CONTRACT.md):

   - transform / opacity / clip-path only. Nothing here triggers layout.
   - Every travel distance is derived from --parallax-max (the 40px
     vestibular cap), so no single scroll-linked movement can exceed it and
     reduced motion (which zeroes the token) collapses the distances too.
   - Every scrubbed effect lives inside @supports (animation-timeline: view())
     and is pure CSS: no JS, no .js-ready dependency, and engines without
     scroll-driven animations simply render the finished static state.
     Nothing outside the @supports block ever hides or offsets an element.
   - The global reduced-motion animation-duration kill in tokens.css does NOT
     stop timeline-driven animations (their duration is ignored by the
     timeline), so the reduced-motion block at the end of this file removes
     every animation declared here explicitly. No information is lost: each
     base state is the finished state.
   - Scoped to body[data-path="/"]: this is a homepage pass. Widening it to
     other pages means re-verifying every selector against that page's
     markup first.
   - Deliberately untouched: the hero (already fully choreographed by
     motion.js + site.css SS8.1), the Arrernte acknowledgement, the countdown
     (aria contract), both marquees (they move on their own clock), and
     anything carrying data-reveal / data-reveal-child transforms of its own -
     animating those same elements would fight the house transition states.
   =========================================================================== */

/* ---------------------------------------------------------------------------
   1. EYEBROW ACCENT RULE - static base state.
   A short brand-red hairline under each section eyebrow. This is the ONE
   element this file adds to the page, so its no-support / no-scrub state is
   declared here, outside @supports, fully visible: a finished drawn line.
   The scrub below only adds the drawing-in.
   --------------------------------------------------------------------------- */
body[data-path="/"] .rcn-eyebrow::after {
  content: '';
  display: block;
  height: var(--fx-rule-w);
  margin-block-start: var(--space-1);
  background: linear-gradient(90deg, var(--color-red-500) 0%, var(--color-red-500) 55%, transparent 100%);
  transform-origin: left center;
  transform: scaleX(1);
}

/* ---------------------------------------------------------------------------
   2. SECTION-TRANSITION GLOW - static base state.
   A faint red radial wash anchored to the media side of each homepage
   feature section. Base opacity is 0 because it is pure decoration - engines
   without view() simply never see it, which loses no information.
   isolation:isolate makes the section a stacking context so the z-index:-1
   pseudo sits above the page background but under ALL section content
   (including unpositioned card/text boxes) instead of washing over it.
   --------------------------------------------------------------------------- */
body[data-path="/"] .rcn-feature-section {
  isolation: isolate;
}
body[data-path="/"] .rcn-feature-section::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background: radial-gradient(55% 70% at 18% 40%, var(--fx-glow-red-soft), transparent 72%);
  opacity: 0;
}
/* The reversed feature puts its media on the right, so its glow anchors
   right. :has() ships in every engine that has animation-timeline, and in
   older engines this rule is simply skipped along with the glow itself. */
body[data-path="/"] .rcn-feature-section:has(.rcn-feature--reversed)::before {
  background: radial-gradient(55% 70% at 82% 40%, var(--fx-glow-red-soft), transparent 72%);
}

/* ---------------------------------------------------------------------------
   3. THE SCRUBBED CHOREOGRAPHY - view() timelines, pure CSS, zero JS.
   --------------------------------------------------------------------------- */
@supports (animation-timeline: view()) {

  /* --- 3.0 Named frame timelines - REQUIRED, not decorative -----------------
     Per spec, `overflow: hidden` makes an element a scroll container, and an
     anonymous view() resolves against the NEAREST scroll container - so for
     anything inside the clipped photo frames (.rcn-photo, .rcn-precinct,
     .rcn-split__path all clip) an anonymous view() binds to a box that never
     scrolls and the timeline sits inert forever. Verified live: the drift
     and converge effects froze at one keyframe until this was added.
     Instead, each clipped frame declares a NAMED view-timeline on itself -
     the subject's own position still resolves against the root viewport
     because its ancestors don't clip - and the animated descendants consume
     the name, which is in scope for all descendants including pseudos. */
  body[data-path="/"] .rcn-precinct,
  body[data-path="/"] .rcn-feature__media-primary,
  body[data-path="/"] .rcn-split__path {
    view-timeline: --fx-frame;
  }

  /* --- 3.1 Heading speed-wipe -------------------------------------------
     Every big section heading arrives like something passing the stands:
     slides in from the left at the full 40px cap, transiently skewed, while
     a clip-path wipe uncovers the line left-to-right. Lands perfectly
     upright and unclipped. Scrub range matches the house reveal
     (entry 0 to cover 30) so headings and their sections arrive on one beat.
     The newsletter h2 is deliberately excluded - it sits inside a
     [data-reveal-child] that already moves. */
  body[data-path="/"] main h2.rcn-h4,
  body[data-path="/"] .rcn-precincts__heading,
  body[data-path="/"] .rcn-feature__title,
  body[data-path="/"] .rcn-partner-band__heading {
    animation: rcn-fx-heading-wipe var(--duration-slow) var(--ease-drift) both;
    animation-timeline: view();
    animation-range: entry 0% cover 30%;
  }

  /* --- 3.2 Eyebrow rule draw --------------------------------------------
     The hairline from section 1 draws itself left-to-right slightly behind
     its heading's wipe - eyebrow answers headline. */
  body[data-path="/"] .rcn-eyebrow::after {
    animation: rcn-fx-rule-draw var(--duration-slow) var(--ease-drift) both;
    animation-timeline: view();
    animation-range: entry 10% cover 35%;
  }

  /* --- 3.3 Photography breathes inside its frame ------------------------
     Precinct portraits, both feature primaries and the split-CTA photos
     drift vertically inside their overflow-clipped frames for as long as
     they are on screen - a whisper of depth, not a zoom. Constant 1.08
     scale supplies the overdraw headroom; the drift is 0.3 x the parallax
     cap each way (12px), well under the 40px rule, and collapses to zero
     under reduced motion with the token. Marquee card images are excluded:
     they move horizontally on their own clock and carry a :hover scale. */
     Flat ARTWORK is excluded (:not(--artwork), set from the media manifest's
     category in views/blocks/imageFeature.ejs). The 1.08 overdraw is invisible
     on a photograph but trims 4% off every edge of a poster, which is enough to
     shear a lockup off the top - it took the LMCT+ "presents" mark off the
     RCN12 key art (client card 2026-07-30). */
  body[data-path="/"] .rcn-precinct__media img,
  body[data-path="/"] .rcn-feature__media-primary:not(.rcn-feature__media-primary--artwork) img,
  body[data-path="/"] .rcn-split__path img {
    animation: rcn-fx-photo-drift var(--duration-ambient) linear both;
    animation-timeline: --fx-frame;
    animation-range: cover 0% cover 100%;
  }

  /* --- 3.4 Prize stat stack - payout board flips up ----------------------
     The five prize rows slide in from the left one after another, each row's
     scrub window starting a step later than the one above it, so the board
     fills top-to-bottom as it enters. The amounts are server-rendered text
     the whole time - nothing here touches the figures. */
  body[data-path="/"] .rcn-feature__stats li {
    animation: rcn-fx-stat-in var(--duration-base) var(--ease-drift) both;
    animation-timeline: view();
    animation-range: entry 0% cover 30%;
  }
  body[data-path="/"] .rcn-feature__stats li:nth-child(2) { animation-range: entry 8% cover 38%; }
  body[data-path="/"] .rcn-feature__stats li:nth-child(3) { animation-range: entry 16% cover 46%; }
  body[data-path="/"] .rcn-feature__stats li:nth-child(4) { animation-range: entry 24% cover 54%; }
  body[data-path="/"] .rcn-feature__stats li:nth-child(5) { animation-range: entry 32% cover 62%; }

  /* --- 3.5 Split CTA converge -------------------------------------------
     The finale: ENTER IT slides in from the left, WATCH IT from the right,
     the two halves meeting in the middle as the section arrives. The house
     [data-reveal] on the section itself supplies a small upward move at the
     same time, so each panel travels a shallow diagonal. Amplitude is
     0.6 x the cap (24px) per panel. */
  body[data-path="/"] .rcn-split__path--entrant .rcn-split__content {
    animation: rcn-fx-converge-left var(--duration-slow) var(--ease-drift) both;
    animation-timeline: --fx-frame;
    /* Range is relative to the tall PATH frame, not the content box: the
       content sits at the frame's bottom, so the window opens as the content
       clears the viewport edge and closes when it reaches mid-viewport. */
    animation-range: cover 20% cover 55%;
  }
  body[data-path="/"] .rcn-split__path--spectator .rcn-split__content {
    animation: rcn-fx-converge-right var(--duration-slow) var(--ease-drift) both;
    animation-timeline: --fx-frame;
    animation-range: cover 20% cover 55%;
  }
  /* The split panels' eyebrows sit inside the clipped path, so their rule-draw
     must ride the named frame timeline too (see 3.0). */
  body[data-path="/"] .rcn-split__content .rcn-eyebrow::after {
    animation-timeline: --fx-frame;
    animation-range: cover 30% cover 60%;
  }

  /* --- 3.6 Section-transition glow tracks scroll -------------------------
     The wash from section 2 swells to full strength when its feature
     section is centred in the viewport and dies away on the way out -
     opacity only, driven by the section's own cover range. */
  body[data-path="/"] .rcn-feature-section::before {
    animation: rcn-fx-glow var(--duration-ambient) var(--ease-drift) both;
    animation-timeline: view();
    animation-range: cover 0% cover 100%;
  }
}

/* ---------------------------------------------------------------------------
   4. KEYFRAMES
   Custom properties resolve per element at apply time, so the travel
   distances below track --parallax-max, including its reduced-motion zero.
   --------------------------------------------------------------------------- */
@keyframes rcn-fx-heading-wipe {
  from {
    opacity: 0;
    clip-path: inset(0 70% 0 0);
    transform: translateX(calc(-1 * var(--parallax-max))) skewX(var(--fx-skew-entry));
  }
  45% { opacity: 1; }
  to {
    opacity: 1;
    clip-path: inset(0 0 0 0);
    transform: translateX(0) skewX(0deg);
  }
}

@keyframes rcn-fx-rule-draw {
  from { transform: scaleX(0); }
  to   { transform: scaleX(1); }
}

@keyframes rcn-fx-photo-drift {
  from { transform: scale(1.08) translateY(calc(var(--parallax-max) * 0.3)); }
  to   { transform: scale(1.08) translateY(calc(var(--parallax-max) * -0.3)); }
}

@keyframes rcn-fx-stat-in {
  from {
    opacity: 0;
    transform: translateX(calc(var(--parallax-max) * -0.6));
  }
  to {
    opacity: 1;
    transform: translateX(0);
  }
}

@keyframes rcn-fx-converge-left {
  from {
    opacity: 0;
    transform: translateX(calc(var(--parallax-max) * -0.6));
  }
  to {
    opacity: 1;
    transform: translateX(0);
  }
}

@keyframes rcn-fx-converge-right {
  from {
    opacity: 0;
    transform: translateX(calc(var(--parallax-max) * 0.6));
  }
  to {
    opacity: 1;
    transform: translateX(0);
  }
}

@keyframes rcn-fx-glow {
  0%   { opacity: 0; }
  50%  { opacity: 1; }
  100% { opacity: 0; }
}

/* ---------------------------------------------------------------------------
   5. REDUCED MOTION - explicit, not inherited.
   tokens.css already zeroes --parallax-max and the global duration kill, but
   a TIMELINE-driven animation ignores its duration, so each one added above
   must be removed by name here. With the animations gone every element falls
   back to its base state, which is the finished, fully visible one - the
   eyebrow rule shows complete, every heading and stat is upright and legible,
   the photos sit still, and the decorative glow simply stays off.
   Movement removed, zero information lost.
   --------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  body[data-path="/"] main h2.rcn-h4,
  body[data-path="/"] .rcn-precincts__heading,
  body[data-path="/"] .rcn-feature__title,
  body[data-path="/"] .rcn-partner-band__heading,
  body[data-path="/"] .rcn-eyebrow::after,
  body[data-path="/"] .rcn-precinct__media img,
  body[data-path="/"] .rcn-feature__media-primary img,
  body[data-path="/"] .rcn-split__path img,
  body[data-path="/"] .rcn-feature__stats li,
  body[data-path="/"] .rcn-split__path--entrant .rcn-split__content,
  body[data-path="/"] .rcn-split__path--spectator .rcn-split__content,
  body[data-path="/"] .rcn-feature-section::before {
    animation: none !important;
  }
}
