feat: redesign landing hero to match website quality

This commit is contained in:
Usman Baig
2026-03-21 19:41:51 +01:00
parent 3710f081a6
commit acfd532194

View File

@@ -4,7 +4,7 @@ import { useEffect, useState } from 'react'
import Link from 'next/link' import Link from 'next/link'
import { motion } from 'framer-motion' import { motion } from 'framer-motion'
import { useAuth } from '@/lib/auth/context' import { useAuth } from '@/lib/auth/context'
import { initiateOAuthFlow, initiateSignupFlow } from '@/lib/api/oauth' import { initiateOAuthFlow } from '@/lib/api/oauth'
import { listSites, listDeletedSites, restoreSite, type Site } from '@/lib/api/sites' import { listSites, listDeletedSites, restoreSite, type Site } from '@/lib/api/sites'
import { getStats } from '@/lib/api/stats' import { getStats } from '@/lib/api/stats'
import type { Stats } from '@/lib/api/stats' import type { Stats } from '@/lib/api/stats'
@@ -15,6 +15,7 @@ import DeleteSiteModal from '@/components/sites/DeleteSiteModal'
import { Button } from '@ciphera-net/ui' import { Button } from '@ciphera-net/ui'
import Image from 'next/image' import Image from 'next/image'
import { BarChartIcon, LockIcon, ZapIcon, CheckCircleIcon, XIcon, GlobeIcon } from '@ciphera-net/ui' import { BarChartIcon, LockIcon, ZapIcon, CheckCircleIcon, XIcon, GlobeIcon } from '@ciphera-net/ui'
import { Cookie, ShieldCheck, Code, Lightning, ArrowRight, GithubLogo } from '@phosphor-icons/react'
import { toast } from '@ciphera-net/ui' import { toast } from '@ciphera-net/ui'
import { getAuthErrorMessage } from '@ciphera-net/ui' import { getAuthErrorMessage } from '@ciphera-net/ui'
import { getSitesLimitForPlan } from '@/lib/plans' import { getSitesLimitForPlan } from '@/lib/plans'
@@ -232,83 +233,82 @@ export default function HomePage() {
if (!user) { if (!user) {
return ( return (
<div className="relative min-h-screen flex flex-col overflow-hidden"> <>
{/* HERO — full viewport */}
{/* * --- 1. ATMOSPHERE (Background) --- */} <div className="relative -mt-[88px] min-h-screen flex items-center pt-[88px] pb-20 lg:pb-32 overflow-hidden">
<div className="absolute inset-0 -z-10 pointer-events-none"> {/* Background atmosphere */}
{/* * Bottom-right Neutral Glow */} <div className="absolute inset-0 -z-10 pointer-events-none">
<div className="absolute bottom-0 right-1/4 w-[500px] h-[500px] bg-neutral-400/10 rounded-full blur-[128px] opacity-40" /> <div className="absolute top-0 left-1/4 w-[800px] h-[800px] bg-brand-orange/5 rounded-full blur-[200px]" />
{/* * Grid Pattern with Radial Mask */} <div className="absolute bottom-0 right-1/4 w-[600px] h-[600px] bg-neutral-500/5 rounded-full blur-[150px]" />
<div </div>
className="absolute inset-0 bg-grid-pattern opacity-[0.05]" <div className="absolute bottom-0 left-0 right-0 h-48 bg-gradient-to-t from-neutral-950 to-transparent" />
style={{ maskImage: 'radial-gradient(ellipse at center, black 0%, transparent 70%)' }}
/>
</div>
<div className="flex-grow w-full max-w-6xl mx-auto px-4 pt-20 pb-10 z-10"> <div className="relative z-10 w-full max-w-6xl mx-auto px-6">
{/* H1 */}
{/* * --- 2. BADGE --- */}
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5 }}
className="inline-flex justify-center mb-8 w-full"
>
<span className="badge-primary">
<span className="w-1.5 h-1.5 rounded-full bg-brand-orange animate-pulse" />
Privacy-First Analytics
</span>
</motion.div>
{/* * --- 3. HEADLINE --- */}
<div className="text-center mb-20">
<motion.h1 <motion.h1
initial={{ opacity: 0, y: 20 }} initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }} animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5, delay: 0.1 }} transition={{ duration: 0.5 }}
className="text-5xl md:text-7xl font-bold tracking-tight text-white mb-6" className="text-5xl sm:text-6xl md:text-7xl font-bold text-white leading-[1.1] mb-6"
> >
Simple analytics for <br /> Analytics without the{' '}
<span className="relative inline-block"> <span className="relative inline-block">
<span className="gradient-text">privacy-conscious</span> <span className="gradient-text">surveillance.</span>
{/* * SVG Underline from Main Site */}
<svg className="absolute -bottom-2 left-0 w-full h-3 text-brand-orange/30" viewBox="0 0 200 12" preserveAspectRatio="none"> <svg className="absolute -bottom-2 left-0 w-full h-3 text-brand-orange/30" viewBox="0 0 200 12" preserveAspectRatio="none">
<path d="M0 9C50 3 150 3 200 9" fill="none" stroke="currentColor" strokeWidth="4" strokeLinecap="round" /> <path d="M0 9C50 3 150 3 200 9" fill="none" stroke="currentColor" strokeWidth="4" strokeLinecap="round" />
</svg> </svg>
</span> </span>
{' '}apps.
</motion.h1> </motion.h1>
{/* Subtitle */}
<motion.p <motion.p
initial={{ opacity: 0, y: 20 }} initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }} animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5, delay: 0.2 }} transition={{ duration: 0.5, delay: 0.1 }}
className="text-xl text-neutral-400 max-w-2xl mx-auto mb-10 leading-relaxed" className="text-xl text-neutral-300 mb-10 leading-relaxed max-w-xl"
> >
Respect your users' privacy while getting the insights you need. Respect your users' privacy while getting the insights you need.
No cookies, no IP tracking, fully GDPR compliant. No cookies, no IP tracking, fully GDPR compliant.
</motion.p> </motion.p>
{/* * --- 4. CTAs --- */} {/* CTAs */}
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5, delay: 0.2 }}
className="flex flex-row gap-3 flex-wrap mb-10"
>
<Button onClick={() => initiateOAuthFlow()} variant="primary" className="px-6 py-3 shadow-lg shadow-brand-orange/20 gap-2">
Try Pulse Free <ArrowRight weight="bold" className="w-4 h-4" />
</Button>
<Button onClick={() => window.open('https://github.com/ciphera-net/pulse', '_blank')} variant="secondary" className="px-6 py-3 border border-white/10 gap-2">
<GithubLogo weight="bold" className="w-4 h-4" /> View on GitHub
</Button>
</motion.div>
{/* Trust badges */}
<motion.div <motion.div
initial={{ opacity: 0, y: 20 }} initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }} animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5, delay: 0.3 }} transition={{ duration: 0.5, delay: 0.3 }}
className="flex flex-col sm:flex-row gap-4 justify-center items-center mb-20" className="flex flex-wrap gap-x-6 gap-y-3 text-sm text-neutral-400"
> >
<Button onClick={() => initiateOAuthFlow()} variant="primary" className="px-8 py-4 text-lg shadow-lg shadow-brand-orange/20"> <span className="flex items-center gap-2"><Cookie weight="bold" className="w-4 h-4" /> Cookie-free</span>
Get Started <span className="text-neutral-700">|</span>
</Button> <span className="flex items-center gap-2"><Code weight="bold" className="w-4 h-4" /> Open source client</span>
<Button onClick={() => initiateSignupFlow()} variant="secondary" className="px-8 py-4 text-lg"> <span className="text-neutral-700">|</span>
Create Account <span className="flex items-center gap-2"><ShieldCheck weight="bold" className="w-4 h-4" /> GDPR compliant</span>
</Button> <span className="text-neutral-700">|</span>
<span className="flex items-center gap-2"><Lightning weight="bold" className="w-4 h-4" /> Under 2KB</span>
</motion.div> </motion.div>
</div> </div>
</div>
{/* * NEW: DASHBOARD PREVIEW */} {/* Existing content below hero */}
<div className="w-full max-w-6xl mx-auto px-4 pb-10">
<DashboardPreview /> <DashboardPreview />
{/* * --- 5. GLASS CARDS --- */} {/* Glass cards */}
<div className="grid md:grid-cols-3 gap-6 text-left mb-32"> <div className="grid md:grid-cols-3 gap-6 text-left mb-32">
{[ {[
{ icon: LockIcon, title: "Privacy First", desc: "We don't track personal data. No IP addresses, no fingerprints, no cookies." }, { icon: LockIcon, title: "Privacy First", desc: "We don't track personal data. No IP addresses, no fingerprints, no cookies." },
@@ -334,10 +334,9 @@ export default function HomePage() {
))} ))}
</div> </div>
{/* * NEW: COMPARISON SECTION */}
<ComparisonSection /> <ComparisonSection />
{/* * NEW: CTA BOTTOM */} {/* Bottom CTA */}
<motion.div <motion.div
initial={{ opacity: 0, y: 20 }} initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
@@ -351,9 +350,8 @@ export default function HomePage() {
</Button> </Button>
<p className="mt-4 text-sm text-neutral-500">No credit card required Cancel anytime</p> <p className="mt-4 text-sm text-neutral-500">No credit card required Cancel anytime</p>
</motion.div> </motion.div>
</div> </div>
</div> </>
) )
} }