gitea +
This commit is contained in:
@@ -36,9 +36,9 @@ export function CompetitorDemoVisual() {
|
||||
<motion.div
|
||||
className="p-4 rounded-xl border border-zinc-200 dark:border-zinc-800 bg-white dark:bg-zinc-950 relative overflow-hidden shadow-xl"
|
||||
animate={{
|
||||
borderColor: phase === 1 ? '#ef4444' : '#27272a',
|
||||
borderColor: phase === 1 ? 'hsl(var(--burgundy))' : '#27272a',
|
||||
boxShadow: phase === 1
|
||||
? '0 0 20px rgba(239, 68, 68, 0.2)'
|
||||
? '0 0 20px hsl(var(--burgundy) / 0.2)'
|
||||
: '0 1px 3px rgba(0,0,0,0.5)'
|
||||
}}
|
||||
transition={{ duration: 0.5 }}
|
||||
@@ -49,7 +49,7 @@ export function CompetitorDemoVisual() {
|
||||
initial={{ x: '-100%', skewX: -20 }}
|
||||
animate={{ x: '200%' }}
|
||||
transition={{ duration: 0.8, ease: 'easeInOut' }}
|
||||
className="absolute inset-0 bg-gradient-to-r from-transparent via-red-500/10 to-transparent"
|
||||
className="absolute inset-0 bg-gradient-to-r from-transparent via-[hsl(var(--burgundy))]/10 to-transparent"
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -67,7 +67,7 @@ export function CompetitorDemoVisual() {
|
||||
className="text-3xl font-bold"
|
||||
animate={{
|
||||
textDecoration: phase === 1 ? 'line-through' : 'none',
|
||||
color: phase === 1 ? '#ef4444' : '#f4f4f5'
|
||||
color: phase === 1 ? 'hsl(var(--burgundy))' : '#f4f4f5'
|
||||
}}
|
||||
>
|
||||
$99
|
||||
@@ -84,14 +84,14 @@ export function CompetitorDemoVisual() {
|
||||
transition={{ delay: 0.1, type: 'spring', stiffness: 300, damping: 20 }}
|
||||
className="flex items-center gap-3 mt-1"
|
||||
>
|
||||
<div className="flex items-center justify-center h-6 w-6 rounded-full bg-red-500/10">
|
||||
<ArrowDown className="h-4 w-4 text-red-500" strokeWidth={3} />
|
||||
<div className="flex items-center justify-center h-6 w-6 rounded-full bg-[hsl(var(--burgundy))]/10">
|
||||
<ArrowDown className="h-4 w-4 text-[hsl(var(--burgundy))]" strokeWidth={3} />
|
||||
</div>
|
||||
<div className="flex items-baseline gap-2">
|
||||
<span className="text-5xl font-extrabold text-[#ff0000] tracking-tight">
|
||||
<span className="text-5xl font-extrabold text-[hsl(var(--burgundy))] tracking-tight">
|
||||
$79
|
||||
</span>
|
||||
<span className="text-sm font-medium text-red-500">/month</span>
|
||||
<span className="text-sm font-medium text-[hsl(var(--burgundy))]">/month</span>
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
@@ -102,9 +102,9 @@ export function CompetitorDemoVisual() {
|
||||
initial={{ opacity: 0, scale: 0.8, rotate: -3 }}
|
||||
animate={{ opacity: 1, scale: 1, rotate: 0 }}
|
||||
transition={{ delay: 0.3, type: 'spring' }}
|
||||
className="mt-2 inline-flex items-center gap-1.5 px-3 py-1 rounded-full bg-red-500/10 border border-red-500/20"
|
||||
className="mt-2 inline-flex items-center gap-1.5 px-3 py-1 rounded-full bg-[hsl(var(--burgundy))]/10 border border-[hsl(var(--burgundy))]/20"
|
||||
>
|
||||
<span className="text-[10px] font-extrabold text-red-500 uppercase tracking-wider">
|
||||
<span className="text-[10px] font-extrabold text-[hsl(var(--burgundy))] uppercase tracking-wider">
|
||||
Save $240/year
|
||||
</span>
|
||||
</motion.div>
|
||||
@@ -119,17 +119,17 @@ export function CompetitorDemoVisual() {
|
||||
initial={{ opacity: 0, y: 10, scale: 0.95 }}
|
||||
animate={{ opacity: 1, y: 0, scale: 1 }}
|
||||
transition={{ delay: 0.6 }}
|
||||
className="flex items-center gap-2 p-2 rounded-lg bg-red-500/10 border border-red-500/30"
|
||||
className="flex items-center gap-2 p-2 rounded-lg bg-[hsl(var(--burgundy))]/10 border border-[hsl(var(--burgundy))]/30"
|
||||
>
|
||||
<div className="relative flex-shrink-0">
|
||||
<Bell className="h-3 w-3 text-red-500" />
|
||||
<Bell className="h-3 w-3 text-[hsl(var(--burgundy))]" />
|
||||
<motion.span
|
||||
animate={{ scale: [1, 1.3, 1] }}
|
||||
transition={{ duration: 1, repeat: Infinity }}
|
||||
className="absolute -top-0.5 -right-0.5 w-1.5 h-1.5 rounded-full bg-red-500"
|
||||
className="absolute -top-0.5 -right-0.5 w-1.5 h-1.5 rounded-full bg-[hsl(var(--burgundy))]"
|
||||
/>
|
||||
</div>
|
||||
<span className="text-[9px] font-semibold text-red-500">
|
||||
<span className="text-[9px] font-semibold text-[hsl(var(--burgundy))]">
|
||||
Alert sent to your team
|
||||
</span>
|
||||
</motion.div>
|
||||
|
||||
@@ -115,7 +115,7 @@ export function HeroSection() {
|
||||
custom={4}
|
||||
className="w-full max-w-lg"
|
||||
>
|
||||
<WaitlistForm />
|
||||
<WaitlistForm id="waitlist-form" />
|
||||
</motion.div>
|
||||
|
||||
{/* Trust Signals */}
|
||||
@@ -136,7 +136,7 @@ export function HeroSection() {
|
||||
<span>•</span>
|
||||
<div className="flex items-center gap-2">
|
||||
<Star className="h-4 w-4 fill-current" />
|
||||
<span>Early access bonus</span>
|
||||
<span>Early access</span>
|
||||
</div>
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
@@ -765,7 +765,7 @@ export function FinalCTA() {
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<Star className="h-4 w-4 fill-current text-[hsl(var(--primary))]" />
|
||||
<span>Early access: <span className="font-semibold text-foreground">50% off for 6 months</span></span>
|
||||
<span>Early access</span>
|
||||
</div>
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
|
||||
@@ -161,7 +161,7 @@ export function LiveSerpPreview() {
|
||||
<Button
|
||||
variant="outline"
|
||||
className="border-[hsl(var(--primary))] text-[hsl(var(--primary))] hover:bg-[hsl(var(--primary))]/10"
|
||||
onClick={() => document.getElementById('waitlist-form')?.scrollIntoView({ behavior: 'smooth' })}
|
||||
onClick={() => document.getElementById('hero')?.scrollIntoView({ behavior: 'smooth' })}
|
||||
>
|
||||
Get notified on changes
|
||||
<ArrowRight className="ml-2 h-4 w-4" />
|
||||
|
||||
@@ -39,9 +39,9 @@ export function PolicyDemoVisual() {
|
||||
<motion.div
|
||||
className="space-y-2 p-3 rounded-lg border border-zinc-200 dark:border-zinc-800 bg-white dark:bg-zinc-950 overflow-hidden"
|
||||
animate={{
|
||||
borderColor: phase === 1 ? '#ef4444' : '#27272a',
|
||||
borderColor: phase === 1 ? 'hsl(var(--burgundy))' : '#27272a',
|
||||
boxShadow: phase === 1
|
||||
? '0 0 20px rgba(239, 68, 68, 0.2)'
|
||||
? '0 0 20px hsl(var(--burgundy) / 0.2)'
|
||||
: '0 1px 3px rgba(0,0,0,0.2)'
|
||||
}}
|
||||
transition={{ duration: 0.5 }}
|
||||
@@ -63,10 +63,10 @@ export function PolicyDemoVisual() {
|
||||
>
|
||||
<motion.p
|
||||
animate={{
|
||||
backgroundColor: phase === 1 ? 'rgba(239, 68, 68, 0.1)' : 'transparent',
|
||||
backgroundColor: phase === 1 ? 'hsl(var(--burgundy) / 0.1)' : 'transparent',
|
||||
paddingLeft: phase === 1 ? '4px' : '0px',
|
||||
paddingRight: phase === 1 ? '4px' : '0px',
|
||||
color: phase === 1 ? '#ef4444' : 'inherit',
|
||||
color: phase === 1 ? 'hsl(var(--burgundy))' : 'inherit',
|
||||
fontWeight: phase === 1 ? 600 : 400
|
||||
}}
|
||||
transition={{ duration: 0.4 }}
|
||||
@@ -91,7 +91,7 @@ export function PolicyDemoVisual() {
|
||||
initial={{ scaleX: 0 }}
|
||||
animate={{ scaleX: 1 }}
|
||||
transition={{ duration: 0.4 }}
|
||||
className="absolute -left-1 top-0 bottom-0 w-0.5 bg-red-500 rounded-full origin-left"
|
||||
className="absolute -left-1 top-0 bottom-0 w-0.5 bg-[hsl(var(--burgundy))] rounded-full origin-left"
|
||||
/>
|
||||
)}
|
||||
</motion.div>
|
||||
@@ -114,7 +114,7 @@ export function PolicyDemoVisual() {
|
||||
+18 words
|
||||
</span>
|
||||
<span className="flex items-center gap-1">
|
||||
<span className="w-2 h-2 rounded bg-red-500/20 border border-red-500" />
|
||||
<span className="w-2 h-2 rounded bg-[hsl(var(--burgundy))]/20 border border-[hsl(var(--burgundy))]" />
|
||||
-7 words
|
||||
</span>
|
||||
</div>
|
||||
@@ -128,16 +128,16 @@ export function PolicyDemoVisual() {
|
||||
initial={{ opacity: 0, y: 5, scale: 0.9 }}
|
||||
animate={{ opacity: 1, y: 0, scale: 1 }}
|
||||
transition={{ delay: 0.5 }}
|
||||
className="mt-3 flex items-center gap-2 p-2 rounded-lg bg-red-500/10 border border-red-500/30"
|
||||
className="mt-3 flex items-center gap-2 p-2 rounded-lg bg-[hsl(var(--burgundy))]/10 border border-[hsl(var(--burgundy))]/30"
|
||||
>
|
||||
<div className="flex-shrink-0 flex items-center justify-center w-5 h-5 rounded-full bg-red-500 text-white">
|
||||
<div className="flex-shrink-0 flex items-center justify-center w-5 h-5 rounded-full bg-[hsl(var(--burgundy))] text-white">
|
||||
<Check className="h-3 w-3" strokeWidth={3} />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<div className="text-[9px] font-bold text-red-500">
|
||||
<div className="text-[9px] font-bold text-[hsl(var(--burgundy))]">
|
||||
Audit trail saved
|
||||
</div>
|
||||
<div className="text-[8px] text-red-500/80">
|
||||
<div className="text-[8px] text-[hsl(var(--burgundy))]/80">
|
||||
Snapshot archived for compliance
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -59,10 +59,10 @@ export function SEODemoVisual() {
|
||||
<motion.div
|
||||
className="space-y-2 p-3 rounded-lg border border-zinc-200 dark:border-zinc-800 bg-white dark:bg-zinc-950"
|
||||
animate={{
|
||||
borderColor: phase === 0 ? '#27272a' : '#ef4444',
|
||||
borderColor: phase === 0 ? '#27272a' : 'hsl(var(--burgundy))',
|
||||
boxShadow: phase === 0
|
||||
? '0 1px 3px rgba(0,0,0,0.2)'
|
||||
: '0 0 20px rgba(239, 68, 68, 0.2)'
|
||||
: '0 0 20px hsl(var(--burgundy) / 0.2)'
|
||||
}}
|
||||
transition={{ duration: 0.5 }}
|
||||
>
|
||||
@@ -86,8 +86,8 @@ export function SEODemoVisual() {
|
||||
>
|
||||
<motion.span
|
||||
animate={{
|
||||
backgroundColor: phase === 1 ? 'rgba(239, 68, 68, 0.1)' : 'transparent',
|
||||
color: phase === 1 ? '#ef4444' : 'inherit'
|
||||
backgroundColor: phase === 1 ? 'hsl(var(--burgundy) / 0.1)' : 'transparent',
|
||||
color: phase === 1 ? 'hsl(var(--burgundy))' : 'inherit'
|
||||
}}
|
||||
transition={{ duration: 0.5 }}
|
||||
className="inline-block rounded px-0.5"
|
||||
@@ -100,7 +100,7 @@ export function SEODemoVisual() {
|
||||
<motion.span
|
||||
initial={{ opacity: 0, x: -5 }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
className="absolute -right-2 top-0 px-1.5 py-0.5 rounded bg-red-500 text-[8px] font-bold text-white"
|
||||
className="absolute -right-2 top-0 px-1.5 py-0.5 rounded bg-[hsl(var(--burgundy))] text-[8px] font-bold text-white"
|
||||
>
|
||||
Changed
|
||||
</motion.span>
|
||||
|
||||
@@ -5,7 +5,11 @@ import { useState } from 'react'
|
||||
import { Check, ArrowRight, Loader2 } from 'lucide-react'
|
||||
import { Button } from '@/components/ui/button'
|
||||
|
||||
export function WaitlistForm() {
|
||||
interface WaitlistFormProps {
|
||||
id?: string
|
||||
}
|
||||
|
||||
export function WaitlistForm({ id }: WaitlistFormProps) {
|
||||
const [email, setEmail] = useState('')
|
||||
const [isSubmitting, setIsSubmitting] = useState(false)
|
||||
const [isSuccess, setIsSuccess] = useState(false)
|
||||
@@ -160,7 +164,7 @@ export function WaitlistForm() {
|
||||
className="mt-6 inline-flex items-center gap-2 rounded-full bg-[hsl(var(--burgundy))]/10 border border-[hsl(var(--burgundy))]/30 px-4 py-2"
|
||||
>
|
||||
<span className="text-sm font-bold text-[hsl(var(--burgundy))]">
|
||||
🎉 Early access: 50% off for 6 months
|
||||
🎉 Early access
|
||||
</span>
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
@@ -170,65 +174,69 @@ export function WaitlistForm() {
|
||||
|
||||
return (
|
||||
<motion.form
|
||||
id={id}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
onSubmit={handleSubmit}
|
||||
className="max-w-md mx-auto"
|
||||
>
|
||||
<div className="flex flex-col sm:flex-row gap-3">
|
||||
{/* Email Input */}
|
||||
<motion.div
|
||||
className="flex-1 relative"
|
||||
animate={error ? { x: [-10, 10, -10, 10, 0] } : {}}
|
||||
transition={{ duration: 0.4 }}
|
||||
>
|
||||
<input
|
||||
type="email"
|
||||
value={email}
|
||||
onChange={(e) => {
|
||||
setEmail(e.target.value)
|
||||
setError('')
|
||||
}}
|
||||
placeholder="Enter your email"
|
||||
disabled={isSubmitting}
|
||||
className={`w-full h-14 rounded-full px-6 text-base border-2 transition-all outline-none ${error
|
||||
<div className="flex flex-col gap-3">
|
||||
<div className="flex flex-col sm:flex-row gap-3">
|
||||
{/* Email Input */}
|
||||
<motion.div
|
||||
className="flex-1 relative"
|
||||
>
|
||||
<input
|
||||
type="email"
|
||||
value={email}
|
||||
onChange={(e) => {
|
||||
setEmail(e.target.value)
|
||||
setError('')
|
||||
}}
|
||||
placeholder="Enter your email"
|
||||
disabled={isSubmitting}
|
||||
className={`w-full h-14 rounded-full px-6 text-base border-2 transition-all outline-none ${error
|
||||
? 'border-red-500 bg-red-50 focus:border-red-500 focus:ring-4 focus:ring-red-500/20'
|
||||
: 'border-border bg-background focus:border-[hsl(var(--primary))] focus:ring-4 focus:ring-[hsl(var(--primary))]/20'
|
||||
} disabled:opacity-50 disabled:cursor-not-allowed`}
|
||||
/>
|
||||
<AnimatePresence>
|
||||
{error && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: -10 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
exit={{ opacity: 0, y: -10 }}
|
||||
className="absolute -bottom-6 left-4 text-xs font-medium text-red-500"
|
||||
>
|
||||
{error}
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</motion.div>
|
||||
} disabled:opacity-50 disabled:cursor-not-allowed`}
|
||||
/>
|
||||
</motion.div>
|
||||
|
||||
{/* Submit Button */}
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={isSubmitting || !email}
|
||||
size="lg"
|
||||
className="h-14 rounded-full bg-[hsl(var(--burgundy))] px-8 text-white hover:bg-[hsl(var(--burgundy))]/90 shadow-2xl shadow-[hsl(var(--burgundy))]/30 transition-all hover:scale-105 disabled:hover:scale-100 disabled:opacity-50 disabled:cursor-not-allowed font-bold text-base group whitespace-nowrap"
|
||||
>
|
||||
{isSubmitting ? (
|
||||
<>
|
||||
<Loader2 className="mr-2 h-5 w-5 animate-spin" />
|
||||
Joining...
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
Reserve Your Spot
|
||||
<ArrowRight className="ml-2 h-5 w-5 group-hover:translate-x-1 transition-transform" />
|
||||
</>
|
||||
{/* Submit Button */}
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={isSubmitting || !email}
|
||||
size="lg"
|
||||
className="h-14 rounded-full bg-[hsl(var(--burgundy))] px-8 text-white hover:bg-[hsl(var(--burgundy))]/90 shadow-2xl shadow-[hsl(var(--burgundy))]/30 transition-all hover:scale-105 disabled:hover:scale-100 disabled:opacity-50 disabled:cursor-not-allowed font-bold text-base group whitespace-nowrap"
|
||||
>
|
||||
{isSubmitting ? (
|
||||
<>
|
||||
<Loader2 className="mr-2 h-5 w-5 animate-spin" />
|
||||
Joining...
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
Reserve Your Spot
|
||||
<ArrowRight className="ml-2 h-5 w-5 group-hover:translate-x-1 transition-transform" />
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Error Message - Visibility Improved */}
|
||||
<AnimatePresence>
|
||||
{error && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, height: 0 }}
|
||||
animate={{ opacity: 1, height: 'auto' }}
|
||||
exit={{ opacity: 0, height: 0 }}
|
||||
className="text-red-600 bg-red-50 px-4 py-2 rounded-lg text-sm font-medium border border-red-100 flex items-center gap-2"
|
||||
>
|
||||
<div className="h-1.5 w-1.5 rounded-full bg-red-500 flex-shrink-0" />
|
||||
{error}
|
||||
</motion.div>
|
||||
)}
|
||||
</Button>
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
|
||||
{/* Trust Signals Below Form */}
|
||||
|
||||
Reference in New Issue
Block a user