@@ -4,6 +4,7 @@ import { useState, useEffect, useCallback, useRef } from 'react';
44import Link from 'next/link' ;
55import { motion , AnimatePresence } from 'framer-motion' ;
66import { FiArrowRight , FiMoon , FiSun , FiCheck , FiGithub , FiLinkedin , FiDownload } from 'react-icons/fi' ;
7+ import Footer from '@/components/Footer' ;
78
89const ACCENT = '#BE00B5' ;
910const SECTION_LABELS = [ 'Intro' , 'AI & Chatbots' , 'Accessibility' , 'Connect' ] ;
@@ -20,16 +21,6 @@ function useC() {
2021 } ;
2122}
2223
23- // ─── Section tag ─────────────────────────────────────────────────────────────
24- function Tag ( { n, label, c } ) {
25- return (
26- < p style = { { fontFamily : "'JetBrains Mono', monospace" , fontSize : '0.68rem' , letterSpacing : '0.14em' , color : c . muted , margin : '0 0 2.5rem' , display : 'flex' , gap : '0.5rem' , transition : 'color 0.5s ease' } } >
27- < span style = { { color : ACCENT } } > //</ span >
28- < span > { n } — { label } </ span >
29- </ p >
30- ) ;
31- }
32-
3324// ─── Side nav ─────────────────────────────────────────────────────────────────
3425function SideNav ( { current, total, onGo } ) {
3526 const c = useC ( ) ;
@@ -53,6 +44,38 @@ function SideNav({ current, total, onGo }) {
5344 ) ;
5445}
5546
47+ // ─── Cycling role title ────────────────────────────────────────────────────────
48+ const ROLES = [ 'Full-Stack Developer' , 'AI Builder' , 'React Engineer' , 'Product Builder' ] ;
49+
50+ function CyclingRole ( ) {
51+ const [ text , setText ] = useState ( '' ) ;
52+ const [ roleIdx , setRoleIdx ] = useState ( 0 ) ;
53+ const [ deleting , setDeleting ] = useState ( false ) ;
54+
55+ useEffect ( ( ) => {
56+ const target = ROLES [ roleIdx ] ;
57+ let timeout ;
58+ if ( ! deleting && text === target ) {
59+ timeout = setTimeout ( ( ) => setDeleting ( true ) , 1800 ) ;
60+ } else if ( deleting && text === '' ) {
61+ setDeleting ( false ) ;
62+ setRoleIdx ( i => ( i + 1 ) % ROLES . length ) ;
63+ } else if ( deleting ) {
64+ timeout = setTimeout ( ( ) => setText ( t => t . slice ( 0 , - 1 ) ) , 45 ) ;
65+ } else {
66+ timeout = setTimeout ( ( ) => setText ( target . slice ( 0 , text . length + 1 ) ) , 85 ) ;
67+ }
68+ return ( ) => clearTimeout ( timeout ) ;
69+ } , [ text , deleting , roleIdx ] ) ;
70+
71+ return (
72+ < span >
73+ { text }
74+ < span style = { { display : 'inline-block' , width : '2px' , height : '0.85em' , background : ACCENT , marginLeft : '2px' , verticalAlign : 'middle' , animation : 'blink 1s step-end infinite' } } />
75+ </ span >
76+ ) ;
77+ }
78+
5679// ─── Marquee ──────────────────────────────────────────────────────────────────
5780const TICKER = [ 'React' , 'Next.js' , 'Node.js' , 'TypeScript' , 'AI Chatbots' , 'Accessibility' , 'UX Design' , 'Supabase' , 'REST APIs' , 'React Native' , 'Python' , 'Full-Stack Dev' ] ;
5881
@@ -80,7 +103,7 @@ function IntroSection() {
80103 const c = useC ( ) ;
81104
82105 return (
83- < section style = { { minHeight : '100vh ' , width : '100%' , background : c . bg , color : c . fg , display : 'flex' , flexDirection : 'column' , transition : 'background 0.5s ease, color 0.5s ease' } } >
106+ < section style = { { height : '100% ' , width : '100%' , background : c . bg , color : c . fg , display : 'flex' , flexDirection : 'column' , transition : 'background 0.5s ease, color 0.5s ease' } } >
84107 < div style = { { flex : 1 , display : 'flex' , flexDirection : 'column' , justifyContent : 'center' , padding : '7rem max(2rem, 7vw) 2.5rem' , maxWidth : '1200px' , margin : '0 auto' , width : '100%' } } >
85108
86109 < motion . p
@@ -89,7 +112,7 @@ function IntroSection() {
89112 transition = { { delay : 0.08 , duration : 0.55 } }
90113 style = { { fontFamily : "'JetBrains Mono', monospace" , fontSize : '0.95rem' , color : ACCENT , margin : '0 0 1.25rem' , cursor : 'default' , letterSpacing : '0.04em' } }
91114 >
92- // Full-Stack Developer
115+ // < CyclingRole />
93116 </ motion . p >
94117
95118 < div style = { { overflow : 'hidden' } } >
@@ -133,7 +156,7 @@ function IntroSection() {
133156 </ p >
134157 </ div >
135158
136- { /* Primary CTAs */ }
159+ { /* Primary CTA */ }
137160 < div style = { { display : 'flex' , gap : '0.75rem' , flexWrap : 'wrap' , alignItems : 'center' } } >
138161 < Link
139162 href = "/case-studies/elika-beauty"
@@ -143,45 +166,47 @@ function IntroSection() {
143166 >
144167 View Elika Beauty < FiArrowRight size = { 13 } />
145168 </ Link >
146- < Link
147- href = "/resume"
148- style = { { display : 'inline-flex' , alignItems : 'center' , gap : '0.45rem' , padding : '0.7rem 1.5rem' , background : 'transparent' , color : c . fg , border : `2px solid ${ c . border } ` , borderRadius : '4px' , fontFamily : "'JetBrains Mono', monospace" , fontSize : '0.85rem' , textDecoration : 'none' , transition : 'border-color 0.2s, color 0.5s ease' } }
149- onMouseEnter = { e => { e . currentTarget . style . borderColor = c . fg ; } }
150- onMouseLeave = { e => { e . currentTarget . style . borderColor = c . border ; } }
151- >
152- < FiDownload size = { 13 } /> Download Resume
153- </ Link >
154169 </ div >
155170
156- { /* Social links */ }
157- < div style = { { display : 'flex' , gap : '0.65rem' , flexWrap : 'wrap' } } >
158- < a
159- href = "https://github.com/curleycoder"
160- target = "_blank"
161- rel = "noopener noreferrer"
162- style = { { display : 'inline-flex' , alignItems : 'center' , gap : '0.4rem' , padding : '0.45rem 0.9rem' , border : `1.5px solid ${ c . border } ` , borderRadius : '4px' , color : c . sub , fontFamily : "'JetBrains Mono', monospace" , fontSize : '0.76rem' , textDecoration : 'none' , letterSpacing : '0.06em' , transition : 'border-color 0.2s, color 0.2s' } }
163- onMouseEnter = { e => { e . currentTarget . style . borderColor = ACCENT ; e . currentTarget . style . color = ACCENT ; } }
164- onMouseLeave = { e => { e . currentTarget . style . borderColor = c . border ; e . currentTarget . style . color = c . sub ; } }
165- >
166- < FiGithub size = { 13 } /> GitHub
167- </ a >
168- < a
169- href = "https://linkedin.com/in/shabnam-beiraghian"
170- target = "_blank"
171- rel = "noopener noreferrer"
172- style = { { display : 'inline-flex' , alignItems : 'center' , gap : '0.4rem' , padding : '0.45rem 0.9rem' , border : `1.5px solid ${ c . border } ` , borderRadius : '4px' , color : c . sub , fontFamily : "'JetBrains Mono', monospace" , fontSize : '0.76rem' , textDecoration : 'none' , letterSpacing : '0.06em' , transition : 'border-color 0.2s, color 0.2s' } }
173- onMouseEnter = { e => { e . currentTarget . style . borderColor = ACCENT ; e . currentTarget . style . color = ACCENT ; } }
174- onMouseLeave = { e => { e . currentTarget . style . borderColor = c . border ; e . currentTarget . style . color = c . sub ; } }
175- >
176- < FiLinkedin size = { 13 } /> LinkedIn
177- </ a >
171+ { /* Social links + Download Resume */ }
172+ < div style = { { display : 'flex' , alignItems : 'center' , justifyContent : 'space-between' , gap : '0.65rem' , flexWrap : 'wrap' } } >
173+ < div style = { { display : 'flex' , gap : '0.65rem' , flexWrap : 'wrap' } } >
174+ < a
175+ href = "https://github.com/curleycoder"
176+ target = "_blank"
177+ rel = "noopener noreferrer"
178+ style = { { display : 'inline-flex' , alignItems : 'center' , gap : '0.4rem' , padding : '0.45rem 0.9rem' , border : `1.5px solid ${ c . border } ` , borderRadius : '4px' , color : c . sub , fontFamily : "'JetBrains Mono', monospace" , fontSize : '0.76rem' , textDecoration : 'none' , letterSpacing : '0.06em' , transition : 'border-color 0.2s, color 0.2s' } }
179+ onMouseEnter = { e => { e . currentTarget . style . borderColor = ACCENT ; e . currentTarget . style . color = ACCENT ; } }
180+ onMouseLeave = { e => { e . currentTarget . style . borderColor = c . border ; e . currentTarget . style . color = c . sub ; } }
181+ >
182+ < FiGithub size = { 13 } /> GitHub
183+ </ a >
184+ < a
185+ href = "https://linkedin.com/in/shabnam-beiraghian"
186+ target = "_blank"
187+ rel = "noopener noreferrer"
188+ style = { { display : 'inline-flex' , alignItems : 'center' , gap : '0.4rem' , padding : '0.45rem 0.9rem' , border : `1.5px solid ${ c . border } ` , borderRadius : '4px' , color : c . sub , fontFamily : "'JetBrains Mono', monospace" , fontSize : '0.76rem' , textDecoration : 'none' , letterSpacing : '0.06em' , transition : 'border-color 0.2s, color 0.2s' } }
189+ onMouseEnter = { e => { e . currentTarget . style . borderColor = ACCENT ; e . currentTarget . style . color = ACCENT ; } }
190+ onMouseLeave = { e => { e . currentTarget . style . borderColor = c . border ; e . currentTarget . style . color = c . sub ; } }
191+ >
192+ < FiLinkedin size = { 13 } /> LinkedIn
193+ </ a >
194+ < Link
195+ href = "/projects"
196+ style = { { display : 'inline-flex' , alignItems : 'center' , gap : '0.4rem' , padding : '0.45rem 0.9rem' , border : `1.5px solid ${ c . border } ` , borderRadius : '4px' , color : c . sub , fontFamily : "'JetBrains Mono', monospace" , fontSize : '0.76rem' , textDecoration : 'none' , letterSpacing : '0.06em' , transition : 'border-color 0.2s, color 0.2s' } }
197+ onMouseEnter = { e => { e . currentTarget . style . borderColor = ACCENT ; e . currentTarget . style . color = ACCENT ; } }
198+ onMouseLeave = { e => { e . currentTarget . style . borderColor = c . border ; e . currentTarget . style . color = c . sub ; } }
199+ >
200+ All projects < FiArrowRight size = { 12 } />
201+ </ Link >
202+ </ div >
178203 < Link
179- href = "/projects "
180- style = { { display : 'inline-flex' , alignItems : 'center' , gap : '0.4rem ' , padding : '0.45rem 0.9rem' , border : `1.5px solid ${ c . border } ` , borderRadius : '4px' , color : c . sub , fontFamily : "'JetBrains Mono', monospace" , fontSize : '0.76rem' , textDecoration : 'none' , letterSpacing : '0.06em' , transition : 'border-color 0.2s, color 0.2s' } }
204+ href = "/resume "
205+ style = { { display : 'inline-flex' , alignItems : 'center' , gap : '0.45rem ' , padding : '0.45rem 0.9rem' , background : 'transparent' , color : c . sub , border : `1.5px solid ${ c . border } ` , borderRadius : '4px' , fontFamily : "'JetBrains Mono', monospace" , fontSize : '0.76rem' , textDecoration : 'none' , letterSpacing : '0.06em' , transition : 'border-color 0.2s, color 0.2s' } }
181206 onMouseEnter = { e => { e . currentTarget . style . borderColor = ACCENT ; e . currentTarget . style . color = ACCENT ; } }
182207 onMouseLeave = { e => { e . currentTarget . style . borderColor = c . border ; e . currentTarget . style . color = c . sub ; } }
183208 >
184- All projects < FiArrowRight size = { 12 } />
209+ < FiDownload size = { 13 } /> Download Resume
185210 </ Link >
186211 </ div >
187212 </ motion . div >
@@ -190,6 +215,7 @@ function IntroSection() {
190215 < motion . div initial = { { opacity : 0 } } animate = { { opacity : 1 } } transition = { { delay : 0.85 , duration : 0.6 } } >
191216 < Marquee />
192217 </ motion . div >
218+ < Footer />
193219 </ section >
194220 ) ;
195221}
@@ -228,7 +254,6 @@ function ChatSection() {
228254 < div className = "chat-grid" style = { { maxWidth : '1100px' , width : '100%' , margin : '0 auto' , display : 'grid' , gridTemplateColumns : '1fr 1fr' , gap : '6rem' , alignItems : 'center' } } >
229255
230256 < div >
231- < Tag n = "02" label = "AI & CHATBOTS" c = { c } />
232257 < h2 style = { { fontFamily : "'JetBrains Mono', monospace" , fontWeight : 800 , fontSize : 'clamp(2rem, 5vw, 4rem)' , lineHeight : 1.05 , color : c . fg , textTransform : 'uppercase' , letterSpacing : '-0.02em' , margin : '0 0 1.75rem' , transition : 'color 0.5s ease' } } >
233258 Embedded< br /> < span style = { { color : ACCENT } } > AI</ span >
234259 </ h2 >
@@ -300,8 +325,6 @@ function A11ySection({ isDark, toggleDark }) {
300325 return (
301326 < section style = { { minHeight : '100vh' , width : '100%' , background : c . bg , color : c . fg , display : 'flex' , alignItems : 'center' , padding : '5rem max(2rem, 7vw)' , transition : 'background 0.5s ease, color 0.5s ease' } } >
302327 < div style = { { maxWidth : '1100px' , width : '100%' , margin : '0 auto' } } >
303- < Tag n = "03" label = "ACCESSIBILITY" c = { c } />
304-
305328 < div style = { { display : 'flex' , alignItems : 'flex-end' , justifyContent : 'space-between' , marginBottom : '3rem' , flexWrap : 'wrap' , gap : '2rem' } } >
306329 < h2 style = { { fontFamily : "'JetBrains Mono', monospace" , fontWeight : 800 , fontSize : 'clamp(2rem, 5.5vw, 4.5rem)' , lineHeight : 1.05 , color : c . fg , textTransform : 'uppercase' , letterSpacing : '-0.02em' , margin : 0 , transition : 'color 0.5s ease' } } >
307330 Built for< br /> < span style = { { color : ACCENT } } > everyone.</ span >
@@ -359,8 +382,6 @@ function ConnectSection() {
359382 return (
360383 < section style = { { minHeight : '100vh' , width : '100%' , background : c . bg , color : c . fg , display : 'flex' , alignItems : 'center' , padding : '5rem max(2rem, 7vw)' , transition : 'background 0.5s ease, color 0.5s ease' } } >
361384 < div style = { { maxWidth : '1100px' , width : '100%' , margin : '0 auto' } } >
362- < Tag n = "04" label = "CONNECT" c = { c } />
363-
364385 < h2 style = { { fontFamily : "'JetBrains Mono', monospace" , fontWeight : 800 , fontSize : 'clamp(2.5rem, 6vw, 5rem)' , lineHeight : 1.05 , color : c . fg , textTransform : 'uppercase' , letterSpacing : '-0.02em' , margin : '0 0 2rem' , transition : 'color 0.5s ease' } } >
365386 Open for< br /> < span style = { { color : ACCENT } } > new roles.</ span >
366387 </ h2 >
@@ -480,7 +501,28 @@ export default function HomePage() {
480501
481502 useEffect ( ( ) => {
482503 document . body . style . overflow = 'hidden' ;
483- return ( ) => { document . body . style . overflow = '' ; } ;
504+ document . documentElement . style . overflow = 'hidden' ;
505+ const globalFooter = document . querySelector ( 'body > main + footer' ) ;
506+ if ( globalFooter ) globalFooter . style . display = 'none' ;
507+ return ( ) => {
508+ document . body . style . overflow = '' ;
509+ document . documentElement . style . overflow = '' ;
510+ if ( globalFooter ) globalFooter . style . display = '' ;
511+ } ;
512+ } , [ ] ) ;
513+
514+ // Auto-advance sections
515+ useEffect ( ( ) => {
516+ const timer = setInterval ( ( ) => {
517+ if ( animating . current ) return ;
518+ setCurrent ( c => {
519+ const next = ( c + 1 ) % SECTIONS . length ;
520+ animating . current = true ;
521+ setTimeout ( ( ) => { animating . current = false ; } , 700 ) ;
522+ return next ;
523+ } ) ;
524+ } , 12000 ) ;
525+ return ( ) => clearInterval ( timer ) ;
484526 } , [ ] ) ;
485527
486528 const Cur = SECTIONS [ current ] ;
@@ -506,25 +548,15 @@ export default function HomePage() {
506548 < SideNav current = { current } total = { SECTIONS . length } onGo = { goTo } />
507549 </ div >
508550
509- { /* Bottom-center label */ }
510- < div style = { { position : 'fixed' , bottom : '3.5rem' , left : '50%' , transform : 'translateX(-50%)' , zIndex : 20 , pointerEvents : 'none' , userSelect : 'none' } } >
511- < AnimatePresence mode = "wait" >
512- < motion . span
513- key = { current }
514- initial = { { opacity : 0 , y : 4 } } animate = { { opacity : 1 , y : 0 } } exit = { { opacity : 0 , y : - 4 } }
515- transition = { { duration : 0.2 } }
516- style = { { display : 'block' , fontFamily : "'JetBrains Mono', monospace" , fontSize : '0.6rem' , letterSpacing : '0.14em' , textTransform : 'uppercase' , color : 'var(--counter-color)' , transition : 'color 0.5s ease' , whiteSpace : 'nowrap' } }
517- >
518- { String ( current + 1 ) . padStart ( 2 , '0' ) } — { SECTION_LABELS [ current ] }
519- </ motion . span >
520- </ AnimatePresence >
521- </ div >
522-
523- < style jsx global > { `
551+ < style jsx global > { `
524552 @keyframes tickerMove {
525553 from { transform: translateX(0); }
526554 to { transform: translateX(-50%); }
527555 }
556+ @keyframes blink {
557+ 0%, 100% { opacity: 1; }
558+ 50% { opacity: 0; }
559+ }
528560 @media (max-width: 760px) {
529561 .chat-grid { grid-template-columns: 1fr !important; gap: 2.5rem !important; }
530562 .fs-grid { grid-template-columns: 1fr !important; gap: 2.5rem !important; }
0 commit comments