diff --git a/web/public/SLVG.mp3 b/web/public/SLVG.mp3 new file mode 100644 index 0000000..3d56ec2 Binary files /dev/null and b/web/public/SLVG.mp3 differ diff --git a/web/src/app/(root)/page.tsx b/web/src/app/(root)/page.tsx index 55893d5..f5fb725 100644 --- a/web/src/app/(root)/page.tsx +++ b/web/src/app/(root)/page.tsx @@ -30,6 +30,15 @@ export default function Home() { const [timerDone, setTimerDone] = useState(introAlreadyPlayed); + const { homePageHover, setHomePageHover } = useAppStore(); + + const getClass = (id: string) => + `transition-all duration-500 ${ + homePageHover && homePageHover !== id + ? 'blur-sm opacity-40' + : 'blur-0 opacity-100' + }`; + const shouldRunIntro = useMemo(() => { return !introAlreadyPlayed; }, []); @@ -110,15 +119,94 @@ export default function Home() { {!loading && (
- +
setHomePageHover('logo')} + onMouseLeave={() => setHomePageHover(null)} + className={`${getClass('logo')} ${ + homePageHover === 'logo' ? 'scale-110' : 'scale-100' + }`} + > + +
- - - Blogs - - - {session?.user.role === 'admin' && ( +
setHomePageHover('blogs')} + onMouseLeave={() => setHomePageHover(null)} + className={`${getClass('blogs')} ${ + homePageHover === 'blogs' ? 'scale-110' : 'scale-100' + }`} + > + + + Blogs + + +
+ +
setHomePageHover('about')} + onMouseLeave={() => setHomePageHover(null)} + className={`${getClass('about')} ${ + homePageHover === 'about' ? 'scale-110' : 'scale-100' + }`} + > + + + About + + +
+ +
setHomePageHover('theme')} + onMouseLeave={() => setHomePageHover(null)} + className={`${getClass('theme')} ${ + homePageHover === 'theme' ? 'scale-110' : 'scale-100' + }`} + > + + + +
+ +
setHomePageHover('signin')} + onMouseLeave={() => setHomePageHover(null)} + className={`${getClass('signin')} ${ + homePageHover === 'signin' ? 'scale-110' : 'scale-100' + }`} + > + + + +
+
+
+ {session && ( +
setHomePageHover('signin')} + onMouseLeave={() => setHomePageHover(null)} + className={`${getClass('signin')} ${ + homePageHover === 'signin' ? 'scale-110' : 'scale-100' + }`} + > +

+ Logged in as {session.user.name} +

+
+ )} +
setHomePageHover('admin')} + onMouseLeave={() => setHomePageHover(null)} + className={`${getClass('admin')} ${ + homePageHover === 'admin' ? 'scale-110' : 'scale-100' + }`} + > - )} - - - - About - - - - - - - - - - - - {/* - - */} +
- {session && ( -

- Logged in as {session.user.name} -

- )}
-

- -
- tech blogs 2026 -
-
- -
- Coding -
-
- -
- Logs -
-
+

+
setHomePageHover('tech')} + onMouseLeave={() => setHomePageHover(null)} + className={`${getClass('tech')} ${ + homePageHover === 'tech' ? 'scale-110' : 'scale-100' + }`} + > + +
+ tech blogs 2026 +
+
+
+ +
setHomePageHover('coding')} + onMouseLeave={() => setHomePageHover(null)} + className={`${getClass('coding')} ${ + homePageHover === 'coding' ? 'scale-110' : 'scale-100' + }`} + > + +
+ Coding +
+
+
+ +
setHomePageHover('logs')} + onMouseLeave={() => setHomePageHover(null)} + className={`${getClass('logs')} ${ + homePageHover === 'logs' ? 'scale-110' : 'scale-100' + }`} + > + +
+ Logs +
+
+

)} diff --git a/web/src/app/about/components/Footer.tsx b/web/src/app/about/components/Footer.tsx index 405886a..615eb09 100644 --- a/web/src/app/about/components/Footer.tsx +++ b/web/src/app/about/components/Footer.tsx @@ -44,7 +44,7 @@ export function Footer() { Ready to
collaborate?

- I'm currently available for freelance projects and exciting new + I'm currently available for freelance projects and exciting new opportunities.

diff --git a/web/src/app/about/components/JourneyTimeline.tsx b/web/src/app/about/components/JourneyTimeline.tsx index 226e4e8..9204edd 100644 --- a/web/src/app/about/components/JourneyTimeline.tsx +++ b/web/src/app/about/components/JourneyTimeline.tsx @@ -57,7 +57,6 @@ export function JourneyTimeline() {
- {/* Timeline Item 1 */}
{ timelineNodesRef.current[0] = el; @@ -70,21 +69,18 @@ export function JourneyTimeline() { 2024 — PRESENT - - Senior Frontend Engineer - + Lorem Ipsum Title

- Tech Innovations Inc. + Lorem Company Name

- Leading the frontend architecture for next-generation web - applications. Spearheading the migration to Next.js 14 and - implementing complex WebGL features for immersive user experiences. + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do + eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim + ad minim veniam.

- {/* Timeline Item 2 */}
{ timelineNodesRef.current[1] = el; @@ -94,22 +90,19 @@ export function JourneyTimeline() {
- 2021 — 2024 + 2022 — 2024 - Full Stack Developer + Lorem Ipsum Position
-

- Creative Digital Agency -

+

Ipsum Studio

- Developed and maintained multiple high-traffic e-commerce platforms. - Specialized in creating fluid animations using GSAP and building - robust scalable backends with Node.js. + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do + eiusmod tempor incididunt ut labore et dolore magna aliqua. Duis + aute irure dolor in reprehenderit.

- {/* Timeline Item 3 */}
{ timelineNodesRef.current[2] = el; @@ -119,16 +112,18 @@ export function JourneyTimeline() {
- 2019 — 2021 + 2020 — 2022 - Junior Web Developer + Dolor Sit Amet
-

Startup Studio

+

+ Placeholder Organization +

- Began professional journey building responsive landing pages, - optimizing web performance, and learning the fundamentals of UI/UX - design. + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Excepteur + sint occaecat cupidatat non proident, sunt in culpa qui officia + deserunt.

diff --git a/web/src/app/about/components/ScrollMarquee.tsx b/web/src/app/about/components/ScrollMarquee.tsx index 3e6e841..6078be3 100644 --- a/web/src/app/about/components/ScrollMarquee.tsx +++ b/web/src/app/about/components/ScrollMarquee.tsx @@ -4,9 +4,9 @@ import { useEffect, useRef } from 'react'; interface ScrollMarqueeProps { text: string; - direction?: -1 | 1; // Base direction: -1 for left, 1 for right - baseSpeed?: number; // Speed of continuous movement - scrollSpeed?: number; // Additional speed multiplier when scrolling + direction?: -1 | 1; + baseSpeed?: number; + scrollSpeed?: number; className?: string; textClassName?: string; } @@ -26,11 +26,11 @@ export function ScrollMarquee({ let animationFrameId: number; let lastScrollY = window.scrollY; let currentX = 0; - let currentDirection = direction; + let currentDirection: -1 | 1 = direction; - // Delay initialization slightly to ensure fonts load and widths are accurate const initId = setTimeout(() => { let singleBlockWidth = 0; + if (textRef.current && textRef.current.children.length > 0) { singleBlockWidth = (textRef.current.children[0] as HTMLElement) .offsetWidth; @@ -42,20 +42,17 @@ export function ScrollMarquee({ const currentScrollY = window.scrollY; const scrollDelta = currentScrollY - lastScrollY; - // Change continuous direction based on scroll direction if (scrollDelta > 0) { - currentDirection = direction; // Scroll down uses base direction + currentDirection = direction; } else if (scrollDelta < 0) { - currentDirection = -direction; // Scroll up uses opposite direction + currentDirection = -direction as -1 | 1; } lastScrollY = currentScrollY; - // Move by base speed + extra speed from scroll delta currentX += (baseSpeed + Math.abs(scrollDelta) * scrollSpeed) * currentDirection; - // Seamless infinite loop logic if (singleBlockWidth > 0) { if (currentX <= -singleBlockWidth) { currentX += singleBlockWidth; @@ -64,7 +61,7 @@ export function ScrollMarquee({ } } - textRef.current.style.transform = `translate3d(${currentX}px, 0, 0)`; + textRef.current.style.transform = `translate3d(${currentX}px,0,0)`; animationFrameId = requestAnimationFrame(loop); }; @@ -73,11 +70,13 @@ export function ScrollMarquee({ return () => { clearTimeout(initId); - if (animationFrameId) cancelAnimationFrame(animationFrameId); + + if (animationFrameId) { + cancelAnimationFrame(animationFrameId); + } }; }, [direction, baseSpeed, scrollSpeed]); - // Create enough text to fill the screen once const repeatedText = Array(15).fill(text); return ( @@ -89,14 +88,12 @@ export function ScrollMarquee({ ref={textRef} className={`flex whitespace-nowrap will-change-transform ${textClassName}`} > - {/* Block 1 */}
{repeatedText.map((t, i) => ( {t} ))}
- {/* Block 2 (identical clone for seamless looping) */}
{repeatedText.map((t, i) => ( {t} diff --git a/web/src/components/global-music.tsx b/web/src/components/global-music.tsx index 7adddd0..94b51ef 100644 --- a/web/src/components/global-music.tsx +++ b/web/src/components/global-music.tsx @@ -14,7 +14,7 @@ export const GlobalMusic = () => { const loading = useAppStore((s) => s.loading); useEffect(() => { - const audio = new Audio('/Nico on the Beat.mp3'); + const audio = new Audio('/SLVG.mp3'); audio.loop = true; @@ -37,9 +37,6 @@ export const GlobalMusic = () => { } }, [musicEnabled]); - /* - * hide during loader - */ if (loading) return null; return ( diff --git a/web/src/components/model/displacement-sphere.tsx b/web/src/components/model/displacement-sphere.tsx index 52a06c5..d2e4956 100644 --- a/web/src/components/model/displacement-sphere.tsx +++ b/web/src/components/model/displacement-sphere.tsx @@ -200,7 +200,7 @@ export const DisplacementSphere = ({ const ambientLight = new AmbientLight( 0xffffff, - theme === 'dark' ? 1.2 : 0.2 + theme === 'dark' ? 1.2 : 0.4 ); dirLight.position.z = 200; diff --git a/web/src/store/app-store.ts b/web/src/store/app-store.ts index eb7e599..6318e0e 100644 --- a/web/src/store/app-store.ts +++ b/web/src/store/app-store.ts @@ -15,6 +15,9 @@ type AppStore = { musicEnabled: boolean; setMusicEnabled: (value: boolean) => void; + homePageHover: string | null; + setHomePageHover: (value: string | null) => void; + // Route Reveal revealOpen: boolean; revealName: string; @@ -45,6 +48,9 @@ export const useAppStore = create((set) => ({ musicEnabled: value, }), + homePageHover: null, + setHomePageHover: (value) => set({ homePageHover: value }), + // Reveal state revealOpen: false, revealName: '',