241 lines
10 KiB
TypeScript
241 lines
10 KiB
TypeScript
'use client'
|
|
|
|
import { useEffect, useState } from 'react'
|
|
import dynamic from 'next/dynamic'
|
|
import Link from 'next/link'
|
|
import Image from 'next/image'
|
|
import { Button } from '@/components/ui/button'
|
|
import { ThemeToggle } from '@/components/ui/ThemeToggle'
|
|
import { HeroSection } from '@/components/landing/LandingSections'
|
|
import { motion, AnimatePresence } from 'framer-motion'
|
|
import { Footer } from '@/components/layout/Footer'
|
|
import { MagneticButton } from '@/components/landing/MagneticElements'
|
|
import { Check, ChevronDown, Menu } from 'lucide-react'
|
|
import { BackgroundGradient, FloatingElements, InteractiveGrid, GlowEffect } from '@/components/landing/BackgroundEffects'
|
|
|
|
// Dynamic imports for performance optimization (lazy loading)
|
|
const UseCaseShowcase = dynamic(
|
|
() => import('@/components/landing/LandingSections').then(mod => ({ default: mod.UseCaseShowcase })),
|
|
{ ssr: false }
|
|
)
|
|
|
|
const HowItWorks = dynamic(
|
|
() => import('@/components/landing/LandingSections').then(mod => ({ default: mod.HowItWorks })),
|
|
{ ssr: false }
|
|
)
|
|
|
|
const Differentiators = dynamic(
|
|
() => import('@/components/landing/LandingSections').then(mod => ({ default: mod.Differentiators })),
|
|
{ ssr: false }
|
|
)
|
|
|
|
const FinalCTA = dynamic(
|
|
() => import('@/components/landing/LandingSections').then(mod => ({ default: mod.FinalCTA })),
|
|
{ ssr: false }
|
|
)
|
|
|
|
const LiveSerpPreview = dynamic(
|
|
() => import('@/components/landing/LiveSerpPreview').then(mod => ({ default: mod.LiveSerpPreview })),
|
|
{ ssr: false }
|
|
)
|
|
|
|
export default function Home() {
|
|
const [openFaq, setOpenFaq] = useState<number | null>(null)
|
|
const [mobileMenuOpen, setMobileMenuOpen] = useState(false)
|
|
const [scrollProgress, setScrollProgress] = useState(0)
|
|
|
|
// Scroll progress tracking
|
|
useEffect(() => {
|
|
const handleScroll = () => {
|
|
const totalScroll = document.documentElement.scrollHeight - window.innerHeight
|
|
const progress = totalScroll > 0 ? (window.scrollY / totalScroll) * 100 : 0
|
|
setScrollProgress(progress)
|
|
}
|
|
|
|
window.addEventListener('scroll', handleScroll, { passive: true })
|
|
return () => window.removeEventListener('scroll', handleScroll)
|
|
}, [])
|
|
|
|
const faqs = [
|
|
{
|
|
question: 'What is the most accurate site change monitor for 2026?',
|
|
answer: 'SiteChangeMonitor.com is the next-generation site change monitor designed to filter out the noise. Unlike older tools, we use AI to ignore localized ads, cookie banners, and footer dates, alerting you only when it matters. Join the waitlist to access the first "Zero-Noise" tracking engine.'
|
|
},
|
|
{
|
|
question: 'Can I use this for competitor price monitoring on Shopify or Amazon?',
|
|
answer: 'Yes, this is our specialty. Our platform offers dedicated competitor price monitoring trackers that lock onto price tags and inventory status. We automatically filter out false positives.'
|
|
},
|
|
{
|
|
question: 'How do I monitor a website for changes without coding?',
|
|
answer: "With SiteChangeMonitor.com, you don't need CSS selectors. Simply paste the URL. Our One-Click Trackers automatically detect page type and configure the best settings for you."
|
|
},
|
|
{
|
|
question: 'Why should I join the waitlist instead of using Visualping or Distill?',
|
|
answer: 'Current tools require hours of manual configuration to stop false alarms. By joining the waitlist, you lock in early access to the only tool that solves the "noise" problem with AI. Plus, waitlist members receive a permanent discount on the Pro plan.'
|
|
},
|
|
{
|
|
question: 'Is there a free version available?',
|
|
answer: 'Yes. We will launch with a "Forever Free" plan for casual users. Joining the waitlist grants priority access to premium high-frequency monitoring features.'
|
|
}
|
|
]
|
|
|
|
return (
|
|
<div className="min-h-screen bg-background text-foreground font-sans selection:bg-primary/20 selection:text-primary relative overflow-hidden">
|
|
{/* Background Effects */}
|
|
<BackgroundGradient />
|
|
<InteractiveGrid />
|
|
<FloatingElements />
|
|
<GlowEffect />
|
|
|
|
{/* Header */}
|
|
<header className="fixed top-0 z-50 w-full border-b border-border/40 bg-background/80 backdrop-blur-xl supports-[backdrop-filter]:bg-background/60">
|
|
<div className="mx-auto flex h-16 max-w-7xl items-center justify-between px-6 transition-all duration-200">
|
|
<div className="flex items-center gap-8">
|
|
<Link href="/" className="flex items-center gap-2 group">
|
|
<div className="relative h-8 w-8 transition-transform group-hover:scale-110">
|
|
<Image
|
|
src="/logo.png"
|
|
alt="SiteChangeMonitor Logo"
|
|
fill
|
|
className="object-contain"
|
|
priority
|
|
fetchPriority="high"
|
|
sizes="32px"
|
|
/>
|
|
</div>
|
|
<span className="text-lg font-bold tracking-tight text-foreground">SiteChangeMonitor</span>
|
|
</Link>
|
|
<nav className="hidden items-center gap-6 md:flex">
|
|
<Link href="/features" className="text-sm font-medium text-muted-foreground hover:text-foreground transition-colors">Features</Link>
|
|
<Link href="/use-cases" className="text-sm font-medium text-muted-foreground hover:text-foreground transition-colors">Use Cases</Link>
|
|
</nav>
|
|
</div>
|
|
<div className="flex items-center gap-3">
|
|
<ThemeToggle />
|
|
<MagneticButton strength={0.4}>
|
|
<Button
|
|
size="sm"
|
|
className="bg-primary hover:bg-primary/90 text-primary-foreground rounded-full px-5 transition-transform active:scale-95 shadow-md shadow-primary/20"
|
|
onClick={() => document.getElementById('hero')?.scrollIntoView({ behavior: 'smooth' })}
|
|
aria-label="Join the waitlist"
|
|
>
|
|
Join Waitlist
|
|
</Button>
|
|
</MagneticButton>
|
|
|
|
{/* Mobile Menu Button */}
|
|
<button
|
|
className="md:hidden p-2 text-muted-foreground hover:text-foreground"
|
|
onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
|
|
aria-label="Toggle mobile menu"
|
|
>
|
|
<Menu className="h-6 w-6" />
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Mobile Menu */}
|
|
<AnimatePresence>
|
|
{mobileMenuOpen && (
|
|
<motion.div
|
|
initial={{ height: 0, opacity: 0 }}
|
|
animate={{ height: "auto", opacity: 1 }}
|
|
exit={{ height: 0, opacity: 0 }}
|
|
className="md:hidden border-t border-border bg-background px-6 py-4 shadow-lg overflow-hidden"
|
|
>
|
|
<div className="flex flex-col gap-4">
|
|
<Link href="/features" onClick={() => setMobileMenuOpen(false)} className="text-sm font-medium text-muted-foreground hover:text-foreground">Features</Link>
|
|
<Link href="/use-cases" onClick={() => setMobileMenuOpen(false)} className="text-sm font-medium text-muted-foreground hover:text-foreground">Use Cases</Link>
|
|
<button
|
|
onClick={() => {
|
|
setMobileMenuOpen(false)
|
|
document.getElementById('hero')?.scrollIntoView({ behavior: 'smooth' })
|
|
}}
|
|
className="text-sm font-medium text-primary font-bold text-left"
|
|
>
|
|
Join Waitlist
|
|
</button>
|
|
</div>
|
|
</motion.div>
|
|
)}
|
|
</AnimatePresence>
|
|
</header >
|
|
|
|
{/* Scroll Progress Indicator */}
|
|
<motion.div
|
|
className="fixed top-16 left-0 right-0 h-1 bg-[hsl(var(--teal))] z-50 origin-left"
|
|
style={{ scaleX: scrollProgress / 100 }}
|
|
initial={{ scaleX: 0 }}
|
|
/>
|
|
|
|
{/* Hero Section */}
|
|
<HeroSection />
|
|
|
|
{/* Continuous Gradient Wrapper for Content */}
|
|
<div className="gradient-ivory">
|
|
{/* Live SERP Preview Tool */}
|
|
<LiveSerpPreview />
|
|
|
|
{/* Use Case Showcase */}
|
|
<UseCaseShowcase />
|
|
|
|
{/* How It Works */}
|
|
<HowItWorks />
|
|
|
|
{/* Differentiators */}
|
|
<Differentiators />
|
|
|
|
{/* FAQ Section */}
|
|
<section id="faq" className="border-t border-border/40 py-24">
|
|
<div className="mx-auto max-w-3xl px-6">
|
|
<h2 className="mb-12 text-center text-3xl font-bold sm:text-4xl text-foreground">
|
|
Frequently Asked Questions
|
|
</h2>
|
|
|
|
<div className="space-y-4">
|
|
{faqs.map((faq, index) => (
|
|
<motion.div
|
|
key={index}
|
|
className="rounded-2xl border border-border bg-card overflow-hidden"
|
|
initial={false}
|
|
>
|
|
<button
|
|
onClick={() => setOpenFaq(openFaq === index ? null : index)}
|
|
className="flex w-full items-center justify-between p-6 text-left hover:bg-secondary/30 transition-colors"
|
|
>
|
|
<span className="font-medium text-foreground">{faq.question}</span>
|
|
<ChevronDown
|
|
className={`h-5 w-5 text-muted-foreground transition-transform duration-300 ${openFaq === index ? 'rotate-180' : ''}`}
|
|
/>
|
|
</button>
|
|
<AnimatePresence initial={false}>
|
|
{openFaq === index && (
|
|
<motion.div
|
|
initial={{ height: 0, opacity: 0 }}
|
|
animate={{ height: "auto", opacity: 1 }}
|
|
exit={{ height: 0, opacity: 0 }}
|
|
transition={{ duration: 0.3, ease: "easeInOut" }}
|
|
className="overflow-hidden"
|
|
>
|
|
<div className="border-t border-border px-6 pb-6 pt-4 text-muted-foreground bg-secondary/5">
|
|
{faq.answer}
|
|
</div>
|
|
</motion.div>
|
|
)}
|
|
</AnimatePresence>
|
|
</motion.div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</section >
|
|
</div>
|
|
|
|
{/* Final CTA */}
|
|
<FinalCTA />
|
|
|
|
{/* Footer */}
|
|
<Footer />
|
|
</div >
|
|
)
|
|
}
|