"use client"; import { useEffect, useRef } from "react"; import { gsap } from "gsap"; import SplitType from "split-type"; /** * Splits the hero H1 into lines + chars and reveals them with a staggered * mask-up on load — the orchestrated "page load" delight moment. * Under reduced motion the text is simply shown as-is. The real text is * always in the DOM (SplitType only re-wraps the same characters), so SEO * and screen readers read the full heading. */ export default function KineticHeadline({ children, className, }: { children: React.ReactNode; className?: string; }) { const ref = useRef(null); useEffect(() => { const el = ref.current; if (!el) return; const reduce = window.matchMedia("(prefers-reduced-motion: reduce)").matches; if (reduce) { el.style.opacity = "1"; return; } const split = new SplitType(el, { types: "lines,words" }); el.style.opacity = "1"; // wrap each line in an overflow-hidden mask split.lines?.forEach((line) => { const mask = document.createElement("span"); mask.className = "line-mask"; line.parentNode?.insertBefore(mask, line); mask.appendChild(line); }); const tween = gsap.from(split.words || [], { yPercent: 115, opacity: 0, rotateZ: 2, duration: 1, ease: "expo.out", stagger: 0.04, delay: 0.15, }); return () => { tween.kill(); split.revert(); }; }, []); return (

{children}

); }