'use client' import { useLayoutEffect, useRef } from 'react' import { gsap } from 'gsap' import { ScrollTrigger } from 'gsap/ScrollTrigger' import { animateWords } from '@/lib/animateSplit' if (typeof window !== 'undefined') { gsap.registerPlugin(ScrollTrigger) } interface StoryStep { title: string copy: string highlight?: boolean } interface PinnedStoryProps { steps: StoryStep[] className?: string } export default function PinnedStory({ steps, className = '' }: PinnedStoryProps) { const containerRef = useRef(null) const contentRef = useRef(null) useLayoutEffect(() => { const container = containerRef.current const content = contentRef.current if (!container || !content) return // Check for reduced motion const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches const ctx = gsap.context(() => { if (!prefersReducedMotion) { // Pin the section ScrollTrigger.create({ trigger: container, pin: content, pinSpacing: true, start: 'top top', end: '+=150%', invalidateOnRefresh: true }) } // Animate each step const stepElements = gsap.utils.toArray('.story-step') stepElements.forEach((step, i) => { const title = step.querySelector('.story-title') as HTMLElement const copy = step.querySelector('.story-copy') as HTMLElement if (prefersReducedMotion) { // Simple fade for reduced motion gsap.from(step, { opacity: 0, y: 20, duration: 0.6, scrollTrigger: { trigger: step, start: 'top 80%', toggleActions: 'play none none reverse' } }) } else { // Full animation const tl = gsap.timeline({ scrollTrigger: { trigger: step, start: 'top 70%', toggleActions: 'play none none reverse' } }) tl.from(step, { opacity: 0, scale: 0.95, duration: 0.8, ease: 'power2.out' }) if (title) { tl.add(() => animateWords(title, { stagger: 0.05 }), '-=0.4') } if (copy) { tl.from(copy, { opacity: 0, y: 30, duration: 0.6, ease: 'power2.out' }, '-=0.2') } } }) // Background color transitions if (!prefersReducedMotion) { steps.forEach((step, i) => { if (step.highlight) { ScrollTrigger.create({ trigger: `.story-step:nth-child(${i + 1})`, start: 'top 60%', end: 'bottom 40%', onEnter: () => { gsap.to('body', { backgroundColor: 'rgba(124, 244, 226, 0.05)', duration: 1, ease: 'power2.out' }) }, onLeave: () => { gsap.to('body', { backgroundColor: 'transparent', duration: 1, ease: 'power2.out' }) }, onEnterBack: () => { gsap.to('body', { backgroundColor: 'rgba(124, 244, 226, 0.05)', duration: 1, ease: 'power2.out' }) }, onLeaveBack: () => { gsap.to('body', { backgroundColor: 'transparent', duration: 1, ease: 'power2.out' }) } }) } }) } }, container) return () => ctx.revert() }, [steps]) return (
{steps.map((step, i) => (

{step.title}

{step.copy}

))}
) }