import { useEffect, useRef, useState } from "react"; import { useAnimate } from "motion/react"; import { twMerge } from "tailwind-merge"; import { clsx } from "clsx"; function cn(...inputs) { return twMerge(clsx(inputs)); } export const FedoraSVG = ({ className }) => ( ); // Custom Geometric SVGs const LetterA = ({ className, ...props }) => ( ); const LetterP = ({ className, ...props }) => ( ); const LetterD = ({ className, ...props }) => ( ); const LetterF = ({ className, ...props }) => ( ); export function LandingAnimation({ onComplete }) { const [scope, animate] = useAnimate(); const aRef = useRef(null); const gentRef = useRef(null); const pRef = useRef(null); const dRef = useRef(null); const fRef = useRef(null); const [phase, setPhase] = useState(0); useEffect(() => { let active = true; const runSeq = async () => { // Wait for layout/fonts to mount await new Promise(r => setTimeout(r, 400)); if (!active) return; const fedoraNode = document.getElementById("fedora"); const aRect = aRef.current?.getBoundingClientRect(); const fRect = fRef.current?.getBoundingClientRect(); if (fedoraNode && aRect && fRect) { const targetRightX = fRect.right - aRect.left + 20; const startY = fRect.height * 0.4; // Snap Fedora directly to the right side of Agent PDF initially await animate(fedoraNode, { x: targetRightX, y: startY, rotate: 10 }, { duration: 0 }); await animate(fedoraNode, { opacity: 1 }, { duration: 0.3 }); await new Promise(r => setTimeout(r, 400)); if (!active) return; // 1. Hat Boomerang to right (fly out further) await animate(fedoraNode, { x: targetRightX + aRect.width * 1.5, y: startY - aRect.height * 0.2, rotate: 30 }, { duration: 0.6, ease: "easeOut" }); // 2. Hat loops back to lands perfectly on 'A' Base coordinates (0, 0) await animate(fedoraNode, { x: 5, y: -aRect.height * 0.05, rotate: -5, scale: 0.95 }, { duration: 0.7, ease: "backOut" }); setPhase(1); // Hat landed await new Promise(r => setTimeout(r, 400)); if (!active) return; // 3. 'gent' collapses if (gentRef.current) { const gentWidth = gentRef.current.offsetWidth; gentRef.current.style.width = `${gentWidth}px`; await animate(gentRef.current, { width: 0, opacity: 0, paddingRight: 0, marginLeft: 0 }, { duration: 0.6, ease: "backIn" }); gentRef.current.style.display = 'none'; } // Collapse spacer await animate("#spacer", { width: 0 }, { duration: 0.2 }); // 4. P, D, F merge into A const currentARect2 = aRef.current?.getBoundingClientRect(); const mergeLetter = async (ref, delay) => { if (!ref.current || !currentARect2) return; const rect = ref.current.getBoundingClientRect(); const offsetX = currentARect2.left - rect.left; animate(ref.current, { x: offsetX, opacity: 0, filter: "blur(4px)" }, { duration: 0.5, delay }); }; mergeLetter(pRef, 0); mergeLetter(dRef, 0.15); await mergeLetter(fRef, 0.3); if (!active) return; // 5. Center the block! We slide #text-container. const scopeRect = scope.current?.getBoundingClientRect(); const finalARect = aRef.current?.getBoundingClientRect(); if (scopeRect && finalARect) { const aCenterX = finalARect.left + finalARect.width / 2; const scopeCenterX = scopeRect.left + scopeRect.width / 2; const offset = scopeCenterX - aCenterX; await animate("#text-container", { x: offset }, { duration: 0.8, ease: "easeInOut" }); } setPhase(2); // Merge & Center complete // Notify parent that animation is complete if (onComplete) onComplete(); } }; runSeq(); return () => { active = false; }; }, [animate, onComplete, scope]); return (