- Added global ScrollDepthTracker (25%, 50%, 75%, 100%) - Implemented ProductEngagementTracker for deep product analytics - Added field-level tracking to ContactForm and RequestQuoteForm - Standardized SEO metadata (canonical, alternates, x-default) across all routes - Created reusable TrackedLink and TrackedButton components for server components - Fixed 'useAnalytics' hook error in Footer.tsx by adding 'use client'
199 lines
6.5 KiB
TypeScript
199 lines
6.5 KiB
TypeScript
'use client';
|
|
|
|
import Scribble from '@/components/Scribble';
|
|
import { Button, Container, Heading, Section } from '@/components/ui';
|
|
import { motion } from 'framer-motion';
|
|
import { useTranslations, useLocale } from 'next-intl';
|
|
import dynamic from 'next/dynamic';
|
|
import { useAnalytics } from '../analytics/useAnalytics';
|
|
import { AnalyticsEvents } from '../analytics/analytics-events';
|
|
const HeroIllustration = dynamic(() => import('./HeroIllustration'), { ssr: false });
|
|
|
|
export default function Hero() {
|
|
const t = useTranslations('Home.hero');
|
|
const locale = useLocale();
|
|
const { trackEvent } = useAnalytics();
|
|
|
|
return (
|
|
<Section className="relative min-h-[85vh] md:h-[90vh] flex flex-col items-center justify-center overflow-hidden bg-primary py-12 md:py-0 lg:py-0">
|
|
<Container className="relative z-10 text-center md:text-left text-white w-full order-2 md:order-none">
|
|
<motion.div
|
|
className="max-w-5xl mx-auto md:mx-0"
|
|
initial="hidden"
|
|
animate="visible"
|
|
variants={containerVariants}
|
|
>
|
|
<motion.div variants={headingVariants}>
|
|
<Heading
|
|
level={1}
|
|
className="text-center md:text-left mb-6 md:mb-8 md:max-w-none text-white text-4xl sm:text-5xl md:text-7xl font-extrabold [text-shadow:_-2px_-2px_0_#002b49,_2px_-2px_0_#002b49,_-2px_2px_0_#002b49,_2px_2px_0_#002b49,_-2px_0_0_#002b49,_2px_0_0_#002b49,_0_-2px_0_#002b49,_0_2px_0_#002b49]"
|
|
>
|
|
{t.rich('title', {
|
|
green: (chunks) => (
|
|
<span className="relative inline-block">
|
|
<motion.span
|
|
className="relative z-10 text-accent italic"
|
|
variants={accentVariants}
|
|
>
|
|
{chunks}
|
|
</motion.span>
|
|
<motion.div
|
|
variants={scribbleVariants}
|
|
className="w-[140%] h-[140%] -top-[20%] -left-[20%] text-accent/30 hidden md:block absolute -z-10"
|
|
>
|
|
<Scribble variant="circle" />
|
|
</motion.div>
|
|
</span>
|
|
),
|
|
})}
|
|
</Heading>
|
|
</motion.div>
|
|
<motion.div variants={subtitleVariants}>
|
|
<p className="text-lg md:text-xl text-white/90 leading-relaxed max-w-2xl mb-10 md:mb-12">
|
|
{t('subtitle')}
|
|
</p>
|
|
</motion.div>
|
|
<motion.div
|
|
className="flex flex-col sm:flex-row justify-center md:justify-start gap-4 md:gap-6"
|
|
variants={buttonContainerVariants}
|
|
>
|
|
<motion.div variants={buttonVariants}>
|
|
<Button
|
|
href="/contact"
|
|
variant="accent"
|
|
size="lg"
|
|
className="group w-full sm:w-auto h-14 md:h-16 px-8 md:px-10 text-base md:text-lg"
|
|
onClick={() =>
|
|
trackEvent(AnalyticsEvents.BUTTON_CLICK, {
|
|
label: t('cta'),
|
|
location: 'home_hero_primary',
|
|
})
|
|
}
|
|
>
|
|
{t('cta')}
|
|
<span className="transition-transform group-hover/btn:translate-x-1">→</span>
|
|
</Button>
|
|
</motion.div>
|
|
<motion.div variants={buttonVariants}>
|
|
<Button
|
|
href={`/${locale}/${locale === 'de' ? 'produkte' : 'products'}`}
|
|
variant="white"
|
|
size="lg"
|
|
className="group w-full sm:w-auto h-14 md:h-16 px-8 md:px-10 text-base md:text-lg md:bg-white md:text-primary md:hover:bg-neutral-light md:border-none"
|
|
onClick={() =>
|
|
trackEvent(AnalyticsEvents.BUTTON_CLICK, {
|
|
label: t('exploreProducts'),
|
|
location: 'home_hero_secondary',
|
|
})
|
|
}
|
|
>
|
|
{t('exploreProducts')}
|
|
</Button>
|
|
</motion.div>
|
|
</motion.div>
|
|
</motion.div>
|
|
</Container>
|
|
|
|
<motion.div
|
|
className="relative md:absolute inset-0 z-0 w-full h-[40vh] md:h-full order-1 md:order-none mb-[-80px] md:mb-0 mt-[80px] md:mt-0 overflow-visible pointer-events-none"
|
|
initial={{ opacity: 0, scale: 0.95, filter: 'blur(20px)' }}
|
|
animate={{ opacity: 1, scale: 1, filter: 'blur(0px)' }}
|
|
transition={{ duration: 2.2, ease: 'easeOut', delay: 0.05 }}
|
|
>
|
|
<HeroIllustration />
|
|
</motion.div>
|
|
|
|
<motion.div
|
|
className="absolute bottom-6 md:bottom-10 left-1/2 -translate-x-1/2 hidden sm:block"
|
|
initial={{ opacity: 0, y: 16 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
transition={{ duration: 1, ease: 'easeOut', delay: 3 }}
|
|
>
|
|
<div className="w-6 h-10 border-2 border-white/30 rounded-full flex justify-center p-1">
|
|
<motion.div
|
|
className="w-1 h-2 bg-white rounded-full"
|
|
animate={{ y: [0, -10, 0] }}
|
|
transition={{
|
|
duration: 1.5,
|
|
repeat: Infinity,
|
|
ease: 'easeInOut',
|
|
}}
|
|
/>
|
|
</div>
|
|
</motion.div>
|
|
</Section>
|
|
);
|
|
}
|
|
|
|
const containerVariants = {
|
|
hidden: { opacity: 1 },
|
|
visible: {
|
|
opacity: 1,
|
|
transition: {
|
|
staggerChildren: 0.12,
|
|
delayChildren: 0.4,
|
|
},
|
|
},
|
|
} as const;
|
|
|
|
const headingVariants = {
|
|
hidden: { opacity: 0, y: 60, scale: 0.85 },
|
|
visible: {
|
|
opacity: 1,
|
|
y: 0,
|
|
scale: 1,
|
|
transition: { duration: 1.2, ease: [0.25, 0.46, 0.45, 0.94] },
|
|
},
|
|
} as const;
|
|
|
|
const accentVariants = {
|
|
hidden: { opacity: 0, scale: 0.9, rotate: -5 },
|
|
visible: {
|
|
opacity: 1,
|
|
scale: 1,
|
|
rotate: 0,
|
|
transition: { duration: 0.8, ease: [0.25, 0.46, 0.45, 0.94] },
|
|
},
|
|
} as const;
|
|
|
|
const scribbleVariants = {
|
|
hidden: { opacity: 0, scale: 0, rotate: 180 },
|
|
visible: {
|
|
opacity: 1,
|
|
scale: 1,
|
|
rotate: 0,
|
|
transition: { duration: 1, type: 'spring', stiffness: 300, damping: 20 },
|
|
},
|
|
} as const;
|
|
|
|
const subtitleVariants = {
|
|
hidden: { opacity: 0, y: 40, scale: 0.95 },
|
|
visible: {
|
|
opacity: 1,
|
|
y: 0,
|
|
scale: 1,
|
|
transition: { duration: 1, ease: [0.25, 0.46, 0.45, 0.94] },
|
|
},
|
|
} as const;
|
|
|
|
const buttonContainerVariants = {
|
|
hidden: { opacity: 0 },
|
|
visible: {
|
|
opacity: 1,
|
|
transition: {
|
|
staggerChildren: 0.15,
|
|
delayChildren: 0.4,
|
|
},
|
|
},
|
|
} as const;
|
|
|
|
const buttonVariants = {
|
|
hidden: { opacity: 0, y: 30, scale: 0.9 },
|
|
visible: {
|
|
opacity: 1,
|
|
y: 0,
|
|
scale: 1,
|
|
transition: { type: 'spring', stiffness: 400, damping: 20 },
|
|
},
|
|
} as const;
|