Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 | 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 62x 62x 62x 62x 62x 62x 62x 62x 62x 61x 61x 61x 61x 62x 62x 62x 61x 61x 61x 61x 61x 61x 62x 62x 62x 62x 62x 62x 62x 61x 61x 61x 62x 62x 62x 61x 61x 61x 61x 61x 61x 61x 61x 61x 61x 61x 61x 61x 61x 61x 61x 61x 61x 61x 61x 61x 61x 61x 61x 61x 61x 61x 61x 61x 61x 61x 61x 61x 61x 61x 61x 61x 61x 61x 61x 61x 61x 61x 61x 61x 61x 61x 61x 61x 61x 61x 61x 61x 61x 61x 61x 61x 61x 61x 61x 61x 244x 244x 244x 244x 244x 244x 244x 244x 244x 244x 244x 244x 244x 244x 244x 732x 732x 732x 732x 732x 732x 244x 244x 244x 244x 244x 244x 244x 244x 244x 61x 61x 61x 61x 61x 61x 244x 244x 244x 244x 244x 61x 183x 244x 244x 244x 61x 61x 61x 61x 61x 61x 61x 61x 61x 61x 61x 61x 61x 61x | 'use client';
import { useState, useEffect, useCallback } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
import Link from 'next/link';
import {
Stethoscope,
Scale,
Palette,
Crown,
ArrowRight,
Shield,
CheckCircle2,
Code2,
PenTool,
GraduationCap,
ChevronLeft,
ChevronRight,
} from 'lucide-react';
import { Button } from '@/components/ui/button';
const professionals = [
{
icon: Stethoscope,
title: 'Doctors',
description:
'Medical licenses, specializations, and credentials verified by official registries.',
features: ['Medical Council Verification', 'Degree Verification', 'CME Credits Tracking'],
color: 'bg-blue-500/10 text-blue-500 border-blue-500/30',
href: '/doctor',
},
{
icon: Scale,
title: 'Lawyers',
description: 'Bar enrollment, court admissions, and legal qualifications verified officially.',
features: ['Bar Council Verification', 'Court Enrollment', 'Practice Area Proof'],
color: 'bg-purple-500/10 text-purple-500 border-purple-500/30',
href: '/lawyer',
},
{
icon: Palette,
title: 'Artists',
description: 'Creative works, exhibitions, and commissions verified by peers and institutions.',
features: ['Portfolio Verification', 'Exhibition Records', 'Commission History'],
color: 'bg-pink-500/10 text-pink-500 border-pink-500/30',
href: '/artist',
},
{
icon: Crown,
title: 'Elite Professionals',
description: 'Invite-only membership for high-profile executives and public figures.',
features: ['Identity Verification', 'Achievement Records', 'Trust Amplification'],
color: 'bg-amber-500/10 text-amber-500 border-amber-500/30',
href: '/elite',
},
{
icon: Code2,
title: 'Developers',
description: 'GitHub contributions, open source projects, and technical skills verified.',
features: ['GitHub Integration', 'Project Showcase', 'Tech Stack Verification'],
color: 'bg-green-500/10 text-green-500 border-green-500/30',
href: '/developer',
},
{
icon: PenTool,
title: 'Designers',
description: 'Design portfolios, client work, and creative achievements showcased beautifully.',
features: ['Portfolio Gallery', 'Client Testimonials', 'Design Awards'],
color: 'bg-cyan-500/10 text-cyan-500 border-cyan-500/30',
href: '/designer',
},
{
icon: GraduationCap,
title: 'Tutors',
description: 'Teaching credentials, student reviews, and expertise areas verified.',
features: ['Qualification Proof', 'Student Reviews', 'Subject Expertise'],
color: 'bg-orange-500/10 text-orange-500 border-orange-500/30',
href: '/tutor',
},
];
export function ProfessionalsSection() {
const [currentIndex, setCurrentIndex] = useState(0);
const [direction, setDirection] = useState(0);
const [isAutoPlaying, setIsAutoPlaying] = useState(true);
const itemsPerView = {
mobile: 1,
tablet: 2,
desktop: 4,
};
const getItemsPerView = useCallback(() => {
if (typeof window === 'undefined') return itemsPerView.desktop;
if (window.innerWidth < 768) return itemsPerView.mobile;
if (window.innerWidth < 1024) return itemsPerView.tablet;
return itemsPerView.desktop;
}, []);
const [visibleItems, setVisibleItems] = useState(itemsPerView.desktop);
useEffect(() => {
const handleResize = () => {
setVisibleItems(getItemsPerView());
};
handleResize();
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, [getItemsPerView]);
const maxIndex = Math.max(0, professionals.length - visibleItems);
const nextSlide = useCallback(() => {
setDirection(1);
setCurrentIndex((prev) => (prev >= maxIndex ? 0 : prev + 1));
}, [maxIndex]);
const prevSlide = useCallback(() => {
setDirection(-1);
setCurrentIndex((prev) => (prev <= 0 ? maxIndex : prev - 1));
}, [maxIndex]);
useEffect(() => {
if (!isAutoPlaying) return;
const interval = setInterval(nextSlide, 4000);
return () => clearInterval(interval);
}, [isAutoPlaying, nextSlide]);
const visibleProfessionals = professionals.slice(currentIndex, currentIndex + visibleItems);
// Handle wrap-around for display
if (visibleProfessionals.length < visibleItems) {
visibleProfessionals.push(
...professionals.slice(0, visibleItems - visibleProfessionals.length)
);
}
const slideVariants = {
enter: (direction: number) => ({
x: direction > 0 ? 100 : -100,
opacity: 0,
}),
center: {
x: 0,
opacity: 1,
},
exit: (direction: number) => ({
x: direction < 0 ? 100 : -100,
opacity: 0,
}),
};
return (
<section className="border-t bg-gradient-to-br from-background via-muted/30 to-background py-20 md:py-28">
<div className="container">
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: '-100px' }}
transition={{ duration: 0.6 }}
className="mx-auto mb-16 max-w-3xl text-center"
>
<span className="mb-4 inline-flex items-center gap-2 rounded-full bg-green-500/10 px-4 py-1.5 text-sm font-medium text-green-600 dark:text-green-400">
<Shield className="h-4 w-4" />
Verified Professionals
</span>
<h2 className="mb-4 text-3xl font-bold md:text-4xl lg:text-5xl">
Trust Built on
<span className="bg-gradient-to-r from-primary to-lime-500 bg-clip-text text-transparent">
{' '}
Proof of Work
</span>
</h2>
<p className="text-lg text-muted-foreground">
For regulated professions, we verify credentials with official registries. Your trust
score reflects real, verifiable achievements.
</p>
</motion.div>
{/* Carousel Container */}
<div
className="relative"
onMouseEnter={() => setIsAutoPlaying(false)}
onMouseLeave={() => setIsAutoPlaying(true)}
>
{/* Navigation Arrows */}
<Button
variant="outline"
size="icon"
className="absolute -left-4 top-1/2 z-10 hidden -translate-y-1/2 rounded-full bg-background/80 backdrop-blur-sm transition-all hover:scale-110 hover:bg-background md:flex"
onClick={prevSlide}
>
<ChevronLeft className="h-5 w-5" />
</Button>
<Button
variant="outline"
size="icon"
className="absolute -right-4 top-1/2 z-10 hidden -translate-y-1/2 rounded-full bg-background/80 backdrop-blur-sm transition-all hover:scale-110 hover:bg-background md:flex"
onClick={nextSlide}
>
<ChevronRight className="h-5 w-5" />
</Button>
{/* Cards Container */}
<div className="overflow-hidden">
<AnimatePresence mode="wait" custom={direction}>
<motion.div
key={currentIndex}
custom={direction}
variants={slideVariants}
initial="enter"
animate="center"
exit="exit"
transition={{
x: { type: 'spring', stiffness: 300, damping: 30 },
opacity: { duration: 0.2 },
}}
className="grid gap-6 md:grid-cols-2 lg:grid-cols-4"
>
{visibleProfessionals.map((professional, index) => (
<motion.div
key={`${professional.title}-${currentIndex}-${index}`}
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: index * 0.1, duration: 0.4 }}
whileHover={{ y: -8, transition: { duration: 0.2 } }}
className="group relative flex h-full flex-col overflow-hidden rounded-2xl border bg-background p-6 transition-shadow duration-300 hover:shadow-xl"
>
<div
className={`mb-4 inline-flex h-14 w-14 items-center justify-center rounded-2xl border ${professional.color} transition-transform duration-300 group-hover:scale-110`}
>
<professional.icon className="h-7 w-7" />
</div>
<h3 className="mb-2 text-xl font-semibold">{professional.title}</h3>
<p className="mb-4 text-sm text-muted-foreground">{professional.description}</p>
<ul className="mb-4 flex-1 space-y-2">
{professional.features.map((feature) => (
<li
key={feature}
className="flex items-center gap-2 text-xs text-muted-foreground"
>
<CheckCircle2 className="h-3 w-3 flex-shrink-0 text-green-500" />
{feature}
</li>
))}
</ul>
<Button variant="ghost" size="sm" className="group/btn mt-auto" asChild>
<Link href={professional.href}>
Learn More
<ArrowRight className="ml-1 h-3 w-3 transition-transform group-hover/btn:translate-x-1" />
</Link>
</Button>
{/* Hover gradient */}
<div className="absolute inset-0 -z-10 bg-gradient-to-br from-primary/0 to-primary/5 opacity-0 transition-opacity duration-300 group-hover:opacity-100" />
</motion.div>
))}
</motion.div>
</AnimatePresence>
</div>
{/* Dots Indicator */}
<div className="mt-8 flex items-center justify-center gap-2">
{Array.from({ length: maxIndex + 1 }).map((_, index) => (
<button
key={index}
onClick={() => {
setDirection(index > currentIndex ? 1 : -1);
setCurrentIndex(index);
}}
className={`h-2 rounded-full transition-all duration-300 ${
index === currentIndex
? 'w-8 bg-primary'
: 'w-2 bg-muted-foreground/30 hover:bg-muted-foreground/50'
}`}
aria-label={`Go to slide ${index + 1}`}
/>
))}
</div>
{/* Mobile Navigation */}
<div className="mt-4 flex justify-center gap-4 md:hidden">
<Button variant="outline" size="sm" onClick={prevSlide}>
<ChevronLeft className="mr-1 h-4 w-4" />
Previous
</Button>
<Button variant="outline" size="sm" onClick={nextSlide}>
Next
<ChevronRight className="ml-1 h-4 w-4" />
</Button>
</div>
</div>
</div>
</section>
);
}
|