Some checks failed
CI / Frontend Lint & Type Check (push) Has been cancelled
CI / Frontend Build (push) Has been cancelled
CI / Backend Lint (push) Has been cancelled
CI / Backend Tests (push) Has been cancelled
CI / Docker Build (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
Deploy / Build & Push Images (push) Has been cancelled
Deploy / Deploy to Server (push) Has been cancelled
Deploy / Notify (push) Has been cancelled
🎯 BRAND VOICE: Swift, Sharp, Strategic, Confident
LANDING PAGE:
- Hero: 'Others wait. You pounce.'
- Features: Active, punchy descriptions
- Pricing: 'Pick your weapon.'
- CTAs: 'Hunt Free', 'Start Trading', 'Go Tycoon'
PRICING PAGE:
- Scout: 'Test the waters. Zero risk.'
- Trader: 'Hunt with precision.'
- Tycoon: 'Dominate the market.'
AUTH PAGES:
- Login: 'Back to the hunt.'
- Register: 'Join the hunt.'
- Benefits: 'Your hunting gear. Ready to go.'
EMAIL TEMPLATES:
- Domain Available: 'Time to pounce. [domain] just dropped'
- Price Alert: '.tld just moved.'
- Weekly Digest: 'Your week in domains.'
- Subscription: 'You're in. [Plan] unlocked.'
DASHBOARD:
- Title: 'Command Center'
- Empty State: 'No targets yet'
TLD PAGES:
- Headline: '886+ TLDs. Live Prices.'
- Section: 'Moving Now' instead of 'Trending'
CONTACT & ABOUT:
- About: 'Built for hunters. By hunters.'
- Mission: 'Level the playing field.'
- Contact: 'Let's Talk' - direct, confident
All text now follows the hunter metaphor with:
- Active verbs (Track, Hunt, Pounce, Grab)
- Urgency without panic
- Confident statements
- Data as proof
- Short, punchy sentences
429 lines
18 KiB
TypeScript
429 lines
18 KiB
TypeScript
'use client'
|
|
|
|
import { useEffect, useState } from 'react'
|
|
import Image from 'next/image'
|
|
import { Header } from '@/components/Header'
|
|
import { Footer } from '@/components/Footer'
|
|
import { DomainChecker } from '@/components/DomainChecker'
|
|
import { useStore } from '@/lib/store'
|
|
import { api } from '@/lib/api'
|
|
import { Eye, Bell, Clock, Shield, ArrowRight, Check, TrendingUp, TrendingDown, Minus, Lock, ChevronRight } from 'lucide-react'
|
|
import Link from 'next/link'
|
|
import clsx from 'clsx'
|
|
|
|
const features = [
|
|
{
|
|
icon: Eye,
|
|
title: 'Always Watching',
|
|
description: 'Daily scans. 886+ TLDs. You sleep, we hunt.',
|
|
},
|
|
{
|
|
icon: Bell,
|
|
title: 'Instant Alerts',
|
|
description: 'Domain drops? You know first. Always.',
|
|
},
|
|
{
|
|
icon: Clock,
|
|
title: 'Expiry Intel',
|
|
description: 'See when domains expire. Plan your move.',
|
|
},
|
|
{
|
|
icon: Shield,
|
|
title: 'Your Strategy, Private',
|
|
description: 'No one sees your watchlist. Ever.',
|
|
},
|
|
]
|
|
|
|
const tiers = [
|
|
{
|
|
name: 'Scout',
|
|
price: '0',
|
|
period: '',
|
|
description: 'Test the waters. Zero risk.',
|
|
features: ['5 domains', 'Daily checks', 'Email alerts', 'Basic search'],
|
|
cta: 'Hunt Free',
|
|
highlighted: false,
|
|
},
|
|
{
|
|
name: 'Trader',
|
|
price: '19',
|
|
period: '/mo',
|
|
description: 'Hunt with precision.',
|
|
features: ['50 domains', 'Hourly checks', 'SMS alerts', 'Domain valuation', 'Portfolio tracking'],
|
|
cta: 'Start Trading',
|
|
highlighted: true,
|
|
},
|
|
{
|
|
name: 'Tycoon',
|
|
price: '49',
|
|
period: '/mo',
|
|
description: 'Dominate the market.',
|
|
features: ['500 domains', 'Real-time checks', 'API access', 'SEO metrics', 'Bulk tools'],
|
|
cta: 'Go Tycoon',
|
|
highlighted: false,
|
|
},
|
|
]
|
|
|
|
interface TldData {
|
|
tld: string
|
|
type: string
|
|
description: string
|
|
avg_registration_price: number
|
|
min_registration_price: number
|
|
max_registration_price: number
|
|
trend: string
|
|
}
|
|
|
|
interface TrendingTld {
|
|
tld: string
|
|
reason: string
|
|
current_price: number
|
|
price_change: number // API returns price_change, not price_change_percent
|
|
}
|
|
|
|
// Shimmer component for locked content
|
|
function ShimmerBlock({ className }: { className?: string }) {
|
|
return (
|
|
<div className={clsx(
|
|
"relative overflow-hidden rounded bg-background-tertiary",
|
|
className
|
|
)}>
|
|
<div className="absolute inset-0 -translate-x-full animate-[shimmer_2s_infinite] bg-gradient-to-r from-transparent via-foreground/5 to-transparent" />
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default function HomePage() {
|
|
const { checkAuth, isLoading, isAuthenticated } = useStore()
|
|
const [tldData, setTldData] = useState<TldData[]>([])
|
|
const [trendingTlds, setTrendingTlds] = useState<TrendingTld[]>([])
|
|
const [loadingTlds, setLoadingTlds] = useState(true)
|
|
|
|
useEffect(() => {
|
|
checkAuth()
|
|
fetchTldData()
|
|
}, [checkAuth])
|
|
|
|
const fetchTldData = async () => {
|
|
try {
|
|
const [overview, trending] = await Promise.all([
|
|
api.getTldOverview(8),
|
|
api.getTrendingTlds()
|
|
])
|
|
setTldData(overview.tlds)
|
|
setTrendingTlds(trending.trending.slice(0, 4))
|
|
} catch (error) {
|
|
console.error('Failed to fetch TLD data:', error)
|
|
} finally {
|
|
setLoadingTlds(false)
|
|
}
|
|
}
|
|
|
|
if (isLoading) {
|
|
return (
|
|
<div className="min-h-screen flex items-center justify-center">
|
|
<div className="w-5 h-5 border-2 border-accent border-t-transparent rounded-full animate-spin" />
|
|
</div>
|
|
)
|
|
}
|
|
|
|
const getTrendIcon = (priceChange: number) => {
|
|
if (priceChange > 0) return <TrendingUp className="w-3.5 h-3.5" />
|
|
if (priceChange < 0) return <TrendingDown className="w-3.5 h-3.5" />
|
|
return <Minus className="w-3.5 h-3.5" />
|
|
}
|
|
|
|
const getTrendDirection = (priceChange: number) => {
|
|
if (priceChange > 0) return 'up'
|
|
if (priceChange < 0) return 'down'
|
|
return 'stable'
|
|
}
|
|
|
|
return (
|
|
<div className="min-h-screen relative">
|
|
{/* Ambient background glow */}
|
|
<div className="fixed inset-0 pointer-events-none">
|
|
<div className="absolute top-0 left-1/2 -translate-x-1/2 w-[800px] h-[600px] bg-accent/[0.03] rounded-full blur-3xl" />
|
|
</div>
|
|
|
|
<Header />
|
|
|
|
{/* Hero Section */}
|
|
<section className="relative pt-28 sm:pt-32 md:pt-36 lg:pt-40 pb-16 sm:pb-20 px-4 sm:px-6">
|
|
<div className="max-w-7xl mx-auto text-center">
|
|
{/* Puma Logo - Compact & Elegant */}
|
|
<div className="flex justify-center mb-6 sm:mb-8 animate-fade-in">
|
|
<Image
|
|
src="/pounce-puma.png"
|
|
alt="pounce"
|
|
width={300}
|
|
height={210}
|
|
className="w-32 h-auto sm:w-40 md:w-48 object-contain drop-shadow-[0_0_40px_rgba(16,185,129,0.25)]"
|
|
priority
|
|
/>
|
|
</div>
|
|
|
|
{/* Main Headline - RESPONSIVE */}
|
|
<h1 className="font-display text-[2.25rem] leading-[1.1] sm:text-[3rem] md:text-[3.75rem] lg:text-[4.5rem] xl:text-[5.25rem] tracking-[-0.035em] mb-6 sm:mb-8 md:mb-10 animate-slide-up">
|
|
<span className="block text-foreground">Others wait.</span>
|
|
<span className="block text-foreground-muted">You pounce.</span>
|
|
</h1>
|
|
|
|
{/* Subheadline - RESPONSIVE */}
|
|
<p className="text-body-md sm:text-body-lg md:text-body-xl text-foreground-muted max-w-xl sm:max-w-2xl mx-auto mb-10 sm:mb-12 md:mb-16 animate-slide-up delay-100 px-4 sm:px-0">
|
|
Domain intelligence for the decisive. Track any domain.
|
|
Know the moment it drops. Move before anyone else.
|
|
</p>
|
|
|
|
{/* Domain Checker */}
|
|
<div className="animate-slide-up delay-150">
|
|
<DomainChecker />
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
{/* TLD Price Intelligence Section */}
|
|
<section className="relative py-16 sm:py-20 md:py-24 px-4 sm:px-6 border-t border-border-subtle">
|
|
<div className="max-w-7xl mx-auto">
|
|
{/* Section Header */}
|
|
<div className="text-center mb-10 sm:mb-12">
|
|
<div className="inline-flex items-center gap-2 px-3 py-1.5 bg-accent-muted border border-accent/20 rounded-full mb-5">
|
|
<TrendingUp className="w-4 h-4 text-accent" />
|
|
<span className="text-ui-sm text-accent">Market Intel</span>
|
|
</div>
|
|
<h2 className="font-display text-[1.75rem] sm:text-[2.5rem] md:text-[3.25rem] lg:text-[4rem] leading-[1.1] tracking-[-0.03em] text-foreground mb-4 sm:mb-5 md:mb-6">
|
|
886 TLDs. Tracked Daily.
|
|
</h2>
|
|
<p className="text-body-sm sm:text-body text-foreground-muted max-w-lg mx-auto">
|
|
See price movements. Spot opportunities. Act fast.
|
|
</p>
|
|
</div>
|
|
|
|
{/* Trending TLDs - Card Grid */}
|
|
<div className="mb-8">
|
|
<div className="flex items-center gap-2 mb-4">
|
|
<TrendingUp className="w-4 h-4 text-accent" />
|
|
<span className="text-ui font-medium text-foreground">Trending Now</span>
|
|
</div>
|
|
|
|
{loadingTlds ? (
|
|
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4">
|
|
{[...Array(4)].map((_, i) => (
|
|
<div key={i} className="p-5 bg-background-secondary border border-border rounded-xl">
|
|
<ShimmerBlock className="h-6 w-16 mb-3" />
|
|
<ShimmerBlock className="h-4 w-full mb-2" />
|
|
<ShimmerBlock className="h-4 w-20" />
|
|
</div>
|
|
))}
|
|
</div>
|
|
) : (
|
|
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4">
|
|
{trendingTlds.map((item) => (
|
|
<Link
|
|
key={item.tld}
|
|
href={isAuthenticated ? `/tld-pricing/${item.tld}` : '/register'}
|
|
className="group p-5 bg-background-secondary border border-border rounded-xl
|
|
hover:border-border-hover transition-all duration-300"
|
|
>
|
|
<div className="flex items-center justify-between mb-3">
|
|
<span className="font-mono text-body-lg sm:text-heading-sm text-foreground">.{item.tld}</span>
|
|
<span className={clsx(
|
|
"flex items-center gap-1 text-ui-sm font-medium px-2 py-0.5 rounded-full",
|
|
(item.price_change ?? 0) > 0
|
|
? "text-[#f97316] bg-[#f9731615]"
|
|
: (item.price_change ?? 0) < 0
|
|
? "text-accent bg-accent-muted"
|
|
: "text-foreground-muted bg-background-tertiary"
|
|
)}>
|
|
{getTrendIcon(item.price_change ?? 0)}
|
|
{(item.price_change ?? 0) > 0 ? '+' : ''}{(item.price_change ?? 0).toFixed(1)}%
|
|
</span>
|
|
</div>
|
|
|
|
<p className="text-body-xs text-foreground-subtle mb-3 line-clamp-2">{item.reason}</p>
|
|
|
|
<div className="flex items-center justify-between">
|
|
{isAuthenticated ? (
|
|
<span className="text-body-sm text-foreground">${(item.current_price ?? 0).toFixed(2)}/yr</span>
|
|
) : (
|
|
<ShimmerBlock className="h-5 w-20" />
|
|
)}
|
|
<ChevronRight className="w-4 h-4 text-foreground-subtle group-hover:text-accent transition-colors" />
|
|
</div>
|
|
</Link>
|
|
))}
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{/* Login CTA for non-authenticated users */}
|
|
{!isAuthenticated && (
|
|
<div className="p-5 bg-background-secondary/50 border border-border rounded-xl flex flex-col sm:flex-row items-center justify-between gap-4">
|
|
<div className="flex items-center gap-3">
|
|
<div className="w-10 h-10 bg-accent-muted rounded-xl flex items-center justify-center">
|
|
<Lock className="w-4 h-4 text-accent" />
|
|
</div>
|
|
<div>
|
|
<p className="text-body-sm font-medium text-foreground">Unlock Full Data</p>
|
|
<p className="text-ui-sm text-foreground-subtle">
|
|
Sign in for prices, trends, and registrar comparisons.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
<Link
|
|
href="/register"
|
|
className="shrink-0 px-5 py-2.5 bg-accent text-background text-ui font-medium rounded-lg
|
|
hover:bg-accent-hover transition-all duration-300"
|
|
>
|
|
Get Started Free
|
|
</Link>
|
|
</div>
|
|
)}
|
|
|
|
{/* View All Link */}
|
|
<div className="mt-6 text-center">
|
|
<Link
|
|
href="/tld-pricing"
|
|
className="inline-flex items-center gap-2 text-body-sm font-medium text-accent hover:text-accent-hover transition-colors"
|
|
>
|
|
Explore All TLDs
|
|
<ArrowRight className="w-4 h-4" />
|
|
</Link>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
{/* Features Section */}
|
|
<section className="relative py-20 sm:py-24 md:py-32 px-4 sm:px-6">
|
|
<div className="max-w-7xl mx-auto">
|
|
<div className="text-center mb-12 sm:mb-16 md:mb-20">
|
|
<p className="label sm:label-md text-accent mb-3 sm:mb-4 md:mb-5">How It Works</p>
|
|
<h2 className="font-display text-[1.75rem] sm:text-[2.5rem] md:text-[3.25rem] lg:text-[4rem] leading-[1.1] tracking-[-0.03em] text-foreground mb-4 sm:mb-5 md:mb-6">
|
|
Built for hunters.
|
|
</h2>
|
|
<p className="text-body-sm sm:text-body md:text-body-lg text-foreground-muted max-w-md sm:max-w-lg mx-auto px-4 sm:px-0">
|
|
The tools that give you the edge. Simple. Powerful. Decisive.
|
|
</p>
|
|
</div>
|
|
|
|
<div className="grid sm:grid-cols-2 lg:grid-cols-4 gap-4 sm:gap-5 md:gap-6">
|
|
{features.map((feature, i) => (
|
|
<div
|
|
key={feature.title}
|
|
className="group p-5 sm:p-6 rounded-2xl border border-transparent hover:border-border hover:bg-background-secondary/50 transition-all duration-500"
|
|
style={{ animationDelay: `${i * 100}ms` }}
|
|
>
|
|
<div className="w-10 sm:w-11 h-10 sm:h-11 bg-background-secondary border border-border rounded-xl flex items-center justify-center mb-4 sm:mb-5
|
|
group-hover:border-accent/30 group-hover:bg-accent/5 transition-all duration-500">
|
|
<feature.icon className="w-4 sm:w-5 h-4 sm:h-5 text-foreground-muted group-hover:text-accent transition-colors duration-500" strokeWidth={1.5} />
|
|
</div>
|
|
<h3 className="text-body sm:text-body-md font-medium text-foreground mb-2">{feature.title}</h3>
|
|
<p className="text-body-xs sm:text-body-sm text-foreground-subtle leading-relaxed">{feature.description}</p>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
{/* Pricing Section */}
|
|
<section className="relative py-20 sm:py-24 md:py-32 px-4 sm:px-6">
|
|
{/* Section glow */}
|
|
<div className="absolute inset-0 pointer-events-none overflow-hidden">
|
|
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[600px] h-[400px] bg-accent/[0.02] rounded-full blur-3xl" />
|
|
</div>
|
|
|
|
<div className="relative max-w-5xl mx-auto">
|
|
<div className="text-center mb-12 sm:mb-16 md:mb-20">
|
|
<p className="label sm:label-md text-accent mb-3 sm:mb-4 md:mb-5">Pricing</p>
|
|
<h2 className="font-display text-[1.75rem] sm:text-[2.5rem] md:text-[3.25rem] lg:text-[4rem] leading-[1.1] tracking-[-0.03em] text-foreground mb-4 sm:mb-5 md:mb-6">
|
|
Pick your weapon.
|
|
</h2>
|
|
<p className="text-body-sm sm:text-body md:text-body-lg text-foreground-muted">
|
|
Start free. Scale when you're ready.
|
|
</p>
|
|
</div>
|
|
|
|
<div className="grid sm:grid-cols-2 md:grid-cols-3 gap-4 sm:gap-5">
|
|
{tiers.map((tier, i) => (
|
|
<div
|
|
key={tier.name}
|
|
className={`relative p-5 sm:p-6 md:p-7 rounded-2xl border transition-all duration-500 ${
|
|
tier.highlighted
|
|
? 'bg-background-secondary border-accent/20 glow-accent'
|
|
: 'bg-background-secondary/50 border-border hover:border-border-hover'
|
|
}`}
|
|
style={{ animationDelay: `${i * 100}ms` }}
|
|
>
|
|
{tier.highlighted && (
|
|
<div className="absolute -top-3 left-1/2 -translate-x-1/2">
|
|
<span className="px-3 py-1 bg-accent text-background text-ui-xs sm:text-ui-sm font-medium rounded-full">
|
|
Popular
|
|
</span>
|
|
</div>
|
|
)}
|
|
|
|
<div className="mb-5 sm:mb-6">
|
|
<h3 className="text-body sm:text-body-md font-medium text-foreground mb-1">{tier.name}</h3>
|
|
<p className="text-ui-sm sm:text-ui text-foreground-subtle mb-4 sm:mb-5">{tier.description}</p>
|
|
<div className="flex items-baseline gap-1">
|
|
{tier.price === '0' ? (
|
|
<span className="text-heading-md sm:text-heading-lg font-display text-foreground">Free</span>
|
|
) : (
|
|
<>
|
|
<span className="text-heading-md sm:text-heading-lg font-display text-foreground">${tier.price}</span>
|
|
<span className="text-body-sm text-foreground-subtle">{tier.period}</span>
|
|
</>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
<ul className="space-y-2.5 sm:space-y-3 mb-6 sm:mb-8">
|
|
{tier.features.map((feature) => (
|
|
<li key={feature} className="flex items-center gap-2.5 sm:gap-3 text-body-xs sm:text-body-sm">
|
|
<Check className="w-3.5 sm:w-4 h-3.5 sm:h-4 text-accent shrink-0" strokeWidth={2.5} />
|
|
<span className="text-foreground-muted">{feature}</span>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
|
|
<Link
|
|
href={isAuthenticated ? '/dashboard' : '/register'}
|
|
className={`w-full flex items-center justify-center gap-2 py-2.5 sm:py-3 rounded-xl text-ui-sm sm:text-ui font-medium transition-all duration-300 ${
|
|
tier.highlighted
|
|
? 'bg-accent text-background hover:bg-accent-hover'
|
|
: 'bg-background-tertiary text-foreground border border-border hover:border-border-hover'
|
|
}`}
|
|
>
|
|
{tier.cta}
|
|
</Link>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
{/* CTA Section */}
|
|
<section className="relative py-20 sm:py-24 md:py-32 px-4 sm:px-6">
|
|
<div className="max-w-2xl mx-auto text-center">
|
|
<h2 className="font-display text-[1.75rem] sm:text-[2.5rem] md:text-[3.25rem] lg:text-[4rem] leading-[1.1] tracking-[-0.03em] text-foreground mb-4 sm:mb-5 md:mb-6">
|
|
Start monitoring today
|
|
</h2>
|
|
<p className="text-body-md sm:text-body-lg md:text-body-xl text-foreground-muted mb-8 sm:mb-10">
|
|
Create a free account and track up to 3 domains.
|
|
No credit card required.
|
|
</p>
|
|
<Link
|
|
href="/register"
|
|
className="btn-primary inline-flex items-center gap-2 sm:gap-2.5 px-6 sm:px-8 py-3 sm:py-4 text-body-sm sm:text-body"
|
|
>
|
|
Get Started Free
|
|
<ArrowRight className="w-4 h-4" />
|
|
</Link>
|
|
</div>
|
|
</section>
|
|
|
|
<Footer />
|
|
</div>
|
|
)
|
|
}
|