Files
2026-03-16 21:25:53 +01:00

122 lines
4.5 KiB
TypeScript

'use client';
import { motion, AnimatePresence } from 'framer-motion';
import { useEffect, useState } from 'react';
import Link from 'next/link';
import { personal } from '@/content/personal';
import styles from './Hero.module.scss';
const fadeUp = (delay = 0) => ({
initial: { opacity: 0, y: 28 },
animate: { opacity: 1, y: 0 },
transition: { duration: 0.65, ease: [0.22, 0.61, 0.36, 1], delay },
});
export default function Hero() {
const [roleIndex, setRoleIndex] = useState(0);
useEffect(() => {
const interval = setInterval(() => {
setRoleIndex(i => (i + 1) % personal.roles.length);
}, 2800);
return () => clearInterval(interval);
}, []);
return (
<section className={styles.hero} id="home">
<div className={styles.container}>
<div className={styles.content}>
{/* Availability Badge */}
<motion.div className={styles.badge} {...fadeUp(0)}>
<span className={styles.dot} />
{personal.availabilityText}
</motion.div>
{/* Heading */}
<motion.h1 className={styles.title} {...fadeUp(0.1)}>
Hi, I'm <span className={styles.name}>{personal.name}</span>
</motion.h1>
{/* Animated Role */}
<motion.div className={styles.roleWrapper} {...fadeUp(0.2)}>
<AnimatePresence mode="wait">
<motion.span
key={roleIndex}
className={styles.role}
initial={{ opacity: 0, y: 12 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -12 }}
transition={{ duration: 0.4 }}
>
{personal.roles[roleIndex]}
</motion.span>
</AnimatePresence>
</motion.div>
{/* Bio */}
<motion.p className={styles.bio} {...fadeUp(0.3)}>
{personal.bio}
</motion.p>
{/* CTAs */}
<motion.div className={styles.ctas} {...fadeUp(0.4)}>
<motion.div whileHover={{ scale: 1.03 }} whileTap={{ scale: 0.98 }}>
<Link href="#projects" className={styles.btnPrimary}>
View Projects
</Link>
</motion.div>
<motion.div whileHover={{ scale: 1.03 }} whileTap={{ scale: 0.98 }}>
<Link href="#experience" className={styles.btnSecondary}>
Experience
</Link>
</motion.div>
</motion.div>
</div>
{/* Right side: grid + terminal */}
<motion.div
className={styles.visual}
initial={{ opacity: 0, y: 30, scale: 0.96 }}
animate={{ opacity: 1, y: 0, scale: 1 }}
transition={{ duration: 0.9, ease: [0.22, 0.61, 0.36, 1], delay: 0.25 }}
>
<div className={styles.grid} aria-hidden="true">
<motion.div
className={styles.terminal}
initial={{ opacity: 0, scale: 0.92 }}
animate={{ opacity: 1, scale: 1 }}
transition={{ duration: 0.5, delay: 0.5, ease: [0.22, 0.61, 0.36, 1] }}
>
<div className={styles.terminalHeader}>
<div className={styles.trafficLights}>
<span className={styles.trafficRed} />
<span className={styles.trafficYellow} />
<span className={styles.trafficGreen} />
</div>
<span className={styles.terminalTitle}>zsh — portfolio</span>
</div>
<div className={styles.terminalBody}>
<div className={styles.terminalLine}>
<span className={styles.prompt}>$</span>{' '}
<span className={styles.cmd}>whoami</span>
</div>
<div className={styles.terminalLine}><span className={styles.output}>{personal.name.replace(' ', '-').toLowerCase()}</span></div>
<div className={styles.terminalLine}>
<span className={styles.prompt}>$</span>{' '}
<span className={styles.cmd}>cat role.txt</span>
</div>
<div className={styles.terminalLine}><span className={styles.accentLine}>{personal.role}</span></div>
<div className={styles.terminalLine}>
<span className={styles.prompt}>$</span>{' '}
<span className={styles.cursor} />
</div>
</div>
</motion.div>
</div>
</motion.div>
</div>
</section>
);
}