import React, { useState, useEffect, useRef } from 'react'; import { Play, Heart, Music, Mic, Lock, Globe, Ghost, Star, Headphones, MessageCircle, ChevronRight, X, ArrowRight, Check, Mail } from 'lucide-react'; // --- Custom Hooks --- const useScrollReveal = (threshold = 0.1) => { const [isVisible, setIsVisible] = useState(false); const domRef = useRef(); useEffect(() => { const observer = new IntersectionObserver(entries => { entries.forEach(entry => setIsVisible(entry.isIntersecting)); }, { threshold }); const currentRef = domRef.current; if (currentRef) observer.observe(currentRef); return () => { if (currentRef) observer.unobserve(currentRef); }; }, [threshold]); return [domRef, isVisible]; }; // --- Sub-Components --- const FadeInSection = ({ children, delay = 0, className = "" }) => { const [ref, isVisible] = useScrollReveal(0.15); return (
{children}
); }; const CustomCursor = () => { const [position, setPosition] = useState({ x: 0, y: 0 }); const [isHovering, setIsHovering] = useState(false); useEffect(() => { const updatePosition = (e) => { setPosition({ x: e.clientX, y: e.clientY }); }; const handleMouseOver = (e) => { if (e.target.tagName === 'BUTTON' || e.target.tagName === 'A' || e.target.closest('.interactive')) { setIsHovering(true); } else { setIsHovering(false); } }; window.addEventListener('mousemove', updatePosition); document.addEventListener('mouseover', handleMouseOver); return () => { window.removeEventListener('mousemove', updatePosition); document.removeEventListener('mouseover', handleMouseOver); }; }, []); return (
); }; // --- Sections --- const Navbar = () => { const [scrolled, setScrolled] = useState(false); useEffect(() => { const handleScroll = () => setScrolled(window.scrollY > 50); window.addEventListener('scroll', handleScroll); return () => window.removeEventListener('scroll', handleScroll); }, []); return ( ); }; const HeroCanvas = () => { const canvasRef = useRef(null); useEffect(() => { const canvas = canvasRef.current; const ctx = canvas.getContext('2d'); let animationFrameId; let particles = []; const resize = () => { canvas.width = window.innerWidth; canvas.height = window.innerHeight; }; window.addEventListener('resize', resize); resize(); // Particle Class class Particle { constructor() { this.x = Math.random() * canvas.width; this.y = Math.random() * canvas.height; this.vx = (Math.random() - 0.5) * 0.3; this.vy = (Math.random() - 0.5) * 0.3; this.size = Math.random() * 2; this.color = Math.random() > 0.5 ? 'rgba(232, 180, 184, ' : 'rgba(74, 25, 66, '; // Rose Gold or Purple } update() { this.x += this.vx; this.y += this.vy; if (this.x < 0 || this.x > canvas.width) this.vx *= -1; if (this.y < 0 || this.y > canvas.height) this.vy *= -1; } draw() { ctx.beginPath(); ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2); ctx.fillStyle = this.color + '0.5)'; ctx.fill(); } } // Init Particles for (let i = 0; i < 60; i++) particles.push(new Particle()); const animate = () => { ctx.clearRect(0, 0, canvas.width, canvas.height); // Draw Connections particles.forEach((p, index) => { p.update(); p.draw(); for (let j = index + 1; j < particles.length; j++) { const p2 = particles[j]; const dx = p.x - p2.x; const dy = p.y - p2.y; const distance = Math.sqrt(dx * dx + dy * dy); if (distance < 150) { ctx.beginPath(); ctx.strokeStyle = `rgba(232, 180, 184, ${0.15 - distance/1000})`; ctx.lineWidth = 0.5; ctx.moveTo(p.x, p.y); ctx.lineTo(p2.x, p2.y); ctx.stroke(); } } }); animationFrameId = requestAnimationFrame(animate); }; animate(); return () => { window.removeEventListener('resize', resize); cancelAnimationFrame(animationFrameId); }; }, []); return ; }; const Hero = () => { return (
{/* Background Aurora Effect */}

What if discovering music
felt like falling in love?

One song from your heart. One song to your soul.
Matched with a stranger who becomes a story.

{/* Floating Scroll Indicator */}
); }; const HowItWorks = () => { const steps = [ { icon: , title: "Share Your Song", desc: "Choose one song that moves you. Add a note about why it matters.", }, { icon: , title: "The Blind Match", desc: "We pair you with someone, somewhere. No profiles. Just serendipity.", }, { icon: , title: "Listen & React", desc: "Experience their choice. Rate it. Share your thoughts or a voice note.", }, { icon: , title: "The Reveal", desc: "Both reacted? Now meet. Chat. Maybe become friends.", } ]; return (
The Journey

From Strangers to Soulmates

{/* Connector Line */}
{steps.map((step, idx) => (
{/* Card */}
{step.icon}

{step.title}

{step.desc}

))}
); }; const WhyReciprocal = () => { return (

Because music discovery shouldn't feel like homework.

No endless scrolling.

No impersonal playlists.

No "users who liked X also liked Y."

Just two people. Two songs.
One moment of genuine connection.

{/* Abstract Phone UI Representation */}
{/* Screen Content */}

"This track reminds me of rainy Sundays..."

{/* Second Floating Card */}
); }; const Testimonials = () => { const reviews = [ { text: "I matched with someone in Tokyo who sent me a City Pop song I'd never heard. Now we exchange music weekly.", author: "Sarah, Brooklyn", bg: "bg-[#4A1942]" }, { text: "Tinder for music nerds, except it actually works and feels... intimate? I found my music soulmate.", author: "Marcus, London", bg: "bg-[#0A1128]" }, { text: "My match sent me a song her grandmother used to sing. We both cried. Strangers can understand you best.", author: "Yuki, Kyoto", bg: "bg-[#1a1f4d]" } ]; return (

Early Stories

{reviews.map((r, i) => (

"{r.text}"

{r.author[0]}
{r.author}
))}
); }; const Features = () => { const features = [ { icon: , title: "One Match Daily", desc: "Anticipation over abundance. Quality over quantity." }, { icon: , title: "Voice Reactions", desc: "Say it with your voice. Let them hear your excitement.", premium: true }, { icon: , title: "No Ghosting", desc: "48-hour window. Both must listen. Mutual respect built-in." }, { icon: , title: "Private & Safe", desc: "Anonymous until both react. Report tools. Zero tolerance." }, { icon: , title: "Premium Matching", desc: "Taste-based pairing for deeper connections.", premium: true }, { icon: , title: "Cross-Platform", desc: "Share from Spotify, Apple, YouTube. We store links, not files." }, ]; return (
{features.map((f, i) => (
{React.cloneElement(f.icon, { className: "w-6 h-6" })}
{f.premium && ( Plus )}

{f.title}

{f.desc}

))}
); }; const Pricing = () => { const [annual, setAnnual] = useState(false); return (

Choose Your Rhythm

Monthly Yearly (Save 20%)
{/* Free Tier */}

The Romantic

$0

  • 1 match per day
  • Text reactions
  • 3 active chats
{/* Premium Tier */}
Most Popular

The Enthusiast

${annual ? '3.99' : '4.99'}/mo

  • Unlimited matches
  • Voice reactions
  • Smart matching
  • Ad-free experience
{/* Premium+ Tier */}

The Curator

${annual ? '7.99' : '9.99'}

  • All Premium features
  • Group exchanges
  • Priority support
); }; const TrustTicker = () => { return (
🔒 GDPR Compliant ✅ WCAG AA Accessible 💳 Secure Payments via Stripe 🌍 Available in 120+ Countries 🎵 Spotify & Apple Music Compatible {/* Duplicate for infinite loop */} 🔒 GDPR Compliant ✅ WCAG AA Accessible 💳 Secure Payments via Stripe 🌍 Available in 120+ Countries 🎵 Spotify & Apple Music Compatible
); }; const FinalCTA = () => { const [email, setEmail] = useState(""); const [submitted, setSubmitted] = useState(false); const handleSubmit = (e) => { e.preventDefault(); if(email) { setSubmitted(true); setTimeout(() => setSubmitted(false), 3000); } }; return (
{/* Background Particles */}
{[...Array(20)].map((_,i) => (
))}

Ready to fall in love
with music again?

Join 10,000+ early adopters trading songs and stories.

setEmail(e.target.value)} className="px-6 py-4 rounded-full bg-white/10 backdrop-blur-md border border-white/20 text-white placeholder-white/50 focus:outline-none focus:border-[#FF6B35] flex-grow transition-all interactive" />

🎁 First 1,000 signups get Premium free for 3 months

); }; const Footer = () => { return (

Reciprocal.

Made with 💝 for music lovers who believe in human connection.

Product
  • How it Works
  • Pricing
  • FAQ
Legal
  • Privacy
  • Terms
Newsletter
© 2024 Reciprocal. Music brings us together.
); }; // --- Main App --- export default function App() { return (
); }