297 lines
11 KiB
TypeScript

import { useState, useEffect, useRef } from "react";
import { useLocation } from "wouter";
import { Button } from "@/components/ui/button";
import { ArrowRight, DollarSign, IndianRupee } from "lucide-react";
import heroVideo from "@assets/generated_videos/abstract_finance_visualization_video.mp4";
import StrategySelectorModal from "./StrategySelectorModal";
type HeroSectionProps = {
onExploreStrategies: () => void;
};
export default function HeroSection({ onExploreStrategies }: HeroSectionProps) {
const containerRef = useRef<HTMLDivElement>(null);
const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 });
const [isLoaded, setIsLoaded] = useState(false);
const [scrollY, setScrollY] = useState(0);
const [, navigate] = useLocation();
useEffect(() => {
setIsLoaded(true);
}, []);
useEffect(() => {
const handleScroll = () => {
setScrollY(window.scrollY);
};
window.addEventListener("scroll", handleScroll, { passive: true });
return () => window.removeEventListener("scroll", handleScroll);
}, []);
useEffect(() => {
const handleMouseMove = (e: MouseEvent) => {
if (!containerRef.current) return;
const rect = containerRef.current.getBoundingClientRect();
const x = (e.clientX - rect.left - rect.width / 2) / rect.width;
const y = (e.clientY - rect.top - rect.height / 2) / rect.height;
setMousePosition({ x: x * 30, y: y * 30 });
};
const container = containerRef.current;
container?.addEventListener("mousemove", handleMouseMove);
return () => container?.removeEventListener("mousemove", handleMouseMove);
}, []);
const parallaxY = scrollY * 0.5;
const videoScale = 1 + scrollY * 0.0003;
const videoOpacity = Math.max(0.4 - scrollY * 0.0005, 0.15);
return (
<section
ref={containerRef}
className="relative min-h-screen flex items-center justify-center overflow-hidden"
data-testid="section-hero"
style={{ perspective: "1000px" }}
>
<div
className="absolute inset-0 z-0"
style={{
transform: `translateY(${parallaxY}px) scale(${videoScale})`,
transformOrigin: "center center",
}}
>
<video
autoPlay
loop
muted
playsInline
className={`absolute inset-0 w-full h-full object-cover transition-opacity duration-1000 ${
isLoaded ? "opacity-100" : "opacity-0"
}`}
style={{
opacity: videoOpacity,
filter: "blur(1px) saturate(0.8)",
}}
>
<source src={heroVideo} type="video/mp4" />
</video>
<div className="absolute inset-0 bg-gradient-to-b from-background/70 via-background/50 to-background" />
</div>
<div
className="absolute inset-0 transition-transform duration-300 ease-out z-10"
style={{
transform: `translate3d(${mousePosition.x * 0.3}px, ${mousePosition.y * 0.3}px, 0) rotateX(${mousePosition.y * 0.02}deg) rotateY(${-mousePosition.x * 0.02}deg)`,
transformStyle: "preserve-3d",
}}
>
<div
className={`absolute top-1/4 left-1/4 w-96 h-96 bg-primary/20 rounded-full blur-3xl transition-all duration-1000 ${
isLoaded ? "opacity-100 scale-100" : "opacity-0 scale-50"
}`}
style={{
transform: `translateZ(50px)`,
}}
/>
<div
className={`absolute bottom-1/3 right-1/4 w-80 h-80 bg-chart-2/20 rounded-full blur-3xl transition-all duration-1000 delay-200 ${
isLoaded ? "opacity-100 scale-100" : "opacity-0 scale-50"
}`}
style={{
transform: `translateZ(30px)`,
}}
/>
<div
className={`absolute top-1/2 right-1/3 w-64 h-64 bg-chart-3/15 rounded-full blur-3xl transition-all duration-1000 delay-500 ${
isLoaded ? "opacity-100 scale-100" : "opacity-0 scale-50"
}`}
style={{
transform: `translateZ(70px)`,
}}
/>
</div>
<div className="absolute inset-0 overflow-hidden z-10">
{[...Array(25)].map((_, i) => (
<div
key={i}
className={`absolute rounded-full transition-all duration-700 ${
isLoaded ? "opacity-60" : "opacity-0"
}`}
style={{
width: `${3 + (i % 4) * 2}px`,
height: `${3 + (i % 4) * 2}px`,
left: `${5 + (i * 3.8)}%`,
top: `${15 + Math.sin(i * 0.7) * 35}%`,
background: i % 3 === 0
? "hsl(var(--primary))"
: i % 3 === 1
? "hsl(var(--chart-2))"
: "hsl(var(--chart-3))",
transform: `translate3d(${mousePosition.x * (0.5 + i * 0.08)}px, ${mousePosition.y * (0.5 + i * 0.08)}px, ${i * 5}px)`,
transitionDelay: `${i * 30}ms`,
animation: `float-3d ${4 + (i % 4)}s ease-in-out infinite`,
animationDelay: `${i * 0.15}s`,
}}
/>
))}
</div>
<div
className="absolute inset-0 z-10 pointer-events-none"
style={{
transform: `translate3d(${mousePosition.x * 0.1}px, ${mousePosition.y * 0.1}px, 0)`,
}}
>
<div
className={`absolute top-20 left-[15%] w-20 h-20 transition-all duration-1000 ${
isLoaded ? "opacity-100 rotate-12" : "opacity-0 rotate-0"
}`}
style={{
transform: `rotate(${12 + mousePosition.x * 0.3}deg) translateZ(100px)`,
animation: "float-rotate 8s ease-in-out infinite",
}}
aria-hidden="true"
>
<DollarSign className="w-full h-full text-primary/25 drop-shadow-sm" strokeWidth={1.25} />
</div>
<div
className={`absolute bottom-32 right-[20%] w-16 h-16 transition-all duration-1000 delay-300 ${
isLoaded ? "opacity-100" : "opacity-0"
}`}
style={{
transform: `translateZ(80px)`,
animation: "pulse-float 6s ease-in-out infinite",
}}
aria-hidden="true"
>
<IndianRupee className="w-full h-full text-chart-2/25 drop-shadow-sm" strokeWidth={1.25} />
</div>
<div
className={`absolute top-1/3 right-[10%] w-12 h-12 transition-all duration-1000 delay-500 ${
isLoaded ? "opacity-100 rotate-45" : "opacity-0 rotate-0"
}`}
style={{
transform: `rotate(${45 + mousePosition.y * 0.5}deg) translateZ(60px)`,
animation: "float-rotate-reverse 10s ease-in-out infinite",
}}
aria-hidden="true"
>
<DollarSign className="w-full h-full text-chart-3/25 drop-shadow-sm" strokeWidth={1.25} />
</div>
</div>
<div
className="relative z-20 max-w-4xl mx-auto px-6 text-center"
style={{
transform: `translate3d(${mousePosition.x * 0.15}px, ${mousePosition.y * 0.15}px, 100px)`,
transformStyle: "preserve-3d",
}}
>
<div
className={`transition-all duration-1000 delay-300 ${
isLoaded ? "opacity-100 translate-y-0" : "opacity-0 translate-y-8"
}`}
>
<h1
className="text-5xl md:text-7xl font-bold tracking-tight mb-6 leading-tight"
data-testid="text-hero-headline"
style={{ transform: "translateZ(20px)" }}
>
<span className="inline-block animate-fade-in-up" style={{ animationDelay: "0.4s" }}>
Invest with
</span>
<br />
<span
className="text-primary inline-block animate-fade-in-up bg-clip-text"
style={{
animationDelay: "0.6s",
textShadow: "0 0 60px hsl(var(--primary) / 0.3)",
}}
>
Clarity
</span>
</h1>
</div>
<p
className={`text-xl md:text-2xl text-muted-foreground max-w-2xl mx-auto mb-12 leading-relaxed transition-all duration-1000 delay-700 ${
isLoaded ? "opacity-100 translate-y-0" : "opacity-0 translate-y-8"
}`}
data-testid="text-hero-subheadline"
style={{ transform: "translateZ(10px)" }}
>
Simple, disciplined strategies for long-term wealth building.
No noise. Just steady growth. test for the auto deployment by quantfortune team
</p>
<div
className={`flex flex-col sm:flex-row items-center justify-center gap-4 transition-all duration-1000 delay-1000 ${
isLoaded ? "opacity-100 translate-y-0" : "opacity-0 translate-y-8"
}`}
style={{ transform: "translateZ(30px)" }}
>
<Button
size="lg"
className="rounded-xl px-8 py-6 text-lg group relative overflow-hidden shadow-lg shadow-primary/20"
data-testid="button-explore-strategies"
onClick={onExploreStrategies}
>
<span className="relative z-10 flex items-center">
Explore Strategies test for thhe auto deployment by quantfortune team
<ArrowRight className="ml-2 h-5 w-5 transition-transform group-hover:translate-x-1" />
</span>
<div className="absolute inset-0 bg-gradient-to-r from-primary to-chart-1 opacity-0 group-hover:opacity-100 transition-opacity duration-500" />
</Button>
<Button
variant="ghost"
size="lg"
className="rounded-xl px-8 py-6 text-lg text-muted-foreground backdrop-blur-sm"
data-testid="button-learn-more"
onClick={() => navigate("/learn-more")}
>
Learn More
</Button>
</div>
</div>
<div
className={`absolute bottom-8 left-1/2 -translate-x-1/2 z-20 transition-all duration-1000 delay-1500 ${
isLoaded ? "opacity-100 translate-y-0" : "opacity-0 translate-y-4"
}`}
>
<div className="w-6 h-10 border-2 border-muted-foreground/30 rounded-full flex justify-center pt-2 backdrop-blur-sm">
<div className="w-1.5 h-3 bg-muted-foreground/50 rounded-full animate-bounce" />
</div>
</div>
<style>{`
@keyframes float-3d {
0%, 100% { transform: translateY(0px) translateZ(0px); }
50% { transform: translateY(-15px) translateZ(10px); }
}
@keyframes fade-in-up {
from { opacity: 0; transform: translateY(20px) translateZ(0); }
to { opacity: 1; transform: translateY(0) translateZ(20px); }
}
@keyframes float-rotate {
0%, 100% { transform: rotate(12deg) translateY(0) translateZ(100px); }
50% { transform: rotate(18deg) translateY(-20px) translateZ(120px); }
}
@keyframes float-rotate-reverse {
0%, 100% { transform: rotate(45deg) translateY(0) translateZ(60px); }
50% { transform: rotate(35deg) translateY(-15px) translateZ(80px); }
}
@keyframes pulse-float {
0%, 100% { transform: scale(1) translateY(0) translateZ(80px); opacity: 0.6; }
50% { transform: scale(1.1) translateY(-10px) translateZ(100px); opacity: 0.8; }
}
.animate-fade-in-up {
animation: fade-in-up 0.8s ease-out forwards;
opacity: 0;
}
`}</style>
</section>
);
}