website-monitor/frontend/components/landing/SEODemoVisual.tsx

141 lines
5.2 KiB
TypeScript

'use client'
import { motion } from 'framer-motion'
import { useState, useEffect } from 'react'
import { TrendingDown, TrendingUp } from 'lucide-react'
function resolveHsl(cssVar: string): string {
if (typeof window === 'undefined') return 'transparent'
const value = getComputedStyle(document.documentElement).getPropertyValue(cssVar).trim()
return value ? `hsl(${value})` : 'transparent'
}
export function SEODemoVisual() {
const [phase, setPhase] = useState(0)
const [colors, setColors] = useState({ burgundy: '#993350', border: '#27272a', background: '#0c0b09' })
useEffect(() => {
setColors({
burgundy: resolveHsl('--burgundy'),
border: resolveHsl('--border'),
background: resolveHsl('--background'),
})
}, [])
useEffect(() => {
const interval = setInterval(() => {
setPhase(p => (p + 1) % 2)
}, 3000)
return () => clearInterval(interval)
}, [])
const oldMeta = "Best enterprise software for teams of all sizes. Try free for 30 days."
const newMeta = "Best enterprise software for teams of all sizes. Try free for 30 days. Now with AI-powered analytics and real-time collaboration."
return (
<div className="relative h-full bg-gradient-to-br from-background via-background to-[hsl(var(--teal))]/5 rounded-xl p-4 overflow-hidden">
{/* SERP Result */}
<div className="space-y-4">
{/* Ranking Indicator */}
<div className="flex items-center justify-between">
<div className="text-xs font-mono text-muted-foreground">
google.com/search
</div>
<motion.div
className="flex items-center gap-1 px-2 py-1 rounded-full bg-background border border-border"
animate={{
borderColor: phase === 0 ? colors.border : colors.burgundy,
backgroundColor: phase === 0 ? colors.background : `${colors.burgundy}1a`
}}
transition={{ duration: 0.5 }}
>
<span className="text-xs font-bold">Ranking:</span>
<motion.span
key={phase}
initial={{ y: -10, opacity: 0 }}
animate={{ y: 0, opacity: 1 }}
exit={{ y: 10, opacity: 0 }}
className="text-xs font-bold"
>
#{phase === 0 ? '3' : '5'}
</motion.span>
{phase === 1 && (
<motion.div
initial={{ scale: 0, rotate: -180 }}
animate={{ scale: 1, rotate: 0 }}
>
<TrendingDown className="h-3 w-3 text-[hsl(var(--burgundy))]" />
</motion.div>
)}
</motion.div>
</div>
{/* SERP Snippet */}
<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 ? colors.border : colors.burgundy,
boxShadow: phase === 0
? '0 1px 3px rgba(0,0,0,0.2)'
: `0 0 20px ${colors.burgundy}33`
}}
transition={{ duration: 0.5 }}
>
{/* URL */}
<div className="flex items-center gap-2">
<div className="w-4 h-4 rounded-full bg-zinc-800" />
<span className="text-[10px] text-zinc-500 font-mono">
competitor.com/product
</span>
</div>
{/* Title */}
<h4 className="text-sm font-bold text-zinc-300 line-clamp-1">
Best Enterprise Software Solution 2026
</h4>
{/* Meta Description with change highlighting */}
<motion.p
className="text-[11px] text-zinc-500 leading-relaxed relative"
layout
>
<motion.span
animate={{
backgroundColor: phase === 1 ? `${colors.burgundy}1a` : 'rgba(0,0,0,0)',
color: phase === 1 ? colors.burgundy : 'inherit'
}}
transition={{ duration: 0.5 }}
className="inline-block rounded px-0.5"
>
{phase === 0 ? oldMeta : newMeta}
</motion.span>
{/* Change indicator */}
{phase === 1 && (
<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-[hsl(var(--burgundy))] text-[8px] font-bold text-white"
>
Changed
</motion.span>
)}
</motion.p>
</motion.div>
{/* Alert Badge */}
{phase === 1 && (
<motion.div
initial={{ opacity: 0, y: 5, scale: 0.9 }}
animate={{ opacity: 1, y: 0, scale: 1 }}
className="flex items-center justify-center gap-2 text-[10px] font-bold text-[hsl(var(--teal))] uppercase tracking-wider"
>
<span className="w-1.5 h-1.5 rounded-full bg-[hsl(var(--teal))] animate-pulse" />
Meta Description Changed
</motion.div>
)}
</div>
</div>
)
}