feat: Add CHORUS teaser website with mobile-responsive design
- Created complete Next.js 15 teaser website with CHORUS brand styling - Implemented mobile-responsive 3D logo (128px mobile, 512px desktop) - Added proper Exo font loading via Next.js Google Fonts for iOS/Chrome compatibility - Built comprehensive early access form with GDPR compliance and rate limiting - Integrated PostgreSQL database with complete schema for lead capture - Added scroll indicators that auto-hide when scrolling begins - Optimized mobile modal forms with proper scrolling and submit button access - Deployed via Docker Swarm with Traefik SSL termination at chorus.services - Includes database migrations, consent tracking, and email notifications 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
68
modules/teaser/components/TeaserHero.tsx
Normal file
68
modules/teaser/components/TeaserHero.tsx
Normal file
@@ -0,0 +1,68 @@
|
||||
'use client'
|
||||
|
||||
import { useEffect, useState } from 'react'
|
||||
import { LeadSourceType } from '../hooks/useEarlyAccessCapture'
|
||||
import ThreeLogo from './ThreeLogo'
|
||||
|
||||
interface TeaserHeroProps {
|
||||
onEarlyAccess: (leadSource: LeadSourceType) => void
|
||||
}
|
||||
|
||||
export default function TeaserHero({ onEarlyAccess }: TeaserHeroProps) {
|
||||
const [showScrollIndicator, setShowScrollIndicator] = useState(true)
|
||||
|
||||
useEffect(() => {
|
||||
const handleScroll = () => {
|
||||
setShowScrollIndicator(window.scrollY < 50)
|
||||
}
|
||||
|
||||
window.addEventListener('scroll', handleScroll)
|
||||
return () => window.removeEventListener('scroll', handleScroll)
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<section className="min-h-screen flex flex-col items-center justify-center px-chorus-lg py-chorus-xxl bg-gradient-to-b from-white via-sand-100 to-sand-200 dark:from-carbon-950 dark:via-mulberry-950 dark:to-carbon-950 text-center relative">
|
||||
{/* CHORUS 3D Logo - Responsive sizing */}
|
||||
<div className="animate-fade-in">
|
||||
<div className="w-32 h-32 md:w-96 md:h-96 lg:w-[512px] lg:h-[512px]">
|
||||
<ThreeLogo className="mx-auto drop-shadow-2xl w-full h-full" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* CHORUS Title */}
|
||||
<h1 className="text-h1 font-logo font-thin text-carbon-950 dark:text-white mb-chorus-md animate-slide-up" style={{ animationDelay: '0.3s' }}>
|
||||
CHORUS
|
||||
</h1>
|
||||
|
||||
{/* Tagline */}
|
||||
<h2 className="text-xl md:text-2xl text-carbon-700 dark:text-mulberry-100 mb-chorus-xxl max-w-2xl font-light leading-relaxed animate-fade-in-up" style={{ animationDelay: '0.6s' }}>
|
||||
The right context,<br/>to the right agent,<br/>at the right time.
|
||||
</h2>
|
||||
|
||||
{/* CTA Buttons */}
|
||||
<div className="flex gap-chorus-md flex-wrap justify-center animate-fade-in-up" style={{ animationDelay: '0.9s' }}>
|
||||
<button
|
||||
onClick={() => onEarlyAccess('request_early_access')}
|
||||
className="btn-primary text-lg px-chorus-xl py-chorus-md"
|
||||
>
|
||||
Request Early Access
|
||||
</button>
|
||||
<button
|
||||
onClick={() => onEarlyAccess('early_access_waitlist')}
|
||||
className="btn-secondary text-lg px-chorus-xl py-chorus-md"
|
||||
>
|
||||
Join Waitlist
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Subtle scroll indicator */}
|
||||
{showScrollIndicator && (
|
||||
<div className="absolute bottom-chorus-lg left-1/2 -translate-x-1/2 animate-bounce transition-opacity duration-300">
|
||||
<div className="w-6 h-10 border-2 border-carbon-600 dark:border-mulberry-400 rounded-full flex justify-center items-start">
|
||||
<div className="w-1 h-3 bg-carbon-600 dark:bg-mulberry-400 rounded-full mt-2 animate-pulse"></div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user