"use client"; import Image from "next/image"; import { useEffect, useRef, useState } from "react"; import { motion } from "framer-motion"; import { gsap } from "gsap"; import { ScrollTrigger } from "gsap/ScrollTrigger"; // Register GSAP plugins if (typeof window !== "undefined") { gsap.registerPlugin(ScrollTrigger); } type TuxedoVideoHeroProps = { eyebrow?: string; title: string; description: string; olatheSweetLogoUrl?: string | null; primaryButton?: string; secondaryButton?: string; onPrimaryClick?: () => void; onSecondaryClick?: () => void; }; const VIDEO_URL = "/videos/tuxedo-hero.mp4"; // ───────────────────────────────────────────────────────────────────────────── // CINEMATIC HERO - SCROLL-DRIVEN ANIMATIONS // ───────────────────────────────────────────────────────────────────────────── export default function TuxedoVideoHero({ eyebrow, title, description, olatheSweetLogoUrl, primaryButton, secondaryButton, onPrimaryClick, onSecondaryClick, }: TuxedoVideoHeroProps) { const logoSrc = olatheSweetLogoUrl; const sectionRef = useRef(null); const contentRef = useRef(null); const videoRef = useRef(null); const [isVisible, setIsVisible] = useState(false); const [scrollProgress, setScrollProgress] = useState(0); // Trigger entrance animation on mount useEffect(() => { const timer = setTimeout(() => setIsVisible(true), 100); return () => clearTimeout(timer); }, []); // ─── GSAP SCROLL ANIMATIONS ──────────────────────────────────────────────── useEffect(() => { if (typeof window === "undefined" || !sectionRef.current) return; // Respect reduced motion preference const prefersReducedMotion = window.matchMedia?.("(prefers-reduced-motion: reduce)").matches === true; if (prefersReducedMotion) return; const ctx = gsap.context(() => { // Scroll progress tracking ScrollTrigger.create({ trigger: sectionRef.current, start: "top top", end: "bottom top", scrub: true, onUpdate: (self) => { setScrollProgress(self.progress); }, }); // Hero content pin - fade out on scroll if (contentRef.current) { gsap.to(contentRef.current, { y: -80, opacity: 0, ease: "power2.inOut", scrollTrigger: { trigger: sectionRef.current, start: "top top", end: "40% top", scrub: 1.2, }, }); } // Video scale + parallax if (videoRef.current) { gsap.to(videoRef.current, { scale: 1.15, y: 100, ease: "power2.inOut", scrollTrigger: { trigger: sectionRef.current, start: "top top", end: "50% top", scrub: 1.5, }, }); } // Staggered entrance for content elements const contentElements = gsap.utils.toArray(".hero-reveal"); contentElements.forEach((el, i) => { gsap.fromTo( el, { opacity: 0, y: 40, scale: 0.95 }, { opacity: 1, y: 0, scale: 1, duration: 1, ease: "power3.out", scrollTrigger: { trigger: el, start: "top 90%", toggleActions: "play none none reverse", }, } ); }); // Parallax floating elements const floatElements = gsap.utils.toArray(".parallax-float"); floatElements.forEach((el, idx) => { gsap.to(el, { y: -80 - idx * 20, scrollTrigger: { trigger: el, start: "top bottom", end: "bottom top", scrub: 1, }, }); }); // Scroll indicator bounce on scroll gsap.to(".scroll-indicator", { y: 20, opacity: 0, scrollTrigger: { trigger: sectionRef.current, start: "10% top", end: "30% top", scrub: 1, }, }); }, sectionRef); return () => ctx.revert(); }, []); // Smooth scroll to stops section const handlePrimaryClick = () => { const stopsSection = document.getElementById("stops"); if (stopsSection) { stopsSection.scrollIntoView({ behavior: "smooth", block: "start" }); } else if (onPrimaryClick) { onPrimaryClick(); } }; return ( <> {/* ─── SCROLL PROGRESS BAR ──────────────────────────────────────────── */}
{/* ─── CINEMATIC HERO SECTION ──────────────────────────────────────── */}
{/* Background video with parallax */}
{/* ─── GLOBAL ANIMATIONS ──────────────────────────────────────────────── */} ); }