11"use client" ;
22
3- import { memo , useMemo } from "react" ;
3+ import { memo , useEffect , useMemo , useRef , useState } from "react" ;
44import Image from "next/image" ;
55import Link from "next/link" ;
66import { IconLink } from "@/public/assets/svgs" ;
@@ -81,6 +81,28 @@ export const ServicesServiceCard = memo(({ service, onSelect }: Props) => {
8181 [ members ] ,
8282 ) ;
8383
84+ const [ isHovered , setIsHovered ] = useState ( false ) ;
85+ const [ showDescription , setShowDescription ] = useState ( false ) ;
86+ const timerRef = useRef < ReturnType < typeof setTimeout > | null > ( null ) ;
87+
88+ useEffect ( ( ) => {
89+ if ( ! isHovered ) {
90+ setShowDescription ( false ) ;
91+ if ( timerRef . current ) clearTimeout ( timerRef . current ) ;
92+ timerRef . current = null ;
93+ return ;
94+ }
95+
96+ // After 2s on hover, switch to description
97+ timerRef . current = setTimeout ( ( ) => {
98+ setShowDescription ( true ) ;
99+ } , 2000 ) ;
100+
101+ return ( ) => {
102+ if ( timerRef . current ) clearTimeout ( timerRef . current ) ;
103+ } ;
104+ } , [ isHovered ] ) ;
105+
84106 const handleClick = ( ) => {
85107 if ( onSelect ) {
86108 onSelect ( service ) ;
@@ -92,7 +114,12 @@ export const ServicesServiceCard = memo(({ service, onSelect }: Props) => {
92114 return (
93115 < div className = "group cursor-pointer overflow-hidden rounded-xl bg-white/[0.04] ring-1 ring-white/[0.08] transition-all duration-300 hover:bg-white/[0.07] hover:ring-white/[0.16] hover:shadow-[0_8px_30px_rgba(0,0,0,0.3)]" >
94116 { /* Image — consistent 4:3 aspect ratio */ }
95- < div className = "relative aspect-[4/3] w-full overflow-hidden" onClick = { handleClick } >
117+ < div
118+ className = "relative aspect-[4/3] w-full overflow-hidden"
119+ onClick = { handleClick }
120+ onMouseEnter = { ( ) => setIsHovered ( true ) }
121+ onMouseLeave = { ( ) => setIsHovered ( false ) }
122+ >
96123 < Image
97124 src = { imageUrl }
98125 alt = { name }
@@ -102,28 +129,55 @@ export const ServicesServiceCard = memo(({ service, onSelect }: Props) => {
102129 quality = { 75 }
103130 />
104131
105- { /* Hover overlay with action */ }
106- < div className = "absolute inset-0 flex items-center justify-center bg-black/0 transition-all duration-300 group-hover:bg-black/40" >
107- < div className = "flex translate-y-3 items-center gap-2 rounded-full bg-white/90 px-4 py-2 text-sm font-semibold text-gray-900 opacity-0 shadow-xl backdrop-blur-sm transition-all duration-300 group-hover:translate-y-0 group-hover:opacity-100" >
108- < Eye className = "size-4" />
109- View Project
110- </ div >
111- </ div >
112-
113- { /* Category badges — top left */ }
132+ { /* Category badges — dark glass pill */ }
114133 { categories . length > 0 && (
115- < div className = "absolute left-2.5 top-2.5 flex max-w-[calc(100%-1.25rem )] flex-wrap gap-1" >
134+ < div className = "absolute left-3 top-3 flex max-w-[calc(100%-1.5rem )] flex-wrap gap-1.5 " >
116135 { categories . map ( ( category ) => (
117136 < span
118137 key = { category . id }
119- className = "rounded-md bg-black/60 px-2 py-0.5 text-[11px] font-medium text-white backdrop-blur-md "
138+ className = "rounded-md border border-white/10 bg-gray-950/75 px-2.5 py-1 text-[11px] font-semibold text-white/90 "
120139 >
121140 { category . name }
122141 </ span >
123142 ) ) }
124143 </ div >
125144 ) }
126145
146+ { /* Bottom glass band — grows from single-line to 40% max on description */ }
147+ < div
148+ className = "pointer-events-none absolute inset-x-0 bottom-0 border-t border-white/10 bg-gray-950/75 px-4 py-3"
149+ style = { {
150+ opacity : isHovered ? 1 : 0 ,
151+ maxHeight : showDescription ? "40%" : "auto" ,
152+ transition : "opacity 300ms cubic-bezier(0.4,0,0.2,1), max-height 300ms cubic-bezier(0.4,0,0.2,1)" ,
153+ } }
154+ >
155+ { ! showDescription && (
156+ < span
157+ className = "flex items-center gap-2 text-sm font-semibold text-white/90"
158+ style = { {
159+ animation : "fadeSlideIn 300ms cubic-bezier(0.4,0,0.2,1) both" ,
160+ } }
161+ >
162+ < Eye className = "size-4 text-white/60" />
163+ View Project
164+ </ span >
165+ ) }
166+ { showDescription && (
167+ < span
168+ className = "block overflow-hidden text-ellipsis text-[13px] leading-relaxed text-white/80"
169+ style = { {
170+ display : "-webkit-box" ,
171+ WebkitLineClamp : 3 ,
172+ WebkitBoxOrient : "vertical" ,
173+ animation : "fadeSlideIn 300ms cubic-bezier(0.4,0,0.2,1) both" ,
174+ } }
175+ >
176+ { description || "No description available." }
177+ </ span >
178+ ) }
179+ </ div >
180+
127181 { /* Website link — top right */ }
128182 { hasValidWebsite && (
129183 < Link
0 commit comments