"use client" import { motion, useReducedMotion } from "motion/react" import { useEffect, useMemo, useState, memo } from "react" import { useOnboarding } from "./onboarding-context" interface OrbProps { size: number initialX: number initialY: number duration: number delay: number revealDelay: number shouldReveal: boolean color: { primary: string secondary: string tertiary: string } } function FloatingOrb({ size, initialX, initialY, duration, delay, revealDelay, shouldReveal, color, }: OrbProps) { const blurPixels = Math.min(64, Math.max(24, Math.floor(size * 0.08))) const gradient = useMemo(() => { return `radial-gradient(circle, ${color.primary} 0%, ${color.secondary} 40%, ${color.tertiary} 70%, transparent 100%)` }, [color.primary, color.secondary, color.tertiary]) const style = useMemo(() => { return { width: size, height: size, background: gradient, filter: `blur(${blurPixels}px)`, willChange: "transform, opacity", mixBlendMode: "plus-lighter", } as any }, [size, gradient, blurPixels]) const initial = useMemo(() => { return { x: initialX, y: initialY, scale: 0, opacity: 0, } }, [initialX, initialY]) const animate = useMemo(() => { if (!shouldReveal) { return { x: initialX, y: initialY, scale: 0, opacity: 0, } } return { x: [initialX, initialX + 200, initialX - 150, initialX + 100, initialX], y: [initialY, initialY - 180, initialY + 120, initialY - 80, initialY], scale: [0.8, 1.2, 0.9, 1.1, 0.8], opacity: 0.7, } }, [shouldReveal, initialX, initialY]) const transition = useMemo(() => { return { x: { duration: shouldReveal ? duration : 0, repeat: shouldReveal ? Number.POSITIVE_INFINITY : 0, ease: [0.42, 0, 0.58, 1], delay: shouldReveal ? delay + revealDelay : 0, }, y: { duration: shouldReveal ? duration : 0, repeat: shouldReveal ? Number.POSITIVE_INFINITY : 0, ease: [0.42, 0, 0.58, 1], delay: shouldReveal ? delay + revealDelay : 0, }, scale: { duration: shouldReveal ? duration : 0.8, repeat: shouldReveal ? Number.POSITIVE_INFINITY : 0, ease: shouldReveal ? [0.42, 0, 0.58, 1] : [0, 0, 0.58, 1], delay: shouldReveal ? delay + revealDelay : revealDelay, }, opacity: { duration: 1.2, ease: [0, 0, 0.58, 1], delay: shouldReveal ? revealDelay : 0, }, } as any }, [shouldReveal, duration, delay, revealDelay]) return ( ) } const MemoFloatingOrb = memo(FloatingOrb) export function FloatingOrbs() { const { orbsRevealed } = useOnboarding() const reduceMotion = useReducedMotion() const [mounted, setMounted] = useState(false) const [orbs, setOrbs] = useState< Array<{ id: number size: number initialX: number initialY: number duration: number delay: number revealDelay: number color: { primary: string secondary: string tertiary: string } }> >([]) useEffect(() => { setMounted(true) const screenWidth = typeof window !== "undefined" ? window.innerWidth : 1200 const screenHeight = typeof window !== "undefined" ? window.innerHeight : 800 // Define edge zones (avoiding center) const edgeThickness = Math.min(screenWidth, screenHeight) * 0.25 // 25% of smaller dimension // Define rainbow color palette const colorPalette = [ { // Magenta primary: "rgba(255, 0, 150, 0.6)", secondary: "rgba(255, 100, 200, 0.4)", tertiary: "rgba(255, 150, 220, 0.1)", }, { // Yellow primary: "rgba(255, 235, 59, 0.6)", secondary: "rgba(255, 245, 120, 0.4)", tertiary: "rgba(255, 250, 180, 0.1)", }, { // Light Blue primary: "rgba(100, 181, 246, 0.6)", secondary: "rgba(144, 202, 249, 0.4)", tertiary: "rgba(187, 222, 251, 0.1)", }, { // Orange (keeping original) primary: "rgba(255, 154, 0, 0.6)", secondary: "rgba(255, 206, 84, 0.4)", tertiary: "rgba(255, 154, 0, 0.1)", }, { // Very Light Red/Pink primary: "rgba(255, 138, 128, 0.6)", secondary: "rgba(255, 171, 145, 0.4)", tertiary: "rgba(255, 205, 210, 0.1)", }, ] // Generate orb configurations positioned along edges const newOrbs = Array.from({ length: 8 }, (_, i) => { let x: number let y: number const zone = i % 4 // Rotate through 4 zones: top, right, bottom, left switch (zone) { case 0: // Top edge x = Math.random() * screenWidth y = Math.random() * edgeThickness break case 1: // Right edge x = screenWidth - edgeThickness + Math.random() * edgeThickness y = Math.random() * screenHeight break case 2: // Bottom edge x = Math.random() * screenWidth y = screenHeight - edgeThickness + Math.random() * edgeThickness break case 3: // Left edge x = Math.random() * edgeThickness y = Math.random() * screenHeight break default: x = Math.random() * screenWidth y = Math.random() * screenHeight } return { id: i, size: Math.random() * 300 + 200, // 200px to 500px initialX: x, initialY: y, duration: Math.random() * 20 + 15, // 15-35 seconds (longer for more gentle movement) delay: i * 0.4, // Staggered start for floating animation revealDelay: i * 0.2, // Faster staggered reveal color: colorPalette[i % colorPalette.length]!, // Cycle through rainbow colors } }) setOrbs(newOrbs) }, []) if (!mounted || orbs.length === 0) return null return (
{orbs.map((orb) => ( ))}
) }