"use client"; import { motion, useReducedMotion, type Variants } from "motion/react"; import { Children, type CSSProperties, type ReactNode } from "react"; import { cn } from "@/lib/utils"; type MaskRevealProps = { /** Each item is a separate line with its own mask. */ lines: ReactNode[]; className?: string; delay?: number; stagger?: number; duration?: number; style?: CSSProperties; as?: "h1" | "h2" | "h3" | "p" | "div"; }; /** * Cinematic reveal: each line rises from below its own overflow mask. * Use for headlines that need physical weight. */ export function MaskReveal({ lines, className, delay = 0, stagger = 0.12, duration = 0.85, style, as: Tag = "h2", }: MaskRevealProps) { const reduce = useReducedMotion(); const container: Variants = { hidden: {}, show: { transition: { staggerChildren: reduce ? 0 : stagger, delayChildren: delay, }, }, }; const line: Variants = reduce ? { hidden: { opacity: 0 }, show: { opacity: 1, transition: { duration: 0.2 } }, } : { hidden: { y: "115%", opacity: 0 }, show: { y: "0%", opacity: 1, transition: { duration, ease: [0.22, 1, 0.36, 1] }, }, }; const MotionTag = motion[Tag] as typeof motion.div; return ( {Children.toArray( lines.map((content, i) => ( {content} )), )} ); }