122 lines
4.5 KiB
TypeScript
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>
|
|
);
|
|
}
|