Files
chorus-services-website/components/sections/EnhancedHero.tsx
anthonyrawlins f343f89d24 Initial commit: CHORUS Services marketing website
Complete Next.js website with Docker containerization:
- Next.js 14 with TypeScript and Tailwind CSS
- Responsive design with modern UI components
- Hero section, features showcase, testimonials
- FAQ section with comprehensive content
- Contact forms and newsletter signup
- Docker production build with Nginx
- Health checks and monitoring support
- SEO optimization and performance tuning

Ready for integration as git submodule in main CHORUS project.

Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-01 22:45:06 +10:00

498 lines
16 KiB
TypeScript

'use client';
import React, { useEffect, useRef, useState } from 'react';
import { motion, useScroll, useTransform, useSpring, useInView } from 'framer-motion';
import { Typography, Space } from 'antd';
import { PlayIcon, ArrowRightIcon, ChevronDownIcon } from 'lucide-react';
import { PrimaryButton, SecondaryButton } from '@/components/ui/Button';
import { useReducedMotion } from '@/hooks/useReducedMotion';
const { Title, Paragraph } = Typography;
interface ParallaxLayerProps {
children: React.ReactNode;
speed: number;
className?: string;
}
const ParallaxLayer: React.FC<ParallaxLayerProps> = ({ children, speed, className = '' }) => {
const { scrollY } = useScroll();
const prefersReducedMotion = useReducedMotion();
const y = useTransform(scrollY, [0, 1000], [0, prefersReducedMotion ? 0 : speed * 100]);
const smoothY = useSpring(y, { stiffness: 100, damping: 30 });
return (
<motion.div
className={`${className} will-change-transform gpu-accelerated`}
style={{ y: prefersReducedMotion ? 0 : smoothY }}
>
{children}
</motion.div>
);
};
const FloatingElement: React.FC<{ delay: number; children: React.ReactNode }> = ({ delay, children }) => {
const prefersReducedMotion = useReducedMotion();
return (
<motion.div
className="will-change-transform gpu-accelerated"
animate={prefersReducedMotion ? {} : {
y: [-10, 10, -10],
rotate: [-2, 2, -2],
}}
transition={{
duration: prefersReducedMotion ? 0 : 6,
delay: prefersReducedMotion ? 0 : delay,
repeat: prefersReducedMotion ? 0 : Infinity,
ease: "easeInOut",
}}
>
{children}
</motion.div>
);
};
const GeometricPattern: React.FC = () => {
const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 });
const prefersReducedMotion = useReducedMotion();
const heroRef = useRef<HTMLDivElement>(null);
useEffect(() => {
const handleMouseMove = (e: MouseEvent) => {
if (heroRef.current && !prefersReducedMotion) {
const rect = heroRef.current.getBoundingClientRect();
setMousePosition({
x: (e.clientX - rect.left) / rect.width,
y: (e.clientY - rect.top) / rect.height,
});
}
};
const heroElement = heroRef.current;
if (heroElement) {
heroElement.addEventListener('mousemove', handleMouseMove);
return () => {
heroElement.removeEventListener('mousemove', handleMouseMove);
};
}
}, [prefersReducedMotion]);
return (
<div ref={heroRef} className="absolute inset-0 overflow-hidden pointer-events-none" aria-hidden="true">
{/* Enhanced animated gradient background */}
<motion.div
className="absolute inset-0 opacity-30"
animate={prefersReducedMotion ? {} : {
background: [
'radial-gradient(circle at 20% 50%, rgba(0, 122, 255, 0.15) 0%, transparent 50%)',
'radial-gradient(circle at 80% 20%, rgba(48, 209, 88, 0.15) 0%, transparent 50%)',
'radial-gradient(circle at 40% 80%, rgba(0, 122, 255, 0.15) 0%, transparent 50%)',
'radial-gradient(circle at 60% 30%, rgba(48, 209, 88, 0.12) 0%, transparent 60%)',
],
}}
transition={{
duration: prefersReducedMotion ? 0 : 12,
repeat: prefersReducedMotion ? 0 : Infinity,
ease: "easeInOut",
}}
/>
{/* Particle system - responsive dots */}
{[...Array(16)].map((_, i) => (
<motion.div
key={`particle-${i}`}
className="absolute w-1 h-1 bg-white/20 rounded-full"
style={{
left: `${15 + (i * 5.5)}%`,
top: `${25 + Math.sin(i * 0.8) * 25}%`,
}}
animate={prefersReducedMotion ? {} : {
scale: [1, 1.8, 1],
opacity: [0.2, 0.7, 0.2],
x: mousePosition.x * (8 + i * 1.5),
y: mousePosition.y * (4 + i * 0.8),
}}
transition={{
duration: prefersReducedMotion ? 0 : 4 + i * 0.3,
repeat: prefersReducedMotion ? 0 : Infinity,
ease: "easeInOut",
}}
/>
))}
{/* Enhanced geometric shapes - hexagons */}
{[...Array(8)].map((_, i) => (
<motion.div
key={`hex-${i}`}
className="absolute w-6 h-6 border border-white/8"
style={{
left: `${8 + (i * 12)}%`,
top: `${15 + Math.cos(i * 0.6) * 35}%`,
clipPath: 'polygon(30% 0%, 70% 0%, 100% 50%, 70% 100%, 30% 100%, 0% 50%)',
}}
animate={prefersReducedMotion ? {} : {
rotate: [0, 360],
scale: [1, 1.3, 1],
opacity: [0.08, 0.25, 0.08],
}}
transition={{
duration: prefersReducedMotion ? 0 : 10 + i * 2.5,
repeat: prefersReducedMotion ? 0 : Infinity,
ease: "linear",
}}
/>
))}
{/* Grid pattern overlay */}
<motion.div
className="absolute inset-0 opacity-5"
style={{
backgroundImage: `
linear-gradient(rgba(255,255,255,0.1) 1px, transparent 1px),
linear-gradient(90deg, rgba(255,255,255,0.1) 1px, transparent 1px)
`,
backgroundSize: '50px 50px',
}}
animate={prefersReducedMotion ? {} : {
opacity: [0.02, 0.08, 0.02],
}}
transition={{
duration: prefersReducedMotion ? 0 : 8,
repeat: prefersReducedMotion ? 0 : Infinity,
ease: "easeInOut",
}}
/>
{/* Additional floating circles for depth */}
{[...Array(4)].map((_, i) => (
<motion.div
key={`circle-${i}`}
className="absolute rounded-full border border-white/5"
style={{
width: `${60 + i * 40}px`,
height: `${60 + i * 40}px`,
left: `${20 + i * 20}%`,
top: `${10 + i * 15}%`,
}}
animate={prefersReducedMotion ? {} : {
scale: [1, 1.1, 1],
opacity: [0.03, 0.12, 0.03],
rotate: [0, 180, 360],
}}
transition={{
duration: prefersReducedMotion ? 0 : 15 + i * 5,
repeat: prefersReducedMotion ? 0 : Infinity,
ease: "easeInOut",
}}
/>
))}
</div>
);
};
const AnimatedText: React.FC<{ children: React.ReactNode; delay?: number }> = ({ children, delay = 0 }) => {
const ref = useRef<HTMLDivElement>(null);
const isInView = useInView(ref, { once: true });
return (
<motion.div
ref={ref}
initial={{ opacity: 0, y: 50 }}
animate={isInView ? { opacity: 1, y: 0 } : { opacity: 0, y: 50 }}
transition={{
duration: 0.8,
delay,
ease: [0.21, 1.11, 0.81, 0.99], // Apple-style easing
}}
>
{children}
</motion.div>
);
};
const GradientText: React.FC<{ children: React.ReactNode }> = ({ children }) => (
<motion.span
className="text-gradient"
animate={{
backgroundPosition: ['0% 50%', '100% 50%', '0% 50%'],
}}
transition={{
duration: 5,
repeat: Infinity,
ease: "easeInOut",
}}
style={{
backgroundSize: '200% 200%',
}}
>
{children}
</motion.span>
);
const ScrollIndicator: React.FC = () => {
const prefersReducedMotion = useReducedMotion();
return (
<motion.div
className="absolute bottom-8 left-1/2 transform -translate-x-1/2 flex flex-col items-center text-white/60"
initial={{ opacity: 0, y: prefersReducedMotion ? 0 : 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: prefersReducedMotion ? 0 : 2, duration: prefersReducedMotion ? 0.01 : 1 }}
role="button"
tabIndex={0}
aria-label="Scroll down to see more content"
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
window.scrollBy({ top: window.innerHeight, behavior: 'smooth' });
}
}}
onClick={() => {
window.scrollBy({ top: window.innerHeight, behavior: 'smooth' });
}}
className="cursor-pointer focus:outline-none focus:ring-2 focus:ring-chorus-blue focus:ring-opacity-50 rounded-lg p-3"
>
<motion.div
animate={prefersReducedMotion ? {} : { y: [0, 8, 0] }}
transition={{
duration: prefersReducedMotion ? 0 : 2,
repeat: prefersReducedMotion ? 0 : Infinity,
ease: "easeInOut"
}}
>
<ChevronDownIcon size={24} />
</motion.div>
<span className="text-sm mt-2 tracking-wider">SCROLL</span>
</motion.div>
);
};
const EnhancedHero: React.FC = () => {
const containerRef = useRef<HTMLDivElement>(null);
const { scrollY } = useScroll();
const opacity = useTransform(scrollY, [0, 500], [1, 0]);
const scale = useTransform(scrollY, [0, 500], [1, 0.95]);
const prefersReducedMotion = useReducedMotion();
// Staggered animation variants with reduced motion support
const containerVariants = {
hidden: {},
visible: {
transition: {
staggerChildren: prefersReducedMotion ? 0 : 0.2,
delayChildren: prefersReducedMotion ? 0 : 0.3,
},
},
};
const itemVariants = {
hidden: {
opacity: 0,
y: prefersReducedMotion ? 0 : 60,
scale: prefersReducedMotion ? 1 : 0.95,
},
visible: {
opacity: 1,
y: 0,
scale: 1,
transition: {
duration: prefersReducedMotion ? 0.01 : 0.8,
ease: [0.21, 1.11, 0.81, 0.99], // Apple cubic-bezier
},
},
};
const buttonVariants = {
hidden: {
opacity: 0,
y: prefersReducedMotion ? 0 : 30
},
visible: {
opacity: 1,
y: 0,
transition: {
duration: prefersReducedMotion ? 0.01 : 0.6,
ease: [0.21, 1.11, 0.81, 0.99],
},
},
hover: {
scale: prefersReducedMotion ? 1 : 1.05,
transition: {
duration: 0.2,
ease: "easeOut",
},
},
tap: {
scale: prefersReducedMotion ? 1 : 0.98,
transition: {
duration: 0.1,
},
},
};
return (
<section
ref={containerRef}
className="relative min-h-screen flex items-center justify-center overflow-hidden bg-gradient-to-br from-chorus-charcoal via-chorus-charcoal to-chorus-charcoal-dark"
aria-label="CHORUS Services Hero Section"
role="banner"
>
{/* Geometric Background Pattern */}
<GeometricPattern />
{/* Main Content with Parallax */}
<motion.div
className="relative z-10 text-center max-w-6xl mx-auto px-6"
style={{ opacity, scale }}
initial="hidden"
animate="visible"
variants={containerVariants}
>
{/* Background floating elements */}
<ParallaxLayer speed={-0.3} className="absolute -top-20 -left-20">
<FloatingElement delay={0}>
<div className="w-32 h-32 rounded-full border border-white/5 bg-gradient-to-r from-chorus-blue/10 to-chorus-green/10" />
</FloatingElement>
</ParallaxLayer>
<ParallaxLayer speed={-0.5} className="absolute -top-40 -right-32">
<FloatingElement delay={1}>
<div className="w-48 h-48 rounded-full border border-white/5 bg-gradient-to-l from-chorus-green/10 to-chorus-blue/10" />
</FloatingElement>
</ParallaxLayer>
{/* Main Headline */}
<motion.div variants={itemVariants} className="mb-8">
<Title
level={1}
className="text-4xl sm:text-5xl md:text-6xl lg:text-7xl xl:text-8xl font-bold mb-6 text-white leading-tight"
style={{ letterSpacing: '-0.02em' }}
role="heading"
aria-level={1}
>
<GradientText>CHORUS</GradientText> Services
</Title>
</motion.div>
{/* Subtitle */}
<motion.div variants={itemVariants} className="mb-12">
<Paragraph
className="text-lg sm:text-xl md:text-2xl text-gray-300 max-w-4xl mx-auto leading-relaxed"
role="heading"
aria-level={2}
>
Distributed AI Orchestration Without the Hallucinations
</Paragraph>
</motion.div>
{/* CTA Buttons */}
<motion.div variants={itemVariants}>
<Space size="large" className="flex flex-wrap justify-center gap-4">
<motion.div
variants={buttonVariants}
whileHover="hover"
whileTap="tap"
>
<PrimaryButton
size="large"
icon={<PlayIcon size={20} />}
className="text-lg px-8 py-4 h-auto min-w-[180px] shadow-2xl shadow-chorus-blue/25"
aria-label="Explore CHORUS Platform"
>
Explore Platform
</PrimaryButton>
</motion.div>
<motion.div
variants={buttonVariants}
whileHover="hover"
whileTap="tap"
>
<SecondaryButton
size="large"
icon={<ArrowRightIcon size={20} />}
className="text-lg px-8 py-4 h-auto min-w-[180px] backdrop-blur-sm"
aria-label="View CHORUS Documentation"
>
View Documentation
</SecondaryButton>
</motion.div>
</Space>
</motion.div>
{/* Stats or additional info */}
<motion.div
variants={itemVariants}
className="mt-16 grid grid-cols-1 sm:grid-cols-3 gap-8 max-w-2xl mx-auto"
>
{[
{ label: 'Components', value: '4+', color: 'chorus-blue' },
{ label: 'Uptime', value: '99.9%', color: 'chorus-green' },
{ label: 'Performance', value: '< 100ms', color: 'yellow-400' },
].map((stat, index) => (
<motion.div
key={stat.label}
className="text-center"
whileHover={{ scale: 1.05 }}
transition={{ duration: 0.2 }}
>
<motion.div
className={`text-2xl font-bold text-${stat.color} mb-2`}
animate={{
scale: [1, 1.1, 1],
}}
transition={{
duration: 2,
delay: index * 0.5 + 3,
repeat: Infinity,
repeatDelay: 5,
}}
>
{stat.value}
</motion.div>
<div className="text-gray-400 text-sm uppercase tracking-wider">
{stat.label}
</div>
</motion.div>
))}
</motion.div>
</motion.div>
{/* Scroll Indicator */}
<ScrollIndicator />
{/* Ambient lighting effects */}
<div className="absolute inset-0 pointer-events-none" aria-hidden="true">
<motion.div
className="absolute top-1/4 left-1/4 w-96 h-96 bg-chorus-blue/5 rounded-full blur-3xl"
animate={prefersReducedMotion ? {} : {
scale: [1, 1.2, 1],
opacity: [0.3, 0.5, 0.3],
}}
transition={{
duration: prefersReducedMotion ? 0 : 8,
repeat: prefersReducedMotion ? 0 : Infinity,
ease: "easeInOut",
}}
/>
<motion.div
className="absolute bottom-1/4 right-1/4 w-96 h-96 bg-chorus-green/5 rounded-full blur-3xl"
animate={prefersReducedMotion ? {} : {
scale: [1.2, 1, 1.2],
opacity: [0.5, 0.3, 0.5],
}}
transition={{
duration: prefersReducedMotion ? 0 : 8,
repeat: prefersReducedMotion ? 0 : Infinity,
ease: "easeInOut",
delay: prefersReducedMotion ? 0 : 2,
}}
/>
</div>
</section>
);
};
export default EnhancedHero;