2026-08-18 18:33:49 -07:00
|
|
|
'use client'
|
|
|
|
|
|
|
|
|
|
import { motion, AnimatePresence } from 'framer-motion'
|
|
|
|
|
import { useState, useEffect } from 'react'
|
2026-08-31 13:59:46 -07:00
|
|
|
import Image from 'next/image'
|
|
|
|
|
import { Code, Network, Zap, Sparkles, Shield, Cpu } from 'lucide-react'
|
|
|
|
|
import { useLanguage } from '@/lib/LanguageContext'
|
2026-08-18 18:33:49 -07:00
|
|
|
|
2026-08-31 13:59:46 -07:00
|
|
|
const roleIcons = [Network, Shield, Code, Sparkles, Zap, Cpu]
|
2026-08-18 18:33:49 -07:00
|
|
|
|
|
|
|
|
export function HeroBlobFlow() {
|
|
|
|
|
const [index, setIndex] = useState(0)
|
2026-08-31 13:59:46 -07:00
|
|
|
const [isDark, setIsDark] = useState(false)
|
|
|
|
|
const { t } = useLanguage()
|
|
|
|
|
const roles = t.hero.roles
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
const isDarkMode = document.documentElement.classList.contains('dark')
|
|
|
|
|
setIsDark(isDarkMode)
|
|
|
|
|
|
|
|
|
|
const observer = new MutationObserver(() => {
|
|
|
|
|
const darkMode = document.documentElement.classList.contains('dark')
|
|
|
|
|
setIsDark(darkMode)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
observer.observe(document.documentElement, {
|
|
|
|
|
attributes: true,
|
|
|
|
|
attributeFilter: ['class'],
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
return () => observer.disconnect()
|
|
|
|
|
}, [])
|
2026-08-18 18:33:49 -07:00
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
const interval = setInterval(() => {
|
|
|
|
|
setIndex((prev) => (prev + 1) % roles.length)
|
|
|
|
|
}, 2000)
|
|
|
|
|
return () => clearInterval(interval)
|
2026-08-31 13:59:46 -07:00
|
|
|
}, [roles.length])
|
2026-08-18 18:33:49 -07:00
|
|
|
|
|
|
|
|
return (
|
2026-08-31 13:59:46 -07:00
|
|
|
<div className="relative w-full max-w-5xl mx-auto">
|
|
|
|
|
{/* Full hire-me image */}
|
|
|
|
|
<div className="relative w-full">
|
|
|
|
|
<Image
|
|
|
|
|
src={isDark ? '/hire-me.png' : '/hire-me-light.png'}
|
|
|
|
|
alt="You hire me, you get a bundle"
|
|
|
|
|
width={1200}
|
|
|
|
|
height={800}
|
|
|
|
|
className="w-full h-auto"
|
|
|
|
|
priority
|
2026-08-18 18:33:49 -07:00
|
|
|
/>
|
2026-08-31 13:59:46 -07:00
|
|
|
|
|
|
|
|
{/* Avatar + Rotating text - centered on image */}
|
|
|
|
|
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 z-20 flex flex-col items-center">
|
|
|
|
|
{/* Avatar */}
|
2026-08-18 18:33:49 -07:00
|
|
|
<motion.div
|
2026-08-31 13:59:46 -07:00
|
|
|
initial={{ scale: 0, opacity: 0 }}
|
|
|
|
|
animate={{ scale: 1, opacity: 1 }}
|
|
|
|
|
transition={{ duration: 0.8, delay: 0.2 }}
|
2026-08-18 18:33:49 -07:00
|
|
|
>
|
2026-08-31 13:59:46 -07:00
|
|
|
<Image
|
|
|
|
|
src="/avatar.jpeg"
|
|
|
|
|
alt="Profile"
|
|
|
|
|
width={200}
|
|
|
|
|
height={200}
|
|
|
|
|
className="w-48 h-48 rounded-full object-cover border-4 border-white dark:border-gray-950 shadow-xl"
|
|
|
|
|
/>
|
2026-08-18 18:33:49 -07:00
|
|
|
</motion.div>
|
2026-08-31 13:59:46 -07:00
|
|
|
|
|
|
|
|
{/* Rotating role text */}
|
|
|
|
|
<div className="mt-6 h-16 flex items-center justify-center">
|
|
|
|
|
<AnimatePresence mode="wait">
|
|
|
|
|
<motion.div
|
|
|
|
|
key={index}
|
|
|
|
|
initial={{ opacity: 0, y: 20, scale: 0.95 }}
|
|
|
|
|
animate={{ opacity: 1, y: 0, scale: 1 }}
|
|
|
|
|
exit={{ opacity: 0, y: -20, scale: 0.95 }}
|
|
|
|
|
transition={{ duration: 0.6, ease: 'easeInOut' }}
|
|
|
|
|
className="text-3xl font-bold text-gray-900 dark:text-white text-center flex items-center gap-3 justify-center bg-white/80 dark:bg-gray-900/80 rounded-xl py-3 px-6 backdrop-blur-sm"
|
|
|
|
|
>
|
|
|
|
|
{(() => {
|
|
|
|
|
const Icon = roleIcons[index]
|
|
|
|
|
return <Icon size={32} className="text-blue-600 dark:text-blue-400 flex-shrink-0" />
|
|
|
|
|
})()}
|
|
|
|
|
{roles[index]}
|
|
|
|
|
</motion.div>
|
|
|
|
|
</AnimatePresence>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2026-08-18 18:33:49 -07:00
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
)
|
|
|
|
|
}
|