@@ -2,15 +2,16 @@ import React from "react";
22import { motion } from "framer-motion" ;
33import { Avatar , AvatarFallback , AvatarImage } from "../ui/avatar" ;
44import { useSafeColorMode } from "../../utils/useSafeColorMode" ;
5- import { ExternalLink , Quote } from "lucide-react" ;
5+
66
77interface TestimonialCardProps {
88 name : string ;
99 username : string ;
1010 content : string ;
1111 date : string ;
1212 avatar : string ;
13- link : string ;
13+ gradient ?: string ;
14+ borderColor ?: string ;
1415}
1516
1617const TestimonialCard : React . FC < TestimonialCardProps > = ( {
@@ -19,122 +20,99 @@ const TestimonialCard: React.FC<TestimonialCardProps> = ({
1920 content,
2021 date,
2122 avatar,
22- link,
23+ gradient,
24+ borderColor,
2325} ) => {
2426 const { colorMode, isDark } = useSafeColorMode ( ) ;
2527
26- const formatLinkDisplay = ( url : string ) => {
27- try {
28- const urlObj = new URL ( url ) ;
29- return urlObj . hostname + urlObj . pathname ;
30- } catch {
31- return url ;
28+ const getBackgroundStyle = ( ) => {
29+ let colorStop = "" ;
30+ if ( gradient === "bg-pink-100" ) {
31+ colorStop = "rgba(244, 194, 214, 0.35)" ; // Pink
32+ } else if ( gradient === "bg-purple-100" ) {
33+ colorStop = "rgba(191, 190, 255, 0.35)" ; // Blue/Lavender
34+ } else {
35+ colorStop = "rgba(165, 243, 252, 0.35)" ; // Cyan
3236 }
37+
38+ return {
39+ background : `
40+ radial-gradient(
41+ ellipse 600px 500px at 10% 85%,
42+ ${ colorStop } 0%,
43+ rgba(255, 255, 255, 0) 70%
44+ ),
45+ linear-gradient(
46+ 135deg,
47+ rgba(255, 255, 255, 0.95) 0%,
48+ rgba(255, 255, 255, 0.9) 100%
49+ )
50+ ` ,
51+ backdropFilter : "blur(4px)" ,
52+ WebkitBackdropFilter : "blur(4px)" ,
53+ border : "1px solid rgba(200, 200, 220, 0.4)" ,
54+ } ;
3355 } ;
3456
3557 return (
3658 < motion . div
3759 initial = { { opacity : 0 , y : 20 } }
3860 animate = { { opacity : 1 , y : 0 } }
3961 exit = { { opacity : 0 , y : - 20 } }
40- whileHover = { { y : - 5 } }
62+ whileHover = { { y : - 8 } }
4163 transition = { { duration : 0.3 } }
42- className = { `group relative h-full overflow-hidden rounded-2xl border backdrop-blur-sm transition-all duration-300 hover:shadow-2xl ${
43- isDark
44- ? "border-gray-700/50 bg-gray-900/80 shadow-xl"
45- : "border-gray-200/50 bg-white/90 shadow-lg"
46- } `}
64+ className = { `group relative h-full w-full overflow-hidden rounded-3xl min-h-[550px] flex flex-col justify-between p-8` }
65+ style = { getBackgroundStyle ( ) }
4766 >
48- { /* Gradient Background */ }
49- < div className = "absolute inset-0 bg-gradient-to-br from-purple-500/5 via-blue-500/5 to-pink-500/5" />
50-
51- { /* Quote Icon */ }
52- < div className = "absolute top-4 right-4 opacity-20" >
53- < Quote size = { 32 } className = "text-purple-500" />
54- </ div >
67+ { /* Subtle glossy overlay */ }
68+ < div
69+ className = "absolute inset-0 rounded-3xl pointer-events-none"
70+ style = { {
71+ background : `linear-gradient(135deg, rgba(255, 255, 255, 0.5) 0%, rgba(255, 255, 255, 0.2) 40%, transparent 70%)` ,
72+ } }
73+ / >
5574
56- < div className = "relative flex h-full flex-col p-6" >
57- { /* Header */ }
58- < div className = "mb-6 flex items-center gap-4" >
59- < div className = "relative shrink-0" >
60- < Avatar className = "h-16 w-16 overflow-hidden border-2 border-gradient-to-r from-purple-500 to-pink-500 bg-white/90 shadow-md" >
61- < AvatarImage src = { avatar } className = "h-full w-full scale-[2.3] object-cover transition-transform duration-500" />
62- < AvatarFallback className = "bg-gradient-to-br from-purple-500 to-pink-500 text-white font-semibold" >
63- { name . charAt ( 0 ) }
64- </ AvatarFallback >
65- </ Avatar >
66- < div className = "absolute bottom-0 right-0 h-4 w-4 rounded-full border-2 border-white bg-green-500 shadow-sm" />
67- </ div >
68- < div className = "flex-1" >
69- < h3 className = { `text-lg font-bold ${
70- isDark ? "text-white" : "text-gray-900"
71- } `} >
72- { name }
73- </ h3 >
74- < p className = { `text-sm ${
75- isDark ? "text-gray-400" : "text-gray-500"
76- } `} >
77- @{ username }
78- </ p >
79- </ div >
80- </ div >
75+ { /* Soft shadow */ }
76+ < div className = "absolute inset-0 rounded-3xl pointer-events-none shadow-lg shadow-black/5" />
8177
82- { /* Content */ }
83- < div className = "flex-1" >
84- < p className = { `text-base leading-relaxed ${
85- isDark ? "text-gray-300" : "text-gray-700"
86- } `} >
87- { content . replace ( / # \w + / g, '' ) . trim ( ) }
78+ < div className = "relative z-10 flex flex-col h-full" >
79+ { /* Testimonial Quote - Top Section */ }
80+ < div className = "mb-12" >
81+ < p className = "text-2xl leading-relaxed font-semibold text-gray-900 tracking-tight" >
82+ "{ content . replace ( / # \w + / g, '' ) . trim ( ) } "
8883 </ p >
8984 </ div >
9085
91- { /* Footer */ }
92- < div className = { `mt-6 space-y-4 border-t pt-4 ${
93- isDark ? "border-gray-700/50" : "border-gray-200/50"
94- } `} >
95- { /* Hashtags */ }
96- < div className = "flex flex-wrap gap-2" >
97- { content . match ( / # \w + / g) ?. map ( ( hashtag , index ) => (
98- < span
99- key = { index }
100- className = { `rounded-full px-3 py-1 text-xs font-medium transition-colors hover:scale-105 ${
101- isDark
102- ? "bg-blue-500/20 text-blue-400 hover:bg-blue-500/30"
103- : "bg-blue-100 text-blue-600 hover:bg-blue-200"
104- } `}
105- >
106- { hashtag }
107- </ span >
108- ) ) }
109- </ div >
86+ { /* Avatar and Info - Bottom Section */ }
87+ < div className = "mt-auto" >
88+ < div className = "flex items-center gap-6" >
89+ { /* Large Avatar with White Background */ }
90+ < Avatar className = "h-20 w-20 overflow-hidden rounded-full border-3 border-white shadow-md flex-shrink-0 bg-white" >
91+ < AvatarImage src = { avatar } className = "h-full w-full object-cover scale-125" />
92+ < AvatarFallback className = "text-white font-bold text-lg bg-gradient-to-br from-purple-400 to-pink-400" >
93+ { name . charAt ( 0 ) }
94+ </ AvatarFallback >
95+ </ Avatar >
96+
97+ < div className = "flex-1" >
98+ < h3 className = "text-xl font-bold text-gray-900 mb-1 tracking-tight" >
99+ { name }
100+ </ h3 >
101+ { username !== "AryanGupta" && username !== "DonaldAnyamba" && (
102+ < p className = "text-sm text-gray-700 font-medium mb-3" >
103+ { username === "VivienChen" ? "Founder @ Toastie (BC Y24)" :
104+ username === "DanielHan" ? "Founder @ Unsloth AI (YC W24, BC Y24)" :
105+ "AI Engineer @ Relevance AI" }
106+ </ p >
107+ ) }
110108
111- { /* Link and Date */ }
112- < div className = "flex items-center justify-between" >
113- < a
114- href = { link }
115- target = "_blank"
116- rel = "noopener noreferrer"
117- className = { `group/link flex items-center gap-2 text-sm font-medium transition-colors ${
118- isDark
119- ? "text-purple-400 hover:text-purple-300"
120- : "text-purple-600 hover:text-purple-700"
121- } `}
122- >
123- < span className = "truncate" > { formatLinkDisplay ( link ) } </ span >
124- < ExternalLink size = { 14 } className = "transition-transform group-hover/link:translate-x-0.5 group-hover/link:-translate-y-0.5" />
125- </ a >
126- < span className = { `text-xs ${
127- isDark ? "text-gray-500" : "text-gray-400"
128- } `} >
129- { date }
130- </ span >
109+ < div className = "flex items-center gap-3 text-xs text-gray-700" >
110+ < span className = "font-medium" > { date } </ span >
111+ </ div >
112+ </ div >
131113 </ div >
132114 </ div >
133115 </ div >
134-
135- { /* Hover Effect Border */ }
136- < div className = "absolute inset-0 rounded-2xl border-2 border-transparent bg-gradient-to-r from-purple-500/20 via-blue-500/20 to-pink-500/20 opacity-0 transition-opacity duration-300 group-hover:opacity-100"
137- style = { { mask : 'linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0)' , maskComposite : 'xor' } } />
138116 </ motion . div >
139117 ) ;
140118} ;
0 commit comments