jashdoshi77 commited on
Commit
869902a
·
1 Parent(s): 647bf4a

changed achievement section

Browse files
src/app/globals.css CHANGED
@@ -56,8 +56,7 @@ html.lenis body {
56
  .service-card,
57
  .scroll-stack-card,
58
  .timeline-card,
59
- .stat-card,
60
- .award-card {
61
  will-change: transform, opacity;
62
  -webkit-backface-visibility: hidden;
63
  backface-visibility: hidden;
 
56
  .service-card,
57
  .scroll-stack-card,
58
  .timeline-card,
59
+ .text-reveal-row {
 
60
  will-change: transform, opacity;
61
  -webkit-backface-visibility: hidden;
62
  backface-visibility: hidden;
src/app/page.tsx CHANGED
@@ -38,7 +38,7 @@ export default function Home() {
38
  {/* Skills Section */}
39
  <SkillsSection />
40
 
41
- {/* Achievements Section */}
42
  <AchievementsSection />
43
  </main>
44
 
 
38
  {/* Skills Section */}
39
  <SkillsSection />
40
 
41
+ {/* Clients Section */}
42
  <AchievementsSection />
43
  </main>
44
 
src/components/AchievementsSection.tsx CHANGED
@@ -1,446 +1,227 @@
1
  "use client";
2
 
3
- import { useRef, useLayoutEffect, useEffect, useState, useCallback } from "react";
4
- import { motion, useInView } from "framer-motion";
5
- import gsap from "gsap";
6
- import { ScrollTrigger } from "gsap/ScrollTrigger";
7
 
8
- // Register GSAP plugins
9
- if (typeof window !== "undefined") {
10
- gsap.registerPlugin(ScrollTrigger);
11
- }
12
-
13
- interface Achievement {
14
- number: number;
15
- suffix: string;
16
- label: string;
17
- description: string;
18
- }
19
-
20
- interface Award {
21
- icon: React.ReactNode;
22
  title: string;
23
- organization: string;
24
- year: string;
25
  }
26
 
27
- const achievements: Achievement[] = [
28
- { number: 50, suffix: "+", label: "Projects Delivered", description: "Across various industries" },
29
- { number: 30, suffix: "+", label: "Happy Clients", description: "Worldwide satisfaction" },
30
- { number: 15, suffix: "+", label: "Awards Won", description: "Design & development" },
31
- { number: 99, suffix: "%", label: "Client Retention", description: "Long-term partnerships" },
32
- ];
33
-
34
- const awards: Award[] = [
35
  {
36
- icon: (
37
- <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5">
38
- <path d="M12 15l-2 5-1.5-3.5L5 15l3.5-1.5L10 10l2 5zM19 6l-1.5 3.5L14 11l3.5 1.5L19 16l1.5-3.5L24 11l-3.5-1.5L19 6z" />
39
- <path d="M9 2l-1 2.5L5.5 5.5 8 6.5l1 2.5 1-2.5 2.5-1-2.5-1L9 2z" />
40
- </svg>
41
- ),
42
- title: "Best Web Design",
43
- organization: "Awwwards",
44
- year: "2024",
45
  },
46
  {
47
- icon: (
48
- <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5">
49
- <circle cx="12" cy="8" r="6" />
50
- <path d="M15.477 12.89L17 22l-5-3-5 3 1.523-9.11" />
51
- </svg>
52
- ),
53
- title: "Site of the Day",
54
- organization: "CSS Design Awards",
55
- year: "2024",
56
  },
57
  {
58
- icon: (
59
- <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5">
60
- <path d="M6 9H4.5a2.5 2.5 0 010-5C6 4 6 9 6 9zm12 0h1.5a2.5 2.5 0 000-5C18 4 18 9 18 9z" />
61
- <path d="M4 22h16M10 14.66V17c0 1.1-.9 2-2 2H6M14 14.66V17c0 1.1.9 2 2 2h2" />
62
- <path d="M18 9a6 6 0 01-12 0V6a2 2 0 012-2h8a2 2 0 012 2v3z" />
63
- </svg>
64
- ),
65
- title: "Developer Award",
66
- organization: "FWA",
67
- year: "2023",
68
  },
69
  {
70
- icon: (
71
- <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5">
72
- <polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2" />
73
- </svg>
74
- ),
75
- title: "Excellence in UX",
76
- organization: "UX Awards",
77
- year: "2023",
78
  },
79
  ];
80
 
81
- // Animated counter component
82
- function AnimatedCounter({ target, suffix, isInView }: { target: number; suffix: string; isInView: boolean }) {
83
- const [count, setCount] = useState(0);
84
- const countRef = useRef<HTMLSpanElement>(null);
85
-
86
- useEffect(() => {
87
- if (!isInView) return;
88
-
89
- let startTime: number;
90
- const duration = 2000; // 2 seconds
91
-
92
- const animate = (currentTime: number) => {
93
- if (!startTime) startTime = currentTime;
94
- const elapsed = currentTime - startTime;
95
- const progress = Math.min(elapsed / duration, 1);
96
-
97
- // Easing function for smooth animation
98
- const easeOutQuart = 1 - Math.pow(1 - progress, 4);
99
- const currentCount = Math.floor(easeOutQuart * target);
100
-
101
- setCount(currentCount);
102
-
103
- if (progress < 1) {
104
- requestAnimationFrame(animate);
105
- }
106
- };
107
-
108
- requestAnimationFrame(animate);
109
- }, [isInView, target]);
110
-
111
- return (
112
- <span ref={countRef}>
113
- {count}
114
- {suffix}
115
- </span>
116
- );
117
- }
118
 
119
  export default function AchievementsSection() {
120
  const sectionRef = useRef<HTMLElement>(null);
121
- const statsRef = useRef<HTMLDivElement>(null);
122
- const awardsRef = useRef<HTMLDivElement>(null);
123
- const [mounted, setMounted] = useState(false);
124
-
125
- const isInView = useInView(statsRef, { once: true, margin: "-100px" });
126
 
 
127
  useEffect(() => {
128
- setMounted(true);
129
- }, []);
130
-
131
- // 3D tilt effect for award cards
132
- const handleMouseMove = useCallback((e: React.MouseEvent<HTMLDivElement>) => {
133
- const card = e.currentTarget;
134
- const rect = card.getBoundingClientRect();
135
- const x = (e.clientX - rect.left) / rect.width;
136
- const y = (e.clientY - rect.top) / rect.height;
137
-
138
- const tiltX = (y - 0.5) * 10;
139
- const tiltY = (x - 0.5) * -10;
140
-
141
- card.style.transform = `perspective(1000px) rotateX(${tiltX}deg) rotateY(${tiltY}deg) scale3d(1.02, 1.02, 1.02)`;
142
- }, []);
143
-
144
- const handleMouseLeave = useCallback((e: React.MouseEvent<HTMLDivElement>) => {
145
- e.currentTarget.style.transform = "perspective(1000px) rotateX(0deg) rotateY(0deg) scale3d(1, 1, 1)";
146
- }, []);
147
-
148
- useLayoutEffect(() => {
149
- if (!mounted) return;
150
-
151
- const isMobile = window.innerWidth < 768;
152
-
153
- const ctx = gsap.context(() => {
154
- // Header animation
155
- gsap.fromTo(
156
- ".achievements-label",
157
- { opacity: 0, y: 20 },
158
- {
159
- opacity: 1,
160
- y: 0,
161
- duration: 0.8,
162
- ease: "power2.out",
163
- scrollTrigger: {
164
- trigger: sectionRef.current,
165
- start: "top 90%",
166
- end: "top 70%",
167
- scrub: 1,
168
- },
169
- }
170
- );
171
-
172
- // Heading words animation
173
- const words = document.querySelectorAll(".achievements-heading .word");
174
- gsap.fromTo(
175
- words,
176
- { opacity: 0, y: 40, rotateX: -15 },
177
- {
178
- opacity: 1,
179
- y: 0,
180
- rotateX: 0,
181
- duration: 1,
182
- stagger: 0.08,
183
- ease: "power3.out",
184
- scrollTrigger: {
185
- trigger: sectionRef.current,
186
- start: "top 85%",
187
- end: "top 55%",
188
- scrub: 1,
189
- },
190
- }
191
- );
192
-
193
- // Stats cards animation
194
- const statCards = gsap.utils.toArray<HTMLElement>(".stat-card");
195
- statCards.forEach((card, index) => {
196
- gsap.fromTo(
197
- card,
198
- {
199
- opacity: 0,
200
- y: 50,
201
- scale: 0.9,
202
- },
203
- {
204
- opacity: 1,
205
- y: 0,
206
- scale: 1,
207
- duration: 0.8,
208
- ease: "back.out(1.5)",
209
- scrollTrigger: {
210
- trigger: statsRef.current,
211
- start: `top ${85 - index * 5}%`,
212
- end: `top ${60 - index * 5}%`,
213
- scrub: 1,
214
- },
215
- }
216
- );
217
- });
218
-
219
- // Divider line animation
220
- gsap.fromTo(
221
- ".achievements-divider",
222
- { scaleX: 0, transformOrigin: "center center" },
223
- {
224
- scaleX: 1,
225
- duration: 1.2,
226
- ease: "power3.inOut",
227
- scrollTrigger: {
228
- trigger: awardsRef.current,
229
- start: "top 95%",
230
- end: "top 75%",
231
- scrub: 1,
232
- },
233
- }
234
- );
235
-
236
- // Awards section header
237
- gsap.fromTo(
238
- ".awards-header",
239
- { opacity: 0, y: 30 },
240
- {
241
- opacity: 1,
242
- y: 0,
243
- duration: 0.8,
244
- ease: "power2.out",
245
- scrollTrigger: {
246
- trigger: awardsRef.current,
247
- start: "top 85%",
248
- end: "top 65%",
249
- scrub: 1,
250
- },
251
- }
252
- );
253
-
254
- // Award cards staggered animation
255
- const awardCards = gsap.utils.toArray<HTMLElement>(".award-card");
256
- awardCards.forEach((card, index) => {
257
- gsap.fromTo(
258
- card,
259
- {
260
- opacity: 0,
261
- y: 40,
262
- rotateY: -5,
263
- },
264
- {
265
- opacity: 1,
266
- y: 0,
267
- rotateY: 0,
268
- duration: 0.8,
269
- ease: "power3.out",
270
- scrollTrigger: {
271
- trigger: awardsRef.current,
272
- start: `top ${80 - index * 5}%`,
273
- end: `top ${55 - index * 5}%`,
274
- scrub: 1,
275
- },
276
- }
277
- );
278
- });
279
-
280
- // Background orb parallax
281
- if (!isMobile) {
282
- gsap.to(".achievements-orb", {
283
- y: -100,
284
- ease: "none",
285
- scrollTrigger: {
286
- trigger: sectionRef.current,
287
- start: "top bottom",
288
- end: "bottom top",
289
- scrub: true,
290
- },
291
  });
 
292
  }
293
- }, sectionRef);
294
 
295
- return () => ctx.revert();
296
- }, [mounted]);
 
 
297
 
298
- const headingText = "Recognition &";
299
- const headingText2 = "Achievements";
300
 
301
  return (
302
  <section
303
- id="achievements"
304
  ref={sectionRef}
305
- className="relative py-16 md:py-32 px-4 md:px-6 lg:px-8 bg-[#050505] overflow-hidden"
 
 
 
 
 
 
 
 
306
  >
307
- {/* Background orb */}
308
- <div className="achievements-orb absolute top-1/4 right-0 w-[400px] md:w-[600px] h-[400px] md:h-[600px] pointer-events-none opacity-50">
309
- <div className="absolute inset-0 bg-gradient-to-br from-purple-500/10 via-blue-500/5 to-transparent rounded-full blur-3xl" />
310
- </div>
311
-
312
- <div className="max-w-7xl mx-auto relative z-10">
313
- {/* Header */}
314
- <div className="text-center mb-12 md:mb-20">
315
- <p className="achievements-label text-xs md:text-sm font-medium text-white/40 uppercase tracking-widest mb-3 md:mb-4">
316
- Milestones
317
- </p>
318
- <h2 className="achievements-heading text-3xl md:text-4xl lg:text-5xl font-light text-white/90 tracking-tight leading-tight" style={{ perspective: "1000px" }}>
319
- {headingText.split(" ").map((word, i) => (
320
- <span key={i} className="word inline-block mr-[0.25em]" style={{ transformStyle: "preserve-3d" }}>
321
- {word}
322
- </span>
323
- ))}{" "}
324
- <span className="text-white/50">
325
- {headingText2.split(" ").map((word, i) => (
326
- <span key={i} className="word inline-block mr-[0.25em]" style={{ transformStyle: "preserve-3d" }}>
327
- {word}
328
- </span>
329
- ))}
330
- </span>
331
- </h2>
332
- </div>
333
 
334
- {/* Stats Grid */}
335
- <div ref={statsRef} className="grid grid-cols-2 md:grid-cols-4 gap-4 md:gap-6 mb-16 md:mb-24">
336
- {achievements.map((achievement) => (
337
- <motion.div
338
- key={achievement.label}
339
- className="stat-card group relative p-5 md:p-8 bg-white/[0.02] border border-white/5 rounded-xl md:rounded-2xl text-center overflow-hidden"
340
- whileHover={{
341
- scale: 1.03,
342
- borderColor: "rgba(255,255,255,0.15)",
343
- transition: { duration: 0.3 },
 
 
 
 
 
 
 
 
344
  }}
345
  >
346
- {/* Hover glow */}
347
- <div className="absolute inset-0 bg-gradient-to-br from-white/5 via-transparent to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-500" />
348
-
349
- <div className="relative z-10">
350
- {/* Number */}
351
- <p className="text-3xl md:text-5xl lg:text-6xl font-bold text-white/90 mb-2 md:mb-3 tabular-nums">
352
- <AnimatedCounter target={achievement.number} suffix={achievement.suffix} isInView={isInView} />
353
- </p>
354
-
355
- {/* Label */}
356
- <p className="text-sm md:text-base text-white/70 font-medium mb-1">
357
- {achievement.label}
358
- </p>
359
-
360
- {/* Description */}
361
- <p className="text-xs md:text-sm text-white/40 hidden md:block">
362
- {achievement.description}
363
- </p>
364
- </div>
365
-
366
- {/* Decorative corner */}
367
- <div className="absolute -top-10 -right-10 w-20 h-20 bg-gradient-to-br from-white/10 to-transparent rounded-full blur-2xl opacity-0 group-hover:opacity-100 transition-opacity duration-500" />
368
- </motion.div>
369
- ))}
370
- </div>
371
-
372
- {/* Divider */}
373
- <div className="achievements-divider h-px bg-gradient-to-r from-transparent via-white/20 to-transparent mb-16 md:mb-24" />
374
-
375
- {/* Awards Section */}
376
- <div ref={awardsRef}>
377
- <p className="awards-header text-xs md:text-sm font-medium text-white/40 uppercase tracking-widest mb-8 md:mb-12 text-center">
378
- Awards & Recognition
379
- </p>
380
-
381
- <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4 md:gap-6">
382
- {awards.map((award) => (
383
  <div
384
- key={award.title}
385
- className="award-card group relative p-5 md:p-6 bg-white/[0.02] border border-white/5 rounded-xl md:rounded-2xl overflow-hidden cursor-pointer transition-all duration-300"
386
- onMouseMove={handleMouseMove}
387
- onMouseLeave={handleMouseLeave}
388
  style={{
389
- transformStyle: "preserve-3d",
390
- transition: "transform 0.15s ease-out",
 
 
391
  }}
392
  >
393
- {/* Shimmer effect */}
394
- <div
395
- className="absolute inset-0 opacity-0 group-hover:opacity-100 transition-opacity duration-500"
396
  style={{
397
- background: "linear-gradient(105deg, transparent 40%, rgba(255,255,255,0.05) 50%, transparent 60%)",
 
398
  backgroundSize: "200% 100%",
399
- animation: "shimmer 1.5s infinite",
 
 
 
400
  }}
401
- />
402
-
403
- <div className="relative z-10">
404
- {/* Icon */}
405
- <div className="w-12 h-12 md:w-14 md:h-14 rounded-xl bg-gradient-to-br from-white/10 to-white/5 flex items-center justify-center mb-4 text-white/60 group-hover:text-white/90 transition-colors duration-300">
406
- {award.icon}
407
- </div>
408
-
409
- {/* Title */}
410
- <h4 className="text-base md:text-lg font-medium text-white/90 mb-1 group-hover:text-white transition-colors duration-300">
411
- {award.title}
412
- </h4>
413
-
414
- {/* Organization */}
415
- <p className="text-sm text-white/50 mb-2 group-hover:text-white/60 transition-colors duration-300">
416
- {award.organization}
417
- </p>
418
 
419
- {/* Year badge */}
420
- <span className="inline-block px-2 py-0.5 text-xs bg-white/5 text-white/40 rounded-md border border-white/10 group-hover:border-white/20 group-hover:text-white/60 transition-all duration-300">
421
- {award.year}
422
- </span>
423
- </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
424
 
425
- {/* Glow on hover */}
426
- <div className="absolute inset-0 bg-gradient-to-br from-purple-500/5 to-blue-500/5 opacity-0 group-hover:opacity-100 transition-opacity duration-500 pointer-events-none" />
 
 
 
 
 
 
 
 
 
 
 
 
 
427
  </div>
428
- ))}
429
- </div>
430
- </div>
431
  </div>
432
 
433
- {/* Add shimmer keyframes */}
434
- <style jsx>{`
435
- @keyframes shimmer {
436
- 0% {
437
- background-position: 200% 0;
438
- }
439
- 100% {
440
- background-position: -200% 0;
441
- }
442
- }
443
- `}</style>
 
 
444
  </section>
445
  );
446
  }
 
1
  "use client";
2
 
3
+ import { useRef, useEffect, useState } from "react";
 
 
 
4
 
5
+ interface TextRevealItem {
 
 
 
 
 
 
 
 
 
 
 
 
 
6
  title: string;
7
+ hoverTitle: string;
8
+ description: string;
9
  }
10
 
11
+ const textItems: TextRevealItem[] = [
 
 
 
 
 
 
 
12
  {
13
+ title: "WEB DESIGN",
14
+ hoverTitle: "PIXEL PERFECT",
15
+ description: "Crafting visually stunning interfaces with meticulous attention to detail.",
 
 
 
 
 
 
16
  },
17
  {
18
+ title: "DEVELOPMENT",
19
+ hoverTitle: "FULL STACK",
20
+ description: "Building robust, scalable applications from front to back.",
 
 
 
 
 
 
21
  },
22
  {
23
+ title: "CREATIVE CODE",
24
+ hoverTitle: "INTERACTIVE",
25
+ description: "Blending art and engineering into immersive digital experiences.",
 
 
 
 
 
 
 
26
  },
27
  {
28
+ title: "UI / UX",
29
+ hoverTitle: "USER FIRST",
30
+ description: "Designing intuitive flows that delight and engage users.",
31
+ },
32
+ {
33
+ title: "MOTION",
34
+ hoverTitle: "ANIMATION",
35
+ description: "Bringing interfaces to life with smooth, purposeful animations.",
36
  },
37
  ];
38
 
39
+ const baseFontStyle: React.CSSProperties = {
40
+ fontSize: "clamp(2.5rem, 7.5vw, 8.5rem)",
41
+ fontWeight: 800,
42
+ letterSpacing: "-0.04em",
43
+ lineHeight: 1,
44
+ textTransform: "uppercase",
45
+ margin: 0,
46
+ fontFamily: "var(--font-sans)",
47
+ whiteSpace: "nowrap",
48
+ };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
 
50
  export default function AchievementsSection() {
51
  const sectionRef = useRef<HTMLElement>(null);
52
+ const textRefs = useRef<(HTMLHeadingElement | null)[]>([]);
53
+ const [hoveredIndex, setHoveredIndex] = useState<number | null>(null);
 
 
 
54
 
55
+ // Native scroll-driven color sweep — guaranteed to work
56
  useEffect(() => {
57
+ const section = sectionRef.current;
58
+ if (!section) return;
59
+
60
+ let ticking = false;
61
+
62
+ const onScroll = () => {
63
+ if (!ticking) {
64
+ requestAnimationFrame(() => {
65
+ const rect = section.getBoundingClientRect();
66
+ const vh = window.innerHeight;
67
+
68
+ // Slow sweep: takes ~2 viewport heights of scrolling
69
+ // Progress 0 → 1 mapped across a wider scroll range
70
+ const rawProgress = (vh - rect.top) / (vh * 1.8);
71
+ const progress = Math.max(0, Math.min(1, rawProgress));
72
+
73
+ textRefs.current.forEach((el, i) => {
74
+ if (!el) return;
75
+ // Small stagger per row for gradual cascade
76
+ const rowStart = i * 0.07;
77
+ const rowDuration = 0.6;
78
+ const rowEnd = rowStart + rowDuration;
79
+
80
+ let rowProgress: number;
81
+ if (progress <= rowStart) {
82
+ rowProgress = 0;
83
+ } else if (progress >= rowEnd) {
84
+ rowProgress = 1;
85
+ } else {
86
+ rowProgress = (progress - rowStart) / rowDuration;
87
+ }
88
+
89
+ // Move background position from 100% (dim) to 0% (bright)
90
+ const bgPos = 100 - rowProgress * 100;
91
+ el.style.backgroundPosition = `${bgPos}% 0%`;
92
+ });
93
+
94
+ ticking = false;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
95
  });
96
+ ticking = true;
97
  }
98
+ };
99
 
100
+ // Listen on window for Lenis scroll events
101
+ window.addEventListener("scroll", onScroll, { passive: true });
102
+ // Also run once on mount
103
+ onScroll();
104
 
105
+ return () => window.removeEventListener("scroll", onScroll);
106
+ }, []);
107
 
108
  return (
109
  <section
110
+ id="clients"
111
  ref={sectionRef}
112
+ className="relative overflow-hidden"
113
+ style={{
114
+ background: "#050505",
115
+ minHeight: "100vh",
116
+ display: "flex",
117
+ flexDirection: "column",
118
+ justifyContent: "center",
119
+ paddingBottom: "15vh",
120
+ }}
121
  >
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
122
 
123
+ {/* Text rows */}
124
+ <div style={{ padding: "0 clamp(20px, 5vw, 80px)" }}>
125
+ {textItems.map((item, index) => {
126
+ const isHovered = hoveredIndex === index;
127
+
128
+ return (
129
+ <div
130
+ key={index}
131
+ className="text-reveal-row"
132
+ onMouseEnter={() => setHoveredIndex(index)}
133
+ onMouseLeave={() => setHoveredIndex(null)}
134
+ style={{
135
+ position: "relative",
136
+ overflow: "hidden",
137
+ borderBottom:
138
+ index < textItems.length - 1
139
+ ? "1px solid rgba(255,255,255,0.06)"
140
+ : "none",
141
  }}
142
  >
143
+ {/* Base text — gradient background-clip, position driven by scroll */}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
144
  <div
 
 
 
 
145
  style={{
146
+ position: "relative",
147
+ padding: "clamp(8px, 1.4vw, 18px) 0",
148
+ display: "flex",
149
+ alignItems: "center",
150
  }}
151
  >
152
+ <h3
153
+ ref={(el) => { textRefs.current[index] = el; }}
 
154
  style={{
155
+ ...baseFontStyle,
156
+ background: "linear-gradient(to right, rgba(255,255,255,0.9) 50%, rgba(255,255,255,0.15) 50%)",
157
  backgroundSize: "200% 100%",
158
+ backgroundPosition: "100% 0%",
159
+ WebkitBackgroundClip: "text",
160
+ WebkitTextFillColor: "transparent",
161
+ backgroundClip: "text",
162
  }}
163
+ >
164
+ {item.title}
165
+ </h3>
166
+ </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
167
 
168
+ {/* Hover reveal layer */}
169
+ <div
170
+ style={{
171
+ position: "absolute",
172
+ inset: 0,
173
+ background: "#ffffff",
174
+ clipPath: isHovered
175
+ ? "inset(0 0 0 0)"
176
+ : "inset(50% 0 50% 0)",
177
+ transition: "clip-path 0.45s cubic-bezier(0.4, 0, 0.2, 1)",
178
+ display: "flex",
179
+ alignItems: "center",
180
+ justifyContent: "space-between",
181
+ padding: `clamp(8px, 1.4vw, 18px) clamp(20px, 5vw, 80px)`,
182
+ gap: "2rem",
183
+ zIndex: 10,
184
+ pointerEvents: "none",
185
+ }}
186
+ >
187
+ <h3 style={{ ...baseFontStyle, color: "#000000" }}>
188
+ {item.hoverTitle}
189
+ </h3>
190
 
191
+ <p
192
+ style={{
193
+ fontSize: "clamp(0.7rem, 1vw, 0.9rem)",
194
+ color: "rgba(0,0,0,0.55)",
195
+ maxWidth: "260px",
196
+ lineHeight: 1.5,
197
+ margin: 0,
198
+ textAlign: "right",
199
+ flexShrink: 0,
200
+ opacity: isHovered ? 1 : 0,
201
+ transition: "opacity 0.3s ease 0.1s",
202
+ }}
203
+ >
204
+ {item.description}
205
+ </p>
206
  </div>
207
+ </div>
208
+ );
209
+ })}
210
  </div>
211
 
212
+ {/* Bottom gradient fade */}
213
+ <div
214
+ style={{
215
+ position: "absolute",
216
+ bottom: 0,
217
+ left: 0,
218
+ right: 0,
219
+ height: "15vh",
220
+ background: "linear-gradient(to bottom, transparent, #050505)",
221
+ pointerEvents: "none",
222
+ zIndex: 5,
223
+ }}
224
+ />
225
  </section>
226
  );
227
  }
src/components/Footer.tsx CHANGED
@@ -69,7 +69,7 @@ export default function Footer() {
69
 
70
  {/* Quick Links */}
71
  <div className="flex flex-wrap items-center gap-4 md:gap-8">
72
- {["About", "Services", "Work", "Skills", "Achievements", "Contact"].map((link) => (
73
  <motion.a
74
  key={link}
75
  href={`#${link.toLowerCase()}`}
 
69
 
70
  {/* Quick Links */}
71
  <div className="flex flex-wrap items-center gap-4 md:gap-8">
72
+ {["About", "Services", "Work", "Skills", "Clients", "Contact"].map((link) => (
73
  <motion.a
74
  key={link}
75
  href={`#${link.toLowerCase()}`}
src/components/Navigation.tsx CHANGED
@@ -9,7 +9,7 @@ const navLinks = [
9
  { name: "Services", href: "#services" },
10
  { name: "Work", href: "#work" },
11
  { name: "Skills", href: "#skills" },
12
- { name: "Achievements", href: "#achievements" },
13
  { name: "Contact", href: "#contact" },
14
  ];
15
 
@@ -26,7 +26,7 @@ export default function Navigation() {
26
  setIsScrolled(window.scrollY > scrollAnimationEnd);
27
 
28
  // Detect active section
29
- const sections = ["about", "services", "work", "skills", "achievements", "contact"];
30
  for (const section of sections.reverse()) {
31
  const element = document.getElementById(section);
32
  if (element && window.scrollY >= element.offsetTop - 200) {
 
9
  { name: "Services", href: "#services" },
10
  { name: "Work", href: "#work" },
11
  { name: "Skills", href: "#skills" },
12
+ { name: "Clients", href: "#clients" },
13
  { name: "Contact", href: "#contact" },
14
  ];
15
 
 
26
  setIsScrolled(window.scrollY > scrollAnimationEnd);
27
 
28
  // Detect active section
29
+ const sections = ["about", "services", "work", "skills", "clients", "contact"];
30
  for (const section of sections.reverse()) {
31
  const element = document.getElementById(section);
32
  if (element && window.scrollY >= element.offsetTop - 200) {