- 01
Scroll = playhead
One ScrollTrigger spans start:0 → end:"max" and maps document scroll progress onto a 96-second, 24fps SMPTE timecode (00:MM:SS:FF) in the fixed HUD, plus a scaleX playhead bar. The whole page is a timeline being scrubbed.
- 02
Pinned poster scenes
Each <section class="scene"> gets pin:true with a data-len scroll budget (155–330% of viewport). A scrubbed GSAP timeline per scene runs its choreography; scrub:0.55 gives the playhead a hint of tape inertia.
- 03
Hard palette flips between scenes
Scenes are transparent in cinema mode; a fixed .backdrop div sits at z-index:-1. For every scene boundary a fromTo tween scrubs backgroundColor across a narrow window (top 72% → top 18%), so cuts feel hard but smear slightly with velocity — like a film splice.
- 04
Build-time split text
A hand-rolled splitter runs in Astro frontmatter, not in the browser: headlines are server-rendered as word/char <span>s, so no-JS and reduced-motion users get intact text and GSAP gets char handles with zero layout shift. Parents carry aria-label; spans are aria-hidden.
- 05
Outline ↔ fill swaps
Every display headline is two stacked copies: an outlined base (-webkit-text-stroke, transparent fill) and an absolutely-positioned filled layer clipped with clip-path: inset(0 100% 0 0). Mid-scene the clip wipes to 0 — a typographic reveal with no extra paint cost.
- 06
Velocity-skewed marquees
Marquee tracks loop with xPercent:-50 repeat:-1. A global ScrollTrigger reads self.getVelocity(), clamps it to ±14deg and quickSets skewX on the tracks (and a softer skewY on the credits roll), decaying back to 0 with power3.out. Scroll hard and the type smears.
- 07
Counter-rotating type rings
Two SVG <textPath> circles ("OBSESSIVELY CURIOUS • NOMADIC OWLS •") rotate in opposite directions forever; textLength pins the copy to the exact circumference. Scroll velocity bumps their timeScale so they spin up as you scrub.
- 08
Flashcard strobe
The beliefs scene hard-cuts between five cards with timeline set() calls — and flips the entire backdrop black⁄acid per card, a full-viewport strobe that stays scrub-reversible because every state is a recorded tween.
- 09
Difference-blend HUD
The timecode HUD and playhead bar are white with mix-blend-mode:difference, so they stay legible over all five palette states without a single media query.
- 10
Reduced motion = poster mode
gsap.matchMedia() watches prefers-reduced-motion. The default CSS is already a complete stack of static posters (scenes own their background, swap layers pre-filled, cards stacked); motion styles only exist under an .gsap-on class that JS adds when animation is allowed.