Spaces:
Sleeping
Sleeping
| import React, { useRef, useEffect, useState, useMemo } from 'react'; | |
| interface HeroOverlayProps { | |
| visible: boolean; | |
| smoothScrollRef: React.MutableRefObject<number>; | |
| shiftRef: React.MutableRefObject<number>; | |
| shapeMode: string; | |
| isMobile: boolean; | |
| } | |
| // --- OPTIMIZED ANIMATION COMPONENT --- | |
| // Memoized to prevent unnecessary re-renders when parent state changes (like the clock) | |
| const CinematicReveal = React.memo(({ text, delay = 0, visible, className }: { text: string; delay?: number; visible: boolean; className?: string }) => { | |
| return ( | |
| <span className={`inline-block whitespace-nowrap ${className}`}> | |
| {text.split('').map((char, i) => ( | |
| <span | |
| key={i} | |
| style={{ | |
| transitionDelay: `${delay + (i * 35)}ms`, | |
| transitionDuration: '1000ms', | |
| transitionTimingFunction: 'cubic-bezier(0.2, 0.65, 0.3, 0.9)', | |
| }} | |
| className={`inline-block transition-all will-change-transform ${ | |
| visible | |
| ? 'opacity-100 translate-y-0 filter-none scale-100' | |
| : 'opacity-0 translate-y-[100%] blur-sm scale-110' | |
| }`} | |
| > | |
| {char === " " ? "\u00A0" : char} | |
| </span> | |
| ))} | |
| </span> | |
| ); | |
| }); | |
| export const HeroOverlay: React.FC<HeroOverlayProps> = ({ visible, smoothScrollRef, shiftRef, shapeMode, isMobile }) => { | |
| // Refs for direct DOM manipulation (High Performance) | |
| const contentRef = useRef<HTMLDivElement>(null); | |
| const blurRef = useRef<HTMLDivElement>(null); | |
| // State for Live Date | |
| const [dateTime, setDateTime] = useState({ day: '', date: '' }); | |
| // --- LIVE DATE LOGIC --- | |
| useEffect(() => { | |
| const updateTime = () => { | |
| const now = new Date(); | |
| // Format: "SATURDAY" | |
| const day = now.toLocaleDateString('en-US', { weekday: 'long' }).toUpperCase(); | |
| // Format: "DEC 20 2025" | |
| const date = now.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }).toUpperCase().replace(',', ''); | |
| setDateTime({ day, date }); | |
| }; | |
| updateTime(); | |
| // Update every minute (efficient, no need for second-by-second ticking here) | |
| const timer = setInterval(updateTime, 60000); | |
| return () => clearInterval(timer); | |
| }, []); | |
| const getStatus = () => { | |
| if (shapeMode === 'triangle') return 'AWAITING INPUT...'; | |
| if (shapeMode === 'explode') return 'PROCESSING DATA...'; | |
| return 'SYSTEM ONLINE'; | |
| }; | |
| const getStatusColor = () => { | |
| if (shapeMode === 'triangle') return 'text-red-500'; | |
| if (shapeMode === 'explode') return 'text-blue-400'; | |
| return 'text-emerald-500'; | |
| }; | |
| // --- SCROLL LOOP (GPU OPTIMIZED) --- | |
| useEffect(() => { | |
| let frameId: number; | |
| const update = () => { | |
| if (!visible) return; | |
| const y = smoothScrollRef.current; | |
| const h = window.innerHeight; | |
| const processItem = (ref: React.RefObject<HTMLDivElement>, type: 'text' | 'bg', start: number, fade: number = 200) => { | |
| if (!ref.current) return; | |
| let opacity = 0; | |
| let transY = 0; | |
| let scale = 1; | |
| // Logic: Everything fades out as you scroll down | |
| if (y < start + fade) { | |
| opacity = 1 - (y / (start + fade)); | |
| // Only move/scale the text layer, keep the background fixed so it just fades | |
| if (type === 'text') { | |
| transY = -y * 0.5; | |
| scale = 1 - (y / (start + fade)) * 0.05; | |
| } | |
| } else { | |
| opacity = 0; | |
| } | |
| ref.current.style.opacity = Math.max(0, opacity).toFixed(3); | |
| if (type === 'text') { | |
| ref.current.style.transform = `translate3d(0, ${transY.toFixed(3)}px, 0) scale(${scale.toFixed(3)})`; | |
| } | |
| }; | |
| // Apply to Content (Text): Fades and moves up | |
| processItem(contentRef, 'text', 0, 0.8 * h); | |
| // Apply to Blur Layer: Fades out slightly faster to reveal content below | |
| processItem(blurRef, 'bg', 0, 0.7 * h); | |
| // Globe Lateral Shifts Logic | |
| const SHIFT = isMobile ? 0 : 5.5; | |
| let tx = 0; | |
| if (!isMobile) { | |
| if (y > 0.8 * h && y < 2.0 * h) tx = SHIFT; | |
| } | |
| if (shiftRef) shiftRef.current = tx; | |
| frameId = requestAnimationFrame(update); | |
| }; | |
| update(); | |
| return () => cancelAnimationFrame(frameId); | |
| }, [visible, smoothScrollRef, shiftRef, isMobile]); | |
| return ( | |
| <div className={`fixed inset-0 z-20 pointer-events-none transition-opacity duration-1000 ${visible ? 'opacity-100' : 'opacity-0'}`}> | |
| {/* | |
| FULL SCREEN BLUR LAYER | |
| - inset-0: Covers the whole screen. | |
| - backdrop-blur-[2px]: Subtle glass effect. | |
| - bg-black/5: Slight tint for readability. | |
| - Fades out on scroll via blurRef. | |
| */} | |
| <div | |
| ref={blurRef} | |
| className="absolute inset-0 bg-black/5 backdrop-blur-[2px] -z-10" | |
| /> | |
| {/* Hero Content Wrapper */} | |
| <div ref={contentRef} className="absolute inset-0 flex flex-col justify-center px-6 md:px-12 pointer-events-none"> | |
| {/* Content Container - Left Aligned */} | |
| <div className="w-full max-w-[95%] md:max-w-4xl mr-auto relative z-10 text-left"> | |
| {/* Live Date & Day Tags */} | |
| <div className={`flex flex-row items-start justify-start gap-3 mb-8 md:mb-6 transition-opacity duration-1000 delay-500 ${visible ? 'opacity-50' : 'opacity-0'}`}> | |
| <span className="text-[9px] md:text-[10px] font-mono border border-white/30 px-2 py-0.5 rounded-full uppercase tracking-widest text-white"> | |
| {dateTime.day || "LOADING..."} | |
| </span> | |
| <span className="text-[9px] md:text-[10px] font-mono border border-white/30 px-2 py-0.5 rounded-full uppercase tracking-widest text-white"> | |
| {dateTime.date || "..."} | |
| </span> | |
| </div> | |
| {/* Main Headline */} | |
| <h1 className="flex flex-col gap-1 md:gap-2 text-white/90 leading-[1.0] md:leading-[0.85]"> | |
| <span className="text-[11vw] md:text-[8rem] lg:text-[9rem] font-bold md:font-medium tracking-tighter mix-blend-screen overflow-hidden text-left"> | |
| <CinematicReveal text="DESIGNING" visible={visible} delay={100} /> | |
| </span> | |
| <span className="text-[11vw] md:text-[8rem] lg:text-[9rem] font-serif italic text-red-500 mix-blend-screen -mt-2 md:-mt-4 overflow-hidden text-left"> | |
| <CinematicReveal text="INTELLIGENCE" visible={visible} delay={400} /> | |
| </span> | |
| </h1> | |
| {/* Subtext */} | |
| <div className={`mt-8 md:mt-12 flex flex-col md:flex-row items-start justify-start gap-8 transition-all duration-1000 delay-[900ms] ${visible ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-8'}`}> | |
| <div className="h-[1px] w-12 md:w-24 bg-red-500/50 block mt-3"></div> | |
| <p className="text-white/60 max-w-[85%] md:max-w-md font-mono text-xs md:text-sm leading-relaxed text-left mx-0"> | |
| We are an advanced R&D collective specializing in high-frequency neural networks and generative interface design. | |
| <span className="block mt-4 text-white/40"> | |
| Building the cognitive architecture for the next web. | |
| </span> | |
| </p> | |
| </div> | |
| </div> | |
| </div> | |
| {/* Bottom Status Bar */} | |
| <div className="absolute bottom-0 left-0 w-full p-6 md:p-12 z-40 transition-opacity duration-1000 delay-[1200ms]" style={{ opacity: visible ? 1 : 0 }}> | |
| <div className="w-full border-t border-white/10 pt-4 flex flex-col md:flex-row justify-between items-end gap-4"> | |
| <div className="font-mono text-[9px] md:text-[10px] text-white/60 tracking-wider flex items-center gap-2"> | |
| <span className="text-red-500">➜</span> | |
| <span>{shapeMode === 'triangle' ? 'EXEC: NEURAL_VOICE_PROTOCOL' : 'EXEC: TRIANGLE_MAIN_LOOP'}</span> | |
| <span className="w-1.5 h-3 bg-red-500/80 animate-pulse ml-1"></span> | |
| </div> | |
| <div className="flex gap-6 font-mono text-[9px] text-white/30 tracking-widest uppercase md:pr-12"> | |
| <div> | |
| <span className="block text-white/10 mb-1">System</span> | |
| <span className={getStatusColor() + " animate-pulse"}>{getStatus()}</span> | |
| </div> | |
| <div className="hidden md:block"> | |
| <span className="block text-white/10 mb-1">Latency</span> | |
| <span>12ms</span> | |
| </div> | |
| <div className="hidden md:block"> | |
| <span className="block text-white/10 mb-1">Build</span> | |
| <span>v2.0.4-RC</span> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| ) | |
| } |