|
1 | 1 | "use client"; |
2 | 2 | import { motion } from "framer-motion"; |
3 | 3 | import { FiDownload } from "react-icons/fi"; |
| 4 | +import { |
| 5 | + FaRocket, FaCloud, FaUsers, FaShieldHalved, FaChartColumn, FaStar, |
| 6 | +} from "react-icons/fa6"; |
4 | 7 | import { profile, stats } from "@/data/portfolio"; |
5 | 8 |
|
| 9 | +/* icon map for the stats strip */ |
| 10 | +function statIcon(key) { |
| 11 | + const map = { |
| 12 | + rocket: FaRocket, |
| 13 | + cloud: FaCloud, |
| 14 | + users: FaUsers, |
| 15 | + shield: FaShieldHalved, |
| 16 | + chart: FaChartColumn, |
| 17 | + star: FaStar, |
| 18 | + }; |
| 19 | + return map[key] || FaStar; |
| 20 | +} |
| 21 | + |
6 | 22 | export default function Hero() { |
7 | 23 | return ( |
8 | | - <section className="relative overflow-hidden pt-32 pb-20"> |
| 24 | + <section className="relative overflow-hidden pt-32 pb-16"> |
9 | 25 | <div className="pointer-events-none absolute inset-0 -z-10"> |
10 | 26 | <div className="absolute top-10 left-1/4 w-72 h-72 rounded-full bg-accent/20 blur-3xl animate-blob" /> |
11 | 27 | <div className="absolute top-40 right-1/4 w-80 h-80 rounded-full bg-accent2/20 blur-3xl animate-blob [animation-delay:4s]" /> |
12 | 28 | </div> |
13 | 29 |
|
14 | | - <div className="container grid md:grid-cols-[1.15fr_0.85fr] gap-10 items-center"> |
15 | | - <div> |
16 | | - <motion.h1 |
17 | | - initial={{ opacity: 0, y: 16 }} |
18 | | - animate={{ opacity: 1, y: 0 }} |
19 | | - transition={{ duration: 0.6 }} |
20 | | - className="text-4xl sm:text-6xl font-extrabold leading-[1.05]" |
21 | | - > |
22 | | - Hi, I'm {profile.firstName}{" "} |
23 | | - <span className="inline-block animate-wave origin-[70%_70%]">👋</span> |
24 | | - <br /> |
25 | | - <span className="text-gradient">{profile.role}</span> |
26 | | - </motion.h1> |
27 | | - |
28 | | - <motion.p |
29 | | - initial={{ opacity: 0, y: 16 }} |
30 | | - animate={{ opacity: 1, y: 0 }} |
31 | | - transition={{ duration: 0.6, delay: 0.12 }} |
32 | | - className="mt-5 text-base sm:text-lg text-muted max-w-xl" |
33 | | - > |
34 | | - {profile.tagline} |
35 | | - </motion.p> |
| 30 | + <div className="container"> |
| 31 | + {/* ---------- TOP: text + photo ---------- */} |
| 32 | + <div className="grid md:grid-cols-[1.1fr_0.9fr] gap-10 md:gap-16 lg:gap-24 items-center"> |
| 33 | + <div> |
| 34 | + <motion.h1 |
| 35 | + initial={{ opacity: 0, y: 16 }} |
| 36 | + animate={{ opacity: 1, y: 0 }} |
| 37 | + transition={{ duration: 0.6 }} |
| 38 | + className="text-4xl sm:text-6xl font-extrabold leading-[1.05]" |
| 39 | + > |
| 40 | + Hi, I'm {profile.firstName}{" "} |
| 41 | + <span className="inline-block animate-wave origin-[70%_70%]">👋</span> |
| 42 | + </motion.h1> |
36 | 43 |
|
37 | | - <motion.p |
38 | | - initial={{ opacity: 0, y: 12 }} |
39 | | - animate={{ opacity: 1, y: 0 }} |
40 | | - transition={{ duration: 0.6, delay: 0.2 }} |
41 | | - className="mt-4 text-sm font-mono text-accent" |
42 | | - > |
43 | | - {profile.locationNote} |
44 | | - </motion.p> |
| 44 | + <motion.p |
| 45 | + initial={{ opacity: 0, y: 16 }} |
| 46 | + animate={{ opacity: 1, y: 0 }} |
| 47 | + transition={{ duration: 0.6, delay: 0.08 }} |
| 48 | + className="text-2xl sm:text-3xl font-bold mt-3 text-gradient leading-snug" |
| 49 | + > |
| 50 | + {profile.role} |
| 51 | + </motion.p> |
45 | 52 |
|
46 | | - <motion.div |
47 | | - initial={{ opacity: 0, y: 16 }} |
48 | | - animate={{ opacity: 1, y: 0 }} |
49 | | - transition={{ duration: 0.6, delay: 0.28 }} |
50 | | - className="mt-7 flex flex-wrap gap-4" |
51 | | - > |
52 | | - <a href="#projects" className="px-6 py-3 rounded-full bg-accent text-bg font-semibold hover:opacity-90 transition"> |
53 | | - See my work → |
54 | | - </a> |
55 | | - <a href="#training" className="px-6 py-3 rounded-full border border-line hover:border-accent transition"> |
56 | | - Book training |
57 | | - </a> |
58 | | - {profile.resumeUrl && ( |
59 | | - <a |
60 | | - href={profile.resumeUrl} |
61 | | - target="_blank" |
62 | | - rel="noopener noreferrer" |
63 | | - download="Krishna_Andhe_CV.pdf" |
64 | | - className="px-6 py-3 rounded-full border border-accent text-accent font-semibold flex items-center gap-2 hover:bg-accent hover:text-bg transition" |
| 53 | + {/* handwritten tagline (Style C) */} |
| 54 | + {profile.handwritten && ( |
| 55 | + <motion.div |
| 56 | + initial={{ opacity: 0, y: 10 }} |
| 57 | + animate={{ opacity: 1, y: 0 }} |
| 58 | + transition={{ duration: 0.6, delay: 0.2 }} |
| 59 | + className="relative inline-block mt-4" |
65 | 60 | > |
66 | | - <FiDownload size={18} /> Download Resume |
67 | | - </a> |
| 61 | + <span className="font-hand text-accent text-3xl sm:text-4xl leading-none select-none"> |
| 62 | + {profile.handwritten} |
| 63 | + </span> |
| 64 | + <motion.svg |
| 65 | + viewBox="0 0 300 18" fill="none" preserveAspectRatio="none" |
| 66 | + className="absolute -bottom-2 left-0 w-full h-3 text-accent" aria-hidden="true" |
| 67 | + > |
| 68 | + <motion.path |
| 69 | + d="M3 12 C 60 4, 120 4, 180 9 S 260 14, 297 6" |
| 70 | + stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" fill="none" |
| 71 | + initial={{ pathLength: 0, opacity: 0 }} |
| 72 | + animate={{ pathLength: 1, opacity: 1 }} |
| 73 | + transition={{ duration: 0.9, delay: 0.5, ease: "easeInOut" }} |
| 74 | + /> |
| 75 | + </motion.svg> |
| 76 | + </motion.div> |
68 | 77 | )} |
69 | | - </motion.div> |
70 | 78 |
|
| 79 | + <motion.p |
| 80 | + initial={{ opacity: 0, y: 16 }} |
| 81 | + animate={{ opacity: 1, y: 0 }} |
| 82 | + transition={{ duration: 0.6, delay: 0.3 }} |
| 83 | + className="mt-6 text-base sm:text-lg text-muted max-w-xl" |
| 84 | + > |
| 85 | + {profile.tagline} |
| 86 | + </motion.p> |
| 87 | + |
| 88 | + <motion.p |
| 89 | + initial={{ opacity: 0, y: 12 }} |
| 90 | + animate={{ opacity: 1, y: 0 }} |
| 91 | + transition={{ duration: 0.6, delay: 0.36 }} |
| 92 | + className="mt-4 text-sm font-mono text-accent" |
| 93 | + > |
| 94 | + {profile.locationNote} |
| 95 | + </motion.p> |
| 96 | + |
| 97 | + <motion.div |
| 98 | + initial={{ opacity: 0, y: 16 }} |
| 99 | + animate={{ opacity: 1, y: 0 }} |
| 100 | + transition={{ duration: 0.6, delay: 0.44 }} |
| 101 | + className="mt-7 flex flex-wrap gap-4" |
| 102 | + > |
| 103 | + <a href="#projects" data-hover |
| 104 | + className="px-6 py-3 rounded-full bg-accent text-bg font-semibold hover:opacity-90 transition"> |
| 105 | + See my work → |
| 106 | + </a> |
| 107 | + <a href="#training" data-hover |
| 108 | + className="px-6 py-3 rounded-full border border-line hover:border-accent transition"> |
| 109 | + Book training |
| 110 | + </a> |
| 111 | + {profile.resumeUrl && ( |
| 112 | + <a href={profile.resumeUrl} target="_blank" rel="noopener noreferrer" download="Krishna_Andhe_CV.pdf" data-hover |
| 113 | + className="px-6 py-3 rounded-full border border-accent text-accent font-semibold flex items-center gap-2 hover:bg-accent hover:text-bg transition"> |
| 114 | + <FiDownload size={18} /> Résumé |
| 115 | + </a> |
| 116 | + )} |
| 117 | + </motion.div> |
| 118 | + </div> |
| 119 | + |
| 120 | + {/* photo — squircle */} |
71 | 121 | <motion.div |
72 | | - initial={{ opacity: 0 }} |
73 | | - animate={{ opacity: 1 }} |
74 | | - transition={{ duration: 0.8, delay: 0.42 }} |
75 | | - className="mt-12 grid grid-cols-3 gap-6 max-w-md" |
| 122 | + initial={{ opacity: 0, scale: 0.92 }} |
| 123 | + animate={{ opacity: 1, scale: 1 }} |
| 124 | + transition={{ duration: 0.7, delay: 0.2 }} |
| 125 | + className="relative w-72 sm:w-80 lg:w-96 aspect-[4/5] mx-auto md:justify-self-end md:mr-2 lg:mr-6" |
76 | 126 | > |
77 | | - {stats.map((s) => ( |
78 | | - <div key={s.label}> |
79 | | - <div className="text-2xl sm:text-3xl font-bold text-gradient">{s.value}</div> |
80 | | - <div className="text-sm text-muted">{s.label}</div> |
81 | | - </div> |
82 | | - ))} |
| 127 | + <div className="absolute -inset-3 rounded-[30%] bg-gradient-to-tr from-accent via-transparent to-accent2 blur-md opacity-70 animate-spinSlow" /> |
| 128 | + <div className="relative w-full h-full rounded-[30%]"> |
| 129 | + {/* eslint-disable-next-line @next/next/no-img-element */} |
| 130 | + <img src={profile.photo} alt={profile.name} |
| 131 | + className="w-full h-full rounded-[28%] object-cover object-top bg-surface" /> |
| 132 | + </div> |
83 | 133 | </motion.div> |
84 | 134 | </div> |
85 | 135 |
|
| 136 | + {/* ---------- STATS STRIP (full width, bordered, dividers) ---------- */} |
86 | 137 | <motion.div |
87 | | - initial={{ opacity: 0, scale: 0.92 }} |
88 | | - animate={{ opacity: 1, scale: 1 }} |
89 | | - transition={{ duration: 0.7, delay: 0.2 }} |
90 | | - className="relative w-72 sm:w-80 lg:w-96 aspect-[4/5] mx-auto md:justify-self-end md:mr-2 lg:mr-6" |
| 138 | + initial={{ opacity: 0, y: 24 }} |
| 139 | + animate={{ opacity: 1, y: 0 }} |
| 140 | + transition={{ duration: 0.6, delay: 0.6 }} |
| 141 | + className="mt-14 glass rounded-2xl px-2 sm:px-4 py-5 sm:py-6" |
91 | 142 | > |
92 | | - <div className="absolute -inset-3 rounded-[30%] bg-gradient-to-tr from-accent via-transparent to-accent2 blur-md opacity-60 animate-spinSlow" /> |
93 | | - {/* eslint-disable-next-line @next/next/no-img-element */} |
94 | | - <img |
95 | | - src={profile.photo} |
96 | | - alt={profile.name} |
97 | | - className="relative w-full h-full rounded-[30%] object-cover object-top ring-1 ring-line/50 shadow-2xl shadow-accent/10" |
98 | | - /> |
99 | | - <div/> |
| 143 | + <div className="overflow-x-auto no-scrollbar"> |
| 144 | + <ul className="flex items-center min-w-max sm:min-w-0"> |
| 145 | + {stats.map((s, i) => { |
| 146 | + const Icon = statIcon(s.icon); |
| 147 | + return ( |
| 148 | + <li |
| 149 | + key={s.label} |
| 150 | + className={`flex-1 flex items-center gap-3 sm:gap-4 px-4 sm:px-6 ${ |
| 151 | + i < stats.length - 1 ? "border-r border-line/70" : "" |
| 152 | + }`} |
| 153 | + > |
| 154 | + <motion.span |
| 155 | + initial={{ scale: 0.6, opacity: 0 }} |
| 156 | + animate={{ scale: 1, opacity: 1 }} |
| 157 | + transition={{ delay: 0.7 + i * 0.08, type: "spring", stiffness: 220, damping: 14 }} |
| 158 | + className="shrink-0 text-accent" |
| 159 | + > |
| 160 | + <Icon size={26} /> |
| 161 | + </motion.span> |
| 162 | + <span className="min-w-0"> |
| 163 | + <span className="block text-2xl sm:text-3xl font-extrabold leading-none"> |
| 164 | + {s.value} |
| 165 | + </span> |
| 166 | + <span className="block text-[11px] sm:text-xs text-muted mt-1 whitespace-nowrap"> |
| 167 | + {s.label} |
| 168 | + </span> |
| 169 | + </span> |
| 170 | + </li> |
| 171 | + ); |
| 172 | + })} |
| 173 | + </ul> |
| 174 | + </div> |
100 | 175 | </motion.div> |
101 | 176 | </div> |
102 | 177 | </section> |
|
0 commit comments