diff --git a/apps/landing/src/app/homepage/_assets/1.jpg b/apps/landing/src/app/homepage/_assets/1.jpg new file mode 100644 index 0000000..0b00670 Binary files /dev/null and b/apps/landing/src/app/homepage/_assets/1.jpg differ diff --git a/apps/landing/src/app/homepage/_assets/2.jpg b/apps/landing/src/app/homepage/_assets/2.jpg new file mode 100644 index 0000000..cf6f5fb Binary files /dev/null and b/apps/landing/src/app/homepage/_assets/2.jpg differ diff --git a/apps/landing/src/app/homepage/_assets/3.jpg b/apps/landing/src/app/homepage/_assets/3.jpg new file mode 100644 index 0000000..f6f850f Binary files /dev/null and b/apps/landing/src/app/homepage/_assets/3.jpg differ diff --git a/apps/landing/src/app/homepage/_assets/4.jpg b/apps/landing/src/app/homepage/_assets/4.jpg new file mode 100644 index 0000000..b2d3474 Binary files /dev/null and b/apps/landing/src/app/homepage/_assets/4.jpg differ diff --git a/apps/landing/src/app/homepage/_assets/5.jpg b/apps/landing/src/app/homepage/_assets/5.jpg new file mode 100644 index 0000000..f28fb9e Binary files /dev/null and b/apps/landing/src/app/homepage/_assets/5.jpg differ diff --git a/apps/landing/src/app/homepage/_assets/6.jpg b/apps/landing/src/app/homepage/_assets/6.jpg new file mode 100644 index 0000000..3cede12 Binary files /dev/null and b/apps/landing/src/app/homepage/_assets/6.jpg differ diff --git a/apps/landing/src/app/homepage/_assets/7.jpg b/apps/landing/src/app/homepage/_assets/7.jpg new file mode 100644 index 0000000..4fe767e Binary files /dev/null and b/apps/landing/src/app/homepage/_assets/7.jpg differ diff --git a/apps/landing/src/app/homepage/_assets/8.jpg b/apps/landing/src/app/homepage/_assets/8.jpg new file mode 100644 index 0000000..4e9e0be Binary files /dev/null and b/apps/landing/src/app/homepage/_assets/8.jpg differ diff --git a/apps/landing/src/app/homepage/_assets/9.jpg b/apps/landing/src/app/homepage/_assets/9.jpg new file mode 100644 index 0000000..144acfd Binary files /dev/null and b/apps/landing/src/app/homepage/_assets/9.jpg differ diff --git a/apps/landing/src/app/homepage/_components/HeroSection.tsx b/apps/landing/src/app/homepage/_components/HeroSection.tsx index 79fce6e..7aa92bc 100644 --- a/apps/landing/src/app/homepage/_components/HeroSection.tsx +++ b/apps/landing/src/app/homepage/_components/HeroSection.tsx @@ -3,6 +3,7 @@ import { useState } from 'react'; import { Zap, Globe, FlaskConical, AtSign, Link } from 'lucide-react'; import GlowEffect from './GlowEffect'; +import WaitlistPopup from './WaitlistPopup'; export const styles = ` .gradient-text { @@ -45,16 +46,25 @@ const badges = [ export function HeroSection() { const [email, setEmail] = useState(''); + const [showPopup, setShowPopup] = useState(false); const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); console.log('Email submitted:', email); + setShowPopup(true); + }; + + const handleWaitlistSubmit = async (data: { selected: string[]; other: string }) => { + await fetch('/api/brevo', { + method: 'POST', + body: JSON.stringify({ email, useCases: data.selected, other: data.other }), + }); }; return (
-
+
In Active Development
@@ -113,6 +123,11 @@ export function HeroSection() { ))}
+ setShowPopup(false)} + onSubmit={handleWaitlistSubmit} + />
); } diff --git a/apps/landing/src/app/homepage/_components/WaitlistPopup.tsx b/apps/landing/src/app/homepage/_components/WaitlistPopup.tsx new file mode 100644 index 0000000..2c43236 --- /dev/null +++ b/apps/landing/src/app/homepage/_components/WaitlistPopup.tsx @@ -0,0 +1,318 @@ +import { useState, useEffect, useCallback } from 'react'; +import Image from 'next/image'; +import { + X, + Check, + Globe, + ShoppingCart, + Gamepad2, + Smartphone, + Sparkles, + Palette, + PenTool, + Blocks, + Compass, +} from 'lucide-react'; + +import webDevImg from '../_assets/1.jpg'; +import ecommerceImg from '../_assets/2.jpg'; +import gameDevImg from '../_assets/3.jpg'; +import mobileDevImg from '../_assets/4.jpg'; +import vibeCodingImg from '../_assets/5.jpg'; +import aiArtImg from '../_assets/6.jpg'; +import contentImg from '../_assets/7.jpg'; +import nocodeImg from '../_assets/8.jpg'; +import justBrowsingImg from '../_assets/9.jpg'; + +interface WaitlistPopupProps { + isOpen: boolean; + onClose: () => void; + onSubmit: (data: { selected: string[]; other: string }) => void; +} + +const USE_CASES = [ + { id: 'web-dev', label: 'Web Dev', icon: Globe, image: webDevImg }, + { id: 'ecommerce', label: 'E-commerce', icon: ShoppingCart, image: ecommerceImg }, + { id: 'game-dev', label: 'Game Dev', icon: Gamepad2, image: gameDevImg }, + { id: 'mobile-dev', label: 'Mobile Dev', icon: Smartphone, image: mobileDevImg }, + { id: 'vibecoding', label: 'Vibecoding', icon: Sparkles, image: vibeCodingImg }, + { id: 'ai-art', label: 'AI Art', icon: Palette, image: aiArtImg }, + { id: 'content', label: 'Content', icon: PenTool, image: contentImg }, + { id: 'nocode', label: 'Nocode', icon: Blocks, image: nocodeImg }, + { id: 'just-browsing', label: 'Just came across', icon: Compass, image: justBrowsingImg, dashed: true }, +]; + +export default function WaitlistPopup({ isOpen, onClose, onSubmit }: WaitlistPopupProps) { + const [selected, setSelected] = useState([]); + const [other, setOther] = useState(''); + + useEffect(() => { + const handleEscape = (e: KeyboardEvent) => { + if (e.key === 'Escape' && isOpen) { + onClose(); + } + }; + + document.addEventListener('keydown', handleEscape); + return () => document.removeEventListener('keydown', handleEscape); + }, [isOpen, onClose]); + + useEffect(() => { + if (isOpen) { + document.body.style.overflow = 'hidden'; + } else { + document.body.style.overflow = ''; + } + return () => { + document.body.style.overflow = ''; + }; + }, [isOpen]); + + const toggleSelection = useCallback((id: string) => { + setSelected((prev) => (prev.includes(id) ? prev.filter((item) => item !== id) : [...prev, id])); + }, []); + + const handleSubmit = () => { + onSubmit({ selected, other }); + setSelected([]); + setOther(''); + onClose(); + }; + + const handleBackdropClick = (e: React.MouseEvent) => { + if (e.target === e.currentTarget) { + onClose(); + } + }; + + if (!isOpen) return null; + + return ( + <> + + + {/* Backdrop */} +
+ {/* Popup - wide horizontal layout */} +
+ {/* Close button */} + + + {/* Header row: checkmark + title inline */} +
+
+
+ +
+
+

Sweet! You'll hear from us soon

+
+ + {/* Divider */} +
+ + {/* Question */} +

+ Quick one: what brings you here? +

+ + {/* Cards Grid - 3 rows of 3, stretched horizontally */} +
+ {USE_CASES.map(({ id, label, icon: Icon, image, dashed }) => ( + + ))} +
+ + {/* Other input */} +
+
+ Something cooler: + setOther(e.target.value)} + className="waitlist-other-input flex-1 rounded-lg px-4 py-2.5 text-white text-sm placeholder:text-white/20" + placeholder="" + /> +
+
+ + {/* Submit button */} + +
+
+ + ); +}