turash/bugulma/frontend/components/landing/Hero.tsx
2025-12-15 10:06:41 +01:00

308 lines
13 KiB
TypeScript

import ResourceExchangeVisualization from '@/components/landing/ResourceExchangeVisualization.tsx';
import Badge from '@/components/ui/Badge.tsx';
import Button from '@/components/ui/Button.tsx';
import { Container, Grid } from '@/components/ui/layout';
import { useTranslation } from '@/hooks/useI18n.tsx';
import { motion } from 'framer-motion';
import React from 'react';
interface HeroProps {
onNavigateToMap: () => void;
onAddOrganizationClick: () => void;
addOrgButtonRef: React.Ref<HTMLButtonElement>;
}
const Hero = ({ onNavigateToMap, onAddOrganizationClick, addOrgButtonRef }: HeroProps) => {
const { t } = useTranslation();
return (
<section
className="relative bg-background isolate overflow-hidden min-h-screen flex items-center scroll-snap-align-start"
aria-label={t('hero.title')}
>
{/* Animated background pattern */}
<motion.div
className="absolute inset-0 -z-10 opacity-30"
style={{
backgroundImage: `radial-gradient(hsl(var(--border) / 0.5) 1px, transparent 1px)`,
backgroundSize: 'var(--font-size-2xl) var(--font-size-2xl)',
}}
animate={{
backgroundPosition: ['0px 0px', 'var(--font-size-2xl) var(--font-size-2xl)'],
}}
transition={{
duration: 20,
ease: 'linear',
repeat: Infinity,
}}
/>
{/* Abstract backlight effect */}
<div className="absolute inset-0 overflow-hidden pointer-events-none" style={{ zIndex: 0 }}>
{/* Green backlight orb - behind "Connect Your Business" (left/top) */}
<motion.div
className="absolute rounded-full"
style={{
width: 'clamp(400px, 50vw, 900px)',
height: 'clamp(400px, 50vw, 900px)',
background: `radial-gradient(circle, var(--primary) 0%, rgba(46, 125, 50, 0.2) 20%, transparent 60%)`,
left: '5%',
top: '15%',
filter: 'blur(80px)',
mixBlendMode: 'screen',
opacity: 0.6,
}}
animate={{
x: [-30, 30, -30],
y: [-20, 20, -20],
scale: [1, 1.05, 1],
}}
transition={{
duration: 15,
repeat: Infinity,
ease: 'easeInOut',
}}
/>
{/* Blue backlight orb - behind "Grow Together" (right/bottom) */}
<motion.div
className="absolute rounded-full"
style={{
width: 'clamp(400px, 50vw, 900px)',
height: 'clamp(400px, 50vw, 900px)',
background: `radial-gradient(circle, var(--accent) 0%, rgba(74, 144, 164, 0.2) 20%, transparent 60%)`,
right: '10%',
top: '45%',
filter: 'blur(80px)',
mixBlendMode: 'screen',
opacity: 0.6,
}}
animate={{
x: [30, -30, 30],
y: [-25, 25, -25],
scale: [1, 1.05, 1],
}}
transition={{
duration: 18,
repeat: Infinity,
ease: 'easeInOut',
delay: 2,
}}
/>
</div>
{/* Gradient overlay - positioned after backlight */}
<div className="absolute inset-x-0 bottom-0 h-1/2 bg-gradient-to-t from-background via-background/50 to-transparent -z-10" />
{/* Floating particles effect */}
<div className="absolute inset-0 -z-10 overflow-hidden">
{[...Array(20)].map((_, i) => (
<motion.div
key={i}
className="absolute w-1 h-1 bg-primary/20 rounded-full"
style={{
left: `${Math.random() * 100}%`,
top: `${Math.random() * 100}%`,
}}
animate={{
y: [-20, -100],
opacity: [0, 1, 0],
}}
transition={{
duration: Math.random() * 10 + 10,
repeat: Infinity,
delay: Math.random() * 10,
ease: 'easeOut',
}}
/>
))}
</div>
<Container size="xl" className="py-20 sm:py-24 md:py-32 lg:py-40 xl:py-48 2xl:py-56 w-full">
<Grid cols={{ md: 1, lg: 2 }} gap={{ md: '2xl', lg: '3xl', xl: '4xl' }} align="center">
<div className="text-center lg:text-left relative z-10">
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.8, ease: 'easeOut', delay: 0.2 }}
>
{/* Animated badge */}
<div className="mb-6 lg:mb-8 flex justify-center lg:justify-start">
<Badge
variant="material"
showDot={true}
dotColor="primary"
textColor="accent"
size="md"
animate={true}
>
{t('hero.kicker')}
</Badge>
</div>
{/* Main heading with creative 3D effects and color variations */}
<motion.h1
className="font-serif text-4xl sm:text-5xl md:text-6xl lg:text-7xl xl:text-8xl 2xl:text-9xl font-bold tracking-tight relative"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.8, delay: 0.4 }}
>
{(() => {
const title = t('hero.title');
// Split title by period to separate "Connect Your Business" and "Grow Together"
const parts = title.split('.').filter((p) => p.trim());
return parts.map((part, partIndex) => {
const words = part.trim().split(' ');
const isFirstPart = partIndex === 0;
return (
<motion.span
key={partIndex}
className="block mb-2 md:mb-3"
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{
duration: 0.6,
delay: 0.6 + partIndex * 0.2,
ease: [0.16, 1, 0.3, 1],
}}
>
<span className="relative inline-block">
{words.map((word, wordIndex) => (
<motion.span
key={wordIndex}
className="inline-block mr-2 md:mr-3 relative"
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{
duration: 0.5,
delay: 0.8 + partIndex * 0.2 + wordIndex * 0.08,
ease: [0.16, 1, 0.3, 1],
}}
>
{/* 3D shadow layers with color - positioned behind */}
<span
className="absolute inset-0 pointer-events-none"
aria-hidden="true"
style={{
textShadow: isFirstPart
? '2px 2px 0px hsl(var(--primary) / 0.4), 4px 4px 0px hsl(var(--primary) / 0.25), 6px 6px 12px hsl(var(--primary) / 0.15), 8px 8px 20px hsl(var(--primary) / 0.1)'
: '2px 2px 0px hsl(var(--accent) / 0.4), 4px 4px 0px hsl(var(--accent) / 0.25), 6px 6px 12px hsl(var(--accent) / 0.15), 8px 8px 20px hsl(var(--accent) / 0.1)',
color: 'transparent',
zIndex: 0,
}}
>
{word}
</span>
{/* Main text - solid and visible with colored shadow */}
<span
className={`relative z-10 inline-block ${
isFirstPart ? 'text-primary' : 'text-accent'
}`}
style={{
textShadow: isFirstPart
? '0 2px 4px hsl(var(--primary) / 0.4), 0 4px 8px hsl(var(--primary) / 0.2), 0 6px 12px hsl(var(--primary) / 0.1)'
: '0 2px 4px hsl(var(--accent) / 0.4), 0 4px 8px hsl(var(--accent) / 0.2), 0 6px 12px hsl(var(--accent) / 0.1)',
filter: isFirstPart
? 'drop-shadow(0 1px 2px hsl(var(--primary) / 0.3))'
: 'drop-shadow(0 1px 2px hsl(var(--accent) / 0.3))',
}}
>
{word}
</span>
{/* Animated glow effect */}
<motion.span
className="absolute inset-0 blur-2xl -z-10"
aria-hidden="true"
style={{
background: isFirstPart
? 'radial-gradient(circle, hsl(var(--primary) / 0.3), transparent 70%)'
: 'radial-gradient(circle, hsl(var(--accent) / 0.3), transparent 70%)',
}}
animate={{
opacity: [0.2, 0.4, 0.2],
scale: [1, 1.1, 1],
}}
transition={{
duration: 3 + wordIndex * 0.2,
repeat: Infinity,
ease: 'easeInOut',
}}
/>
</motion.span>
))}
</span>
</motion.span>
);
});
})()}
</motion.h1>
<motion.p
className="mt-6 text-lg md:text-xl lg:text-2xl xl:text-3xl text-muted-foreground max-w-xl xl:max-w-2xl mx-auto lg:mx-0"
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.8, delay: 1 }}
>
{t('hero.subtitle')}
</motion.p>
{/* Action buttons */}
<div className="mt-10 lg:mt-12 xl:mt-16 flex flex-col sm:flex-row items-center lg:items-start gap-4 lg:gap-6">
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 1.2, ease: 'easeOut' }}
className="w-full sm:w-auto"
>
<Button
onClick={onNavigateToMap}
size="lg"
className="w-full sm:w-auto shadow-lg shadow-primary/30 hover:shadow-xl hover:shadow-primary/40 transition-all duration-200"
>
{t('hero.mapButton')}
</Button>
</motion.div>
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 1.4, ease: 'easeOut' }}
className="w-full sm:w-auto"
>
<Button
ref={addOrgButtonRef}
onClick={onAddOrganizationClick}
variant="outline"
size="lg"
className="w-full sm:w-auto bg-background/80 backdrop-blur-sm hover:bg-background/90 transition-all duration-200 hover:border-primary/50"
>
{t('hero.addButton')}
</Button>
</motion.div>
</div>
</motion.div>
</div>
{/* Modern sector visualization */}
<motion.div
className="row-start-1 lg:col-start-2 relative z-10 flex items-center justify-center lg:justify-end"
initial={{ opacity: 0, scale: 0.8 }}
animate={{ opacity: 1, scale: 1 }}
transition={{ duration: 1, delay: 0.8 }}
>
<div className="w-full max-w-2xl">
<ResourceExchangeVisualization maxItems={6} showStats={true} />
</div>
</motion.div>
</Grid>
</Container>
</section>
);
};
export default React.memo(Hero);