126 lines
4.7 KiB
TypeScript
126 lines
4.7 KiB
TypeScript
'use client'
|
|
|
|
import { motion } from 'framer-motion'
|
|
import { useState, useEffect } from 'react'
|
|
import { TrendingDown, TrendingUp } from 'lucide-react'
|
|
|
|
export function SEODemoVisual() {
|
|
const [phase, setPhase] = useState(0)
|
|
|
|
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 min-h-[200px] 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 ? 'hsl(var(--border))' : 'hsl(var(--burgundy))',
|
|
backgroundColor: phase === 0 ? 'hsl(var(--background))' : 'hsl(var(--burgundy) / 0.1)'
|
|
}}
|
|
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 ? '#27272a' : 'hsl(var(--burgundy))',
|
|
boxShadow: phase === 0
|
|
? '0 1px 3px rgba(0,0,0,0.2)'
|
|
: '0 0 20px hsl(var(--burgundy) / 0.2)'
|
|
}}
|
|
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 ? 'hsl(var(--burgundy) / 0.1)' : 'transparent',
|
|
color: phase === 1 ? 'hsl(var(--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>
|
|
)
|
|
}
|