This commit is contained in:
Jamie Brunton committed 2025-12-30 20:48:14 +00:00
1 parent edcc5a6670
commit 1b3f1d9409
45 files changed
+14500 -185

No files matched your search

+60
View File
@@ -0,0 +1,60 @@
'use client'
import Image from 'next/image'
import { motion } from 'framer-motion'
export default function CTA() {
return (
<section className="relative py-24 overflow-hidden">
{/* Background */}
<div className="absolute inset-0 bg-gradient-to-b from-background to-background-light" />
{/* Accent Glows */}
<div className="absolute top-0 left-1/4 w-[500px] h-[500px] bg-primary/20 rounded-full blur-[150px]" />
<div className="absolute bottom-0 right-1/4 w-[400px] h-[400px] bg-secondary/20 rounded-full blur-[150px]" />
<div className="relative z-10 max-w-4xl mx-auto px-6 text-center">
<motion.div
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
>
<div className="inline-flex items-center justify-center mb-8">
<Image
src="/logo.png"
alt="Sauci"
width={100}
height={100}
className="w-24 h-24 drop-shadow-[0_0_30px_rgba(233,69,96,0.5)]"
/>
</div>
<h2 className="text-3xl sm:text-4xl lg:text-5xl font-bold mb-6">
Ready to Discover What You{' '}
<span className="gradient-text">Both Desire?</span>
</h2>
<p className="text-lg text-white/60 max-w-2xl mx-auto mb-8">
Join thousands of couples who have deepened their connection with Sauci.
Download now and start exploring together.
</p>
<div className="flex flex-col sm:flex-row items-center justify-center gap-4">
<a
href="https://apps.apple.com/app/sauci"
target="_blank"
rel="noopener noreferrer"
className="btn-primary flex items-center gap-2"
>
<svg viewBox="0 0 24 24" className="w-5 h-5" fill="currentColor">
<path d="M18.71 19.5c-.83 1.24-1.71 2.45-3.05 2.47-1.34.03-1.77-.79-3.29-.79-1.53 0-2 .77-3.27.82-1.31.05-2.3-1.32-3.14-2.53C4.25 17 2.94 12.45 4.7 9.39c.87-1.52 2.43-2.48 4.12-2.51 1.28-.02 2.5.87 3.29.87.78 0 2.26-1.07 3.81-.91.65.03 2.47.26 3.64 1.98-.09.06-2.17 1.28-2.15 3.81.03 3.02 2.65 4.03 2.68 4.04-.03.07-.42 1.44-1.38 2.83M13 3.5c.73-.83 1.94-1.46 2.94-1.5.13 1.17-.34 2.35-1.04 3.19-.69.85-1.83 1.51-2.95 1.42-.15-1.15.41-2.35 1.05-3.11z"/>
</svg>
Download for iOS
</a>
<span className="text-white/40">Coming soon to Android</span>
</div>
</motion.div>
</div>
</section>
)
}
+94
View File
@@ -0,0 +1,94 @@
'use client'
import { motion } from 'framer-motion'
import { Heart, MessageCircle, Lock, Sparkles, Users, Flame } from 'lucide-react'
const features = [
{
icon: Heart,
title: 'Swipe to Connect',
description: 'Answer intimate questions with simple swipes. Yes, no, or maybe - your desires stay private until you match.',
color: 'text-primary',
bgColor: 'bg-primary/10',
},
{
icon: Sparkles,
title: 'Discover Matches',
description: 'When both partners swipe positively on a question, a match is created. Discover what you both secretly want.',
color: 'text-secondary',
bgColor: 'bg-secondary/10',
},
{
icon: MessageCircle,
title: 'Unlock Conversations',
description: 'Each match opens a private chat thread. Talk about your desires in a safe, dedicated space.',
color: 'text-green-400',
bgColor: 'bg-green-400/10',
},
{
icon: Lock,
title: 'Complete Privacy',
description: 'Your individual answers are never revealed. Only mutual matches are shown to both partners.',
color: 'text-blue-400',
bgColor: 'bg-blue-400/10',
},
{
icon: Flame,
title: 'Curated Question Packs',
description: 'Explore themed packs from playful to passionate. New packs added regularly to keep things exciting.',
color: 'text-orange-400',
bgColor: 'bg-orange-400/10',
},
{
icon: Users,
title: 'Built for Couples',
description: 'Designed exclusively for partners. Connect with your significant other using a private invite code.',
color: 'text-pink-400',
bgColor: 'bg-pink-400/10',
},
]
export default function Features() {
return (
<section id="features" className="relative py-24 overflow-hidden">
{/* Background */}
<div className="absolute inset-0 bg-gradient-to-b from-background to-background-light" />
<div className="relative z-10 max-w-7xl mx-auto px-6">
<motion.div
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
className="text-center mb-16"
>
<h2 className="text-3xl sm:text-4xl lg:text-5xl font-bold mb-4">
Everything You Need to{' '}
<span className="gradient-text">Explore Together</span>
</h2>
<p className="text-lg text-white/60 max-w-2xl mx-auto">
Sauci creates a safe space for couples to discover their shared desires without fear of judgment.
</p>
</motion.div>
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-6">
{features.map((feature, index) => (
<motion.div
key={feature.title}
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ delay: index * 0.1 }}
className="glass p-6 hover:border-white/20 transition-all duration-300 group"
>
<div className={`w-14 h-14 rounded-2xl ${feature.bgColor} flex items-center justify-center mb-4 group-hover:scale-110 transition-transform`}>
<feature.icon className={`w-7 h-7 ${feature.color}`} />
</div>
<h3 className="text-xl font-semibold mb-2">{feature.title}</h3>
<p className="text-white/60">{feature.description}</p>
</motion.div>
))}
</div>
</div>
</section>
)
}
+46
View File
@@ -0,0 +1,46 @@
'use client'
import Link from 'next/link'
import Image from 'next/image'
export default function Footer() {
const currentYear = new Date().getFullYear()
return (
<footer className="relative py-12 border-t border-white/5">
<div className="max-w-7xl mx-auto px-6">
<div className="flex flex-col md:flex-row items-center justify-between gap-6">
{/* Logo */}
<Link href="/" className="flex items-center gap-2">
<Image
src="/logo.png"
alt="Sauci"
width={32}
height={32}
className="w-8 h-8"
/>
<span className="text-xl font-bold gradient-text">Sauci</span>
</Link>
{/* Links */}
<div className="flex flex-wrap items-center justify-center gap-6">
<Link href="/privacy" className="text-white/60 hover:text-white transition-colors text-sm">
Privacy Policy
</Link>
<Link href="/terms" className="text-white/60 hover:text-white transition-colors text-sm">
Terms & Conditions
</Link>
<a href="mailto:support@sauci.app" className="text-white/60 hover:text-white transition-colors text-sm">
Contact
</a>
</div>
{/* Copyright */}
<div className="text-white/40 text-sm">
&copy; {currentYear} Sauci. All rights reserved.
</div>
</div>
</div>
</footer>
)
}
+170
View File
@@ -0,0 +1,170 @@
'use client'
import Image from 'next/image'
import { motion } from 'framer-motion'
import { Sparkles } from 'lucide-react'
export default function Hero() {
return (
<section className="relative min-h-screen flex items-center justify-center overflow-hidden pt-20">
{/* Background Effects */}
<div className="absolute inset-0 bg-gradient-to-b from-background-light to-background" />
{/* Accent Glows */}
<div className="absolute top-20 right-0 w-[600px] h-[600px] bg-primary/20 rounded-full blur-[150px] animate-pulse-glow" />
<div className="absolute bottom-0 left-0 w-[500px] h-[500px] bg-secondary/20 rounded-full blur-[150px] animate-pulse-glow" style={{ animationDelay: '2s' }} />
<div className="relative z-10 max-w-7xl mx-auto px-6 py-20 flex flex-col lg:flex-row items-center gap-12 lg:gap-16">
{/* Text Content */}
<motion.div
initial={{ opacity: 0, x: -50 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.8 }}
className="flex-1 text-center lg:text-left"
>
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.2 }}
className="inline-flex items-center gap-2 glass px-4 py-2 mb-6"
>
<Sparkles className="w-4 h-4 text-primary" />
<span className="text-sm text-white/80">Discover What You Both Desire</span>
</motion.div>
<h1 className="text-4xl sm:text-5xl lg:text-6xl xl:text-7xl font-bold leading-tight mb-6">
Ignite Your{' '}
<span className="gradient-text">Connection</span>
</h1>
<p className="text-lg sm:text-xl text-white/60 max-w-xl mx-auto lg:mx-0 mb-8">
Sauci helps couples explore their desires through playful questions.
Swipe, match, and unlock deeper conversations with your partner.
</p>
<div className="flex flex-col sm:flex-row items-center justify-center lg:justify-start gap-4">
<a
href="https://apps.apple.com/app/sauci"
target="_blank"
rel="noopener noreferrer"
className="btn-primary flex items-center gap-2 w-full sm:w-auto justify-center"
>
<svg viewBox="0 0 24 24" className="w-5 h-5" fill="currentColor">
<path d="M18.71 19.5c-.83 1.24-1.71 2.45-3.05 2.47-1.34.03-1.77-.79-3.29-.79-1.53 0-2 .77-3.27.82-1.31.05-2.3-1.32-3.14-2.53C4.25 17 2.94 12.45 4.7 9.39c.87-1.52 2.43-2.48 4.12-2.51 1.28-.02 2.5.87 3.29.87.78 0 2.26-1.07 3.81-.91.65.03 2.47.26 3.64 1.98-.09.06-2.17 1.28-2.15 3.81.03 3.02 2.65 4.03 2.68 4.04-.03.07-.42 1.44-1.38 2.83M13 3.5c.73-.83 1.94-1.46 2.94-1.5.13 1.17-.34 2.35-1.04 3.19-.69.85-1.83 1.51-2.95 1.42-.15-1.15.41-2.35 1.05-3.11z"/>
</svg>
Download for iOS
</a>
<a href="#how-it-works" className="btn-secondary flex items-center gap-2 w-full sm:w-auto justify-center">
Learn More
</a>
</div>
</motion.div>
{/* Phone Mockups */}
<motion.div
initial={{ opacity: 0, x: 50 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.8, delay: 0.3 }}
className="flex-1 flex justify-center items-center"
>
<div className="relative flex items-center justify-center">
{/* Glow behind phones */}
<div className="absolute w-[400px] h-[500px] bg-gradient-primary rounded-full blur-[100px] opacity-20" />
{/* Back Phone - Packs */}
<motion.div
initial={{ opacity: 0, x: 60, rotate: 12 }}
animate={{ opacity: 1, x: 60, rotate: 12 }}
transition={{ duration: 0.8, delay: 0.6 }}
className="absolute -right-4 sm:right-0"
>
<PhoneMockup
screenshot="/screenshots/packs.png"
alt="Question Packs"
className="w-[180px] sm:w-[220px] opacity-60"
/>
</motion.div>
{/* Back Phone - Swipe */}
<motion.div
initial={{ opacity: 0, x: -60, rotate: -12 }}
animate={{ opacity: 1, x: -60, rotate: -12 }}
transition={{ duration: 0.8, delay: 0.5 }}
className="absolute -left-4 sm:left-0"
>
<PhoneMockup
screenshot="/screenshots/swipe.png"
alt="Swipe on Questions"
className="w-[180px] sm:w-[220px] opacity-60"
/>
</motion.div>
{/* Front Phone - Home */}
<motion.div
initial={{ opacity: 0, y: 30 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.8, delay: 0.4 }}
className="relative z-10 animate-float"
>
<PhoneMockup
screenshot="/screenshots/home.png"
alt="Sauci Home Screen"
className="w-[220px] sm:w-[280px]"
featured
/>
</motion.div>
</div>
</motion.div>
</div>
{/* Scroll Indicator */}
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 1.5 }}
className="absolute bottom-8 left-1/2 -translate-x-1/2"
>
<div className="w-6 h-10 rounded-full border-2 border-white/20 flex justify-center pt-2">
<motion.div
animate={{ y: [0, 8, 0] }}
transition={{ repeat: Infinity, duration: 1.5 }}
className="w-1.5 h-1.5 rounded-full bg-white/60"
/>
</div>
</motion.div>
</section>
)
}
function PhoneMockup({
screenshot,
alt,
className = '',
featured = false,
}: {
screenshot: string
alt: string
className?: string
featured?: boolean
}) {
return (
<div className={`relative ${className}`}>
{/* Phone Frame */}
<div className={`relative bg-black rounded-[2.5rem] p-2 shadow-2xl ${featured ? 'glow-primary' : ''}`}>
{/* Screen */}
<div className="relative rounded-[2rem] overflow-hidden bg-background">
<Image
src={screenshot}
alt={alt}
width={430}
height={932}
className="w-full h-auto"
priority={featured}
/>
</div>
{/* Dynamic Island / Notch */}
<div className="absolute top-3 left-1/2 -translate-x-1/2 w-[30%] h-[3%] bg-black rounded-full" />
</div>
</div>
)
}
+80
View File
@@ -0,0 +1,80 @@
'use client'
import { motion } from 'framer-motion'
const steps = [
{
number: '01',
title: 'Connect with Your Partner',
description: 'Create your account and invite your partner using a unique code. Your journey begins together.',
},
{
number: '02',
title: 'Browse Question Packs',
description: 'Choose from curated packs ranging from romantic to adventurous. Each pack contains thoughtful questions.',
},
{
number: '03',
title: 'Swipe on Questions',
description: 'Answer each question honestly. Swipe right for yes, left for no, or up for maybe. Your answers stay private.',
},
{
number: '04',
title: 'Discover Your Matches',
description: 'When both partners answer positively, it\'s a match! Explore your shared desires through private chats.',
},
]
export default function HowItWorks() {
return (
<section id="how-it-works" className="relative py-24 overflow-hidden">
{/* Background */}
<div className="absolute inset-0 bg-gradient-to-b from-background-light to-background" />
{/* Accent Glow */}
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[800px] h-[800px] bg-secondary/10 rounded-full blur-[200px]" />
<div className="relative z-10 max-w-7xl mx-auto px-6">
<motion.div
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
className="text-center mb-16"
>
<h2 className="text-3xl sm:text-4xl lg:text-5xl font-bold mb-4">
How <span className="gradient-text">Sauci</span> Works
</h2>
<p className="text-lg text-white/60 max-w-2xl mx-auto">
Four simple steps to deeper connection and discovery with your partner.
</p>
</motion.div>
<div className="grid md:grid-cols-2 lg:grid-cols-4 gap-8">
{steps.map((step, index) => (
<motion.div
key={step.number}
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ delay: index * 0.15 }}
className="relative"
>
{/* Connector Line */}
{index < steps.length - 1 && (
<div className="hidden lg:block absolute top-12 left-[60%] w-full h-0.5 bg-gradient-to-r from-primary/50 to-transparent" />
)}
<div className="glass p-6 h-full">
<div className="text-5xl font-bold gradient-text mb-4 opacity-50">
{step.number}
</div>
<h3 className="text-xl font-semibold mb-2">{step.title}</h3>
<p className="text-white/60">{step.description}</p>
</div>
</motion.div>
))}
</div>
</div>
</section>
)
}
+103
View File
@@ -0,0 +1,103 @@
'use client'
import { useState, useEffect } from 'react'
import Link from 'next/link'
import Image from 'next/image'
import { motion } from 'framer-motion'
import { Menu, X } from 'lucide-react'
export default function Navbar() {
const [isScrolled, setIsScrolled] = useState(false)
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false)
useEffect(() => {
const handleScroll = () => {
setIsScrolled(window.scrollY > 20)
}
window.addEventListener('scroll', handleScroll)
return () => window.removeEventListener('scroll', handleScroll)
}, [])
return (
<motion.nav
initial={{ y: -100 }}
animate={{ y: 0 }}
className={`fixed top-0 left-0 right-0 z-50 transition-all duration-300 ${
isScrolled ? 'glass py-4' : 'py-6'
}`}
>
<div className="max-w-7xl mx-auto px-6 flex items-center justify-between">
<Link href="/" className="flex items-center gap-3">
<Image
src="/logo.png"
alt="Sauci"
width={40}
height={40}
className="w-10 h-10"
/>
<span className="text-2xl font-bold gradient-text">Sauci</span>
</Link>
{/* Desktop Navigation */}
<div className="hidden md:flex items-center gap-8">
<a href="#features" className="text-white/70 hover:text-white transition-colors">
Features
</a>
<a href="#how-it-works" className="text-white/70 hover:text-white transition-colors">
How It Works
</a>
<a
href="https://apps.apple.com/app/sauci"
target="_blank"
rel="noopener noreferrer"
className="btn-primary text-sm py-3 px-6"
>
Download App
</a>
</div>
{/* Mobile Menu Button */}
<button
className="md:hidden text-white"
onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)}
>
{isMobileMenuOpen ? <X size={24} /> : <Menu size={24} />}
</button>
</div>
{/* Mobile Menu */}
{isMobileMenuOpen && (
<motion.div
initial={{ opacity: 0, y: -20 }}
animate={{ opacity: 1, y: 0 }}
className="md:hidden glass mt-4 mx-6 p-6"
>
<div className="flex flex-col gap-4">
<a
href="#features"
className="text-white/70 hover:text-white transition-colors"
onClick={() => setIsMobileMenuOpen(false)}
>
Features
</a>
<a
href="#how-it-works"
className="text-white/70 hover:text-white transition-colors"
onClick={() => setIsMobileMenuOpen(false)}
>
How It Works
</a>
<a
href="https://apps.apple.com/app/sauci"
target="_blank"
rel="noopener noreferrer"
className="btn-primary text-sm py-3 px-6 text-center"
>
Download App
</a>
</div>
</motion.div>
)}
</motion.nav>
)
}