UI design Updated
This commit is contained in:
@@ -1,101 +1,446 @@
|
||||
"use client";
|
||||
|
||||
import { motion } from "framer-motion";
|
||||
import { Brain, Activity, Search, ShieldCheck } from "lucide-react";
|
||||
|
||||
const features = [
|
||||
{
|
||||
title: "Precision Analysis",
|
||||
description: "Deep learning models trained on millions of clinical cases.",
|
||||
icon: Search
|
||||
},
|
||||
{
|
||||
title: "Real-time Insight",
|
||||
description: "Instantaneous diagnostic suggestions based on symptoms.",
|
||||
icon: Activity
|
||||
},
|
||||
{
|
||||
title: "Validated Models",
|
||||
description: "Clinically validated AI ensuring high accuracy rates.",
|
||||
icon: ShieldCheck
|
||||
}
|
||||
];
|
||||
import { motion, useScroll, useTransform } from "framer-motion";
|
||||
import {
|
||||
Brain,
|
||||
Search,
|
||||
Database,
|
||||
Zap,
|
||||
Shield,
|
||||
TrendingUp,
|
||||
CheckCircle,
|
||||
Activity,
|
||||
Cpu,
|
||||
Network,
|
||||
} from "lucide-react";
|
||||
import { useRef, useEffect, useState } from "react";
|
||||
|
||||
export default function AIDiagnosis() {
|
||||
return (
|
||||
<section id="diagnosis" className="py-24 bg-primary/5 relative">
|
||||
<div className="container mx-auto px-6">
|
||||
<div className="flex flex-col md:flex-row-reverse items-center gap-16">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, x: 50 }}
|
||||
whileInView={{ opacity: 1, x: 0 }}
|
||||
transition={{ duration: 0.8 }}
|
||||
className="flex-1 space-y-8"
|
||||
>
|
||||
<div className="inline-flex items-center gap-2 px-4 py-2 rounded-full bg-secondary/10 border border-secondary/20 text-secondary text-sm font-semibold">
|
||||
<Brain className="w-4 h-4" />
|
||||
<span>Advanced Analytics</span>
|
||||
</div>
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const canvasRef = useRef<HTMLCanvasElement>(null);
|
||||
const [modelLoaded, setModelLoaded] = useState(false);
|
||||
const [scanProgress, setScanProgress] = useState(0);
|
||||
|
||||
<h2 className="text-4xl md:text-5xl font-bold leading-tight">
|
||||
AI-Powered <span className="text-gradient">Diagnosis</span> with Unmatched Precision
|
||||
</h2>
|
||||
const { scrollYProgress } = useScroll({
|
||||
target: containerRef,
|
||||
offset: ["start end", "end start"],
|
||||
});
|
||||
|
||||
<p className="text-lg text-foreground/60 leading-relaxed max-w-xl">
|
||||
Elevate your diagnostic accuracy with Skyheal's clinical brain. Our AI analyzes patient data, medical history, and current symptoms to provide actionable insights.
|
||||
</p>
|
||||
const y = useTransform(scrollYProgress, [0, 0.5], [100, 0]);
|
||||
const opacity = useTransform(scrollYProgress, [0, 0.3], [0, 1]);
|
||||
const scale = useTransform(scrollYProgress, [0, 0.5], [0.9, 1]);
|
||||
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-6">
|
||||
{features.map((f, i) => (
|
||||
<div key={i} className="glass p-6 rounded-2xl group hover:border-primary/50 transition-colors">
|
||||
<f.icon className="w-8 h-8 text-primary mb-4 group-hover:scale-110 transition-transform" />
|
||||
<h3 className="font-bold text-lg mb-2">{f.title}</h3>
|
||||
<p className="text-sm text-foreground/60">{f.description}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</motion.div>
|
||||
// Simulate scan progress
|
||||
useEffect(() => {
|
||||
const interval = setInterval(() => {
|
||||
setScanProgress((prev) => (prev >= 100 ? 0 : prev + 1));
|
||||
}, 50);
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
|
||||
<motion.div
|
||||
initial={{ opacity: 0, scale: 0.8 }}
|
||||
whileInView={{ opacity: 1, scale: 1 }}
|
||||
transition={{ duration: 0.8 }}
|
||||
className="flex-1"
|
||||
>
|
||||
<div className="relative group">
|
||||
<div className="absolute inset-0 bg-secondary/10 rounded-full blur-[100px] animate-pulse-slow" />
|
||||
<div className="relative glass aspect-square rounded-[3rem] overflow-hidden flex items-center justify-center p-12 border-white/5">
|
||||
<div className="absolute inset-0 opacity-20 pointer-events-none">
|
||||
<div className="h-full w-full bg-[radial-gradient(#3b82f6_1px,transparent_1px)] [background-size:20px_20px]" />
|
||||
</div>
|
||||
<motion.div
|
||||
animate={{
|
||||
scale: [1, 1.05, 1],
|
||||
rotate: [0, 5, -5, 0]
|
||||
}}
|
||||
transition={{ repeat: Infinity, duration: 8 }}
|
||||
>
|
||||
<Brain className="w-48 h-48 text-primary drop-shadow-[0_0_80px_rgba(59,130,246,0.5)]" />
|
||||
</motion.div>
|
||||
// Three.js setup
|
||||
useEffect(() => {
|
||||
if (typeof window === "undefined" || !canvasRef.current) return;
|
||||
|
||||
{/* Orbital Elements */}
|
||||
{[...Array(3)].map((_, i) => (
|
||||
<motion.div
|
||||
key={i}
|
||||
animate={{ rotate: 360 }}
|
||||
transition={{ repeat: Infinity, duration: 10 + i * 5, ease: "linear" }}
|
||||
className="absolute border border-white/5 rounded-full"
|
||||
style={{
|
||||
width: `${200 + i * 80}px`,
|
||||
height: `${200 + i * 80}px`
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
let animationId: number | undefined;
|
||||
let scene: any;
|
||||
let camera: any;
|
||||
let renderer: any;
|
||||
let model: any;
|
||||
let controls: any;
|
||||
|
||||
const initThreeJS = async () => {
|
||||
const THREE = await import("three");
|
||||
const { GLTFLoader, OrbitControls } = await import("three-stdlib");
|
||||
|
||||
scene = new THREE.Scene();
|
||||
camera = new THREE.PerspectiveCamera(45, 1, 0.1, 1000);
|
||||
camera.position.set(0, 0, 5);
|
||||
|
||||
renderer = new THREE.WebGLRenderer({
|
||||
canvas: canvasRef.current!,
|
||||
alpha: true,
|
||||
antialias: true,
|
||||
});
|
||||
renderer.setSize(500, 500);
|
||||
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
|
||||
renderer.setClearColor(0x000000, 0);
|
||||
|
||||
// Lighting
|
||||
scene.add(new THREE.AmbientLight(0xffffff, 0.8));
|
||||
|
||||
const dirLight1 = new THREE.DirectionalLight(0x6366f1, 1.2);
|
||||
dirLight1.position.set(5, 5, 5);
|
||||
scene.add(dirLight1);
|
||||
|
||||
const dirLight2 = new THREE.DirectionalLight(0xa855f7, 0.8);
|
||||
dirLight2.position.set(-5, 3, -5);
|
||||
scene.add(dirLight2);
|
||||
|
||||
// Controls
|
||||
controls = new OrbitControls(camera, renderer.domElement);
|
||||
controls.enableDamping = true;
|
||||
controls.dampingFactor = 0.05;
|
||||
controls.enableZoom = false;
|
||||
controls.autoRotate = true;
|
||||
controls.autoRotateSpeed = 1.5;
|
||||
|
||||
// Model loading
|
||||
const loader = new GLTFLoader();
|
||||
const modelURL = "/human_body.glb";
|
||||
|
||||
try {
|
||||
const gltf = await loader.loadAsync(modelURL);
|
||||
model = gltf.scene;
|
||||
|
||||
// Center & scale model
|
||||
const box = new THREE.Box3().setFromObject(model);
|
||||
const center = box.getCenter(new THREE.Vector3());
|
||||
const size = box.getSize(new THREE.Vector3());
|
||||
const maxDim = Math.max(size.x, size.y, size.z);
|
||||
const scaleFactor = 3 / maxDim;
|
||||
|
||||
model.scale.setScalar(scaleFactor);
|
||||
model.position.sub(center.multiplyScalar(scaleFactor));
|
||||
|
||||
// Performance: disable shadows
|
||||
model.traverse((child: any) => {
|
||||
if (child.isMesh) {
|
||||
child.castShadow = false;
|
||||
child.receiveShadow = false;
|
||||
}
|
||||
});
|
||||
|
||||
scene.add(model);
|
||||
setModelLoaded(true);
|
||||
} catch (error) {
|
||||
console.error("Failed to load GLB model → using fallback", error);
|
||||
|
||||
const geometry = new THREE.IcosahedronGeometry(1.5, 2);
|
||||
const material = new THREE.MeshStandardMaterial({
|
||||
color: 0x6366f1,
|
||||
roughness: 0.3,
|
||||
metalness: 0.7,
|
||||
emissive: 0x3b82f6,
|
||||
emissiveIntensity: 0.2,
|
||||
});
|
||||
model = new THREE.Mesh(geometry, material);
|
||||
scene.add(model);
|
||||
setModelLoaded(true);
|
||||
}
|
||||
|
||||
// Animation loop
|
||||
const animate = () => {
|
||||
animationId = requestAnimationFrame(animate);
|
||||
controls.update();
|
||||
if (model) model.rotation.y += 0.002;
|
||||
renderer.render(scene, camera);
|
||||
};
|
||||
|
||||
animate();
|
||||
};
|
||||
|
||||
initThreeJS();
|
||||
|
||||
// Cleanup
|
||||
return () => {
|
||||
if (animationId !== undefined) {
|
||||
cancelAnimationFrame(animationId);
|
||||
}
|
||||
if (controls) controls.dispose();
|
||||
if (renderer) renderer.dispose();
|
||||
if (scene) {
|
||||
scene.traverse((obj: any) => {
|
||||
if (obj.geometry) obj.geometry.dispose();
|
||||
if (obj.material) {
|
||||
if (Array.isArray(obj.material)) {
|
||||
obj.material.forEach((m: any) => m.dispose());
|
||||
} else {
|
||||
obj.material.dispose();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<section
|
||||
ref={containerRef}
|
||||
id="diagnosis"
|
||||
className="relative py-20 md:py-32 lg:py-40 overflow-hidden"
|
||||
>
|
||||
{/* Ambient glows */}
|
||||
<div className="absolute top-1/4 left-0 w-[700px] h-[700px] bg-blue-500/10 rounded-full blur-[150px] animate-pulse" />
|
||||
<div className="absolute bottom-1/4 right-0 w-[600px] h-[600px] bg-purple-500/10 rounded-full blur-[120px]" />
|
||||
|
||||
{/* Subtle grid */}
|
||||
<div
|
||||
className="absolute inset-0 opacity-[0.02]"
|
||||
style={{
|
||||
backgroundImage: `linear-gradient(rgba(255,255,255,0.1) 1px, transparent 1px),
|
||||
linear-gradient(90deg, rgba(255,255,255,0.1) 1px, transparent 1px)`,
|
||||
backgroundSize: "50px 50px",
|
||||
}}
|
||||
/>
|
||||
|
||||
<div className="container relative z-10 mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="grid lg:grid-cols-2 gap-12 lg:gap-16 xl:gap-24 items-center">
|
||||
{/* 3D Viewer Column */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, x: -50 }}
|
||||
whileInView={{ opacity: 1, x: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 1 }}
|
||||
className="relative"
|
||||
>
|
||||
<div className="absolute inset-0 rounded-[3rem] blur-3xl opacity-60" />
|
||||
|
||||
<div className="relative rounded-3xl backdrop-blur-2xl shadow-2xl overflow-hidden">
|
||||
{/* Header */}
|
||||
<div className="relative p-6 border-b border-white/5">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="relative">
|
||||
<div className="w-10 h-10 rounded-xl flex items-center justify-center">
|
||||
<Brain className="w-5 h-5 text-blue-400" />
|
||||
</div>
|
||||
<div className="absolute -top-1 -right-1 w-3 h-3 bg-green-400 rounded-full border-2 border-black animate-pulse" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-sm font-bold text-white">Neural Diagnostic Engine</h3>
|
||||
<p className="text-xs text-zinc-500">Real-time Analysis Active</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="px-3 py-1 rounded-full bg-green-500/10 border border-green-500/20">
|
||||
<span className="text-xs font-semibold text-green-400">Live</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Canvas area */}
|
||||
<div className="relative p-6 sm:p-8">
|
||||
<div className="absolute top-8 left-8 w-16 h-16 border-l-2 border-t-2 border-blue-400/30 rounded-tl-2xl" />
|
||||
<div className="absolute top-8 right-8 w-16 h-16 border-r-2 border-t-2 border-purple-400/30 rounded-tr-2xl" />
|
||||
<div className="absolute bottom-8 left-8 w-16 h-16 border-l-2 border-b-2 border-pink-400/30 rounded-bl-2xl" />
|
||||
<div className="absolute bottom-8 right-8 w-16 h-16 border-r-2 border-b-2 border-cyan-400/30 rounded-br-2xl" />
|
||||
|
||||
<div className="relative aspect-square flex items-center justify-center">
|
||||
<canvas
|
||||
ref={canvasRef}
|
||||
className="w-full h-full max-w-[500px] max-h-[500px] relative z-10"
|
||||
/>
|
||||
|
||||
{!modelLoaded && (
|
||||
<div className="absolute inset-0 flex items-center justify-center">
|
||||
<div className="space-y-4 text-center">
|
||||
<motion.div
|
||||
animate={{ rotate: 360 }}
|
||||
transition={{ duration: 2, repeat: Infinity, ease: "linear" }}
|
||||
>
|
||||
<Brain className="w-16 h-16 text-blue-400 mx-auto" />
|
||||
</motion.div>
|
||||
<p className="text-sm text-zinc-400 font-semibold">
|
||||
Initializing Neural Network...
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{modelLoaded && (
|
||||
<>
|
||||
<motion.div
|
||||
animate={{ y: [-250, 250] }}
|
||||
transition={{ duration: 3, repeat: Infinity, ease: "linear" }}
|
||||
className="absolute inset-x-0 h-px bg-gradient-to-r from-transparent via-blue-400 to-transparent opacity-50"
|
||||
/>
|
||||
<motion.div
|
||||
animate={{ y: [250, -250] }}
|
||||
transition={{ duration: 4, repeat: Infinity, ease: "linear", delay: 1.5 }}
|
||||
className="absolute inset-x-0 h-px bg-gradient-to-r from-transparent via-purple-400 to-transparent opacity-30"
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Status footer */}
|
||||
<div className="p-6 space-y-4 border-t border-white/5">
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center justify-between text-xs">
|
||||
<span className="text-zinc-400 font-semibold">Analysis Progress</span>
|
||||
<span className="text-blue-400 font-bold">{scanProgress}%</span>
|
||||
</div>
|
||||
<div className="h-2 bg-white/5 rounded-full overflow-hidden">
|
||||
<motion.div
|
||||
style={{ width: `${scanProgress}%` }}
|
||||
className="h-full bg-gradient-to-r from-blue-500 via-purple-500 to-pink-500"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-3 gap-3">
|
||||
{[
|
||||
{ icon: Cpu, label: "Processing", value: "98.4%", color: "text-blue-400" },
|
||||
{ icon: Network, label: "Neural Load", value: "76.2%", color: "text-purple-400" },
|
||||
{ icon: Activity, label: "Accuracy", value: "99.8%", color: "text-green-400" },
|
||||
].map((stat, idx) => (
|
||||
<motion.div
|
||||
key={idx}
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
transition={{ delay: idx * 0.1 }}
|
||||
className="p-3 rounded-xl bg-white/[0.03] border border-white/5"
|
||||
>
|
||||
<stat.icon className={`w-4 h-4 ${stat.color} mb-2`} />
|
||||
<p className="text-xs text-zinc-500 mb-1">{stat.label}</p>
|
||||
<p className="text-sm font-bold text-white">{stat.value}</p>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="px-6 pb-6">
|
||||
<p className="text-center text-xs text-zinc-600 font-medium flex items-center justify-center gap-2">
|
||||
<span className="w-1 h-1 rounded-full bg-zinc-600 animate-pulse" />
|
||||
Drag to rotate • Interactive 3D visualization
|
||||
<span className="w-1 h-1 rounded-full bg-zinc-600 animate-pulse" />
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
</motion.div>
|
||||
|
||||
{/* Text / Features Column */}
|
||||
<motion.div
|
||||
style={{ y, opacity, scale }}
|
||||
className="space-y-10 lg:space-y-12"
|
||||
>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.7, delay: 0.1 }}
|
||||
className="space-y-6"
|
||||
>
|
||||
<h2 className="text-4xl sm:text-5xl md:text-6xl lg:text-7xl font-bold leading-[1.1] tracking-tight text-white">
|
||||
Neural Clinical
|
||||
<br />
|
||||
<span className="bg-gradient-to-r from-purple-400 via-pink-400 to-blue-400 bg-clip-text text-transparent">
|
||||
Synthesis
|
||||
</span>
|
||||
</h2>
|
||||
<p className="text-base sm:text-lg lg:text-xl text-zinc-400 leading-relaxed max-w-2xl">
|
||||
Enterprise-grade diagnostic assistance powered by advanced AI. Cross-reference clinical patterns against{" "}
|
||||
<span className="text-white font-semibold">global medical databases</span> with{" "}
|
||||
<span className="text-white font-semibold">sub-second latency</span> and unparalleled accuracy.
|
||||
</p>
|
||||
</motion.div>
|
||||
|
||||
{/* Feature Cards */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.7, delay: 0.2 }}
|
||||
className="grid grid-cols-1 sm:grid-cols-2 gap-4 sm:gap-6"
|
||||
>
|
||||
{[
|
||||
{
|
||||
icon: Search,
|
||||
title: "Pattern Recognition",
|
||||
text: "AI-powered clinical marker identification with zero-bias analysis technology.",
|
||||
color: "from-blue-500 to-cyan-500",
|
||||
iconColor: "text-blue-400",
|
||||
},
|
||||
{
|
||||
icon: Database,
|
||||
title: "Medical Database",
|
||||
text: "Instant access to verified institutional medical models and research.",
|
||||
color: "from-purple-500 to-pink-500",
|
||||
iconColor: "text-purple-400",
|
||||
},
|
||||
].map((item, i) => (
|
||||
<motion.div
|
||||
key={i}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.6, delay: 0.3 + i * 0.1 }}
|
||||
whileHover={{
|
||||
y: -4,
|
||||
transition: { duration: 0.2 },
|
||||
}}
|
||||
className="group p-5 sm:p-6 rounded-2xl bg-white/[0.02] border border-white/5 backdrop-blur-sm hover:bg-white/[0.04] hover:border-white/10 transition-all cursor-pointer"
|
||||
>
|
||||
<div className="space-y-4">
|
||||
<div className="relative inline-block">
|
||||
<div
|
||||
className={`absolute inset-0 bg-gradient-to-br ${item.color} rounded-xl blur-lg opacity-0 group-hover:opacity-50 transition-opacity`}
|
||||
/>
|
||||
<div className="relative w-12 h-12 rounded-xl bg-gradient-to-br from-white/10 to-white/5 border border-white/10 flex items-center justify-center group-hover:border-white/20 transition-colors">
|
||||
<item.icon className={`w-6 h-6 ${item.iconColor}`} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<h3 className="text-lg font-bold text-white group-hover:text-white/90 transition-colors">
|
||||
{item.title}
|
||||
</h3>
|
||||
<p className="text-sm text-zinc-400 leading-relaxed">{item.text}</p>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
))}
|
||||
</motion.div>
|
||||
|
||||
{/* Stats */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.7, delay: 0.4 }}
|
||||
className="grid grid-cols-3 gap-4 sm:gap-6 pt-6"
|
||||
>
|
||||
{[
|
||||
{ icon: Zap, value: "<500ms", label: "Query Time" },
|
||||
{ icon: Shield, value: "100%", label: "Secure" },
|
||||
{ icon: TrendingUp, value: "99.8%", label: "Accuracy" },
|
||||
].map((stat, i) => (
|
||||
<div key={i} className="text-center space-y-2">
|
||||
<div className="flex justify-center">
|
||||
<div className="p-2 rounded-lg bg-white/5 border border-white/10">
|
||||
<stat.icon className="w-4 h-4 sm:w-5 sm:h-5 text-purple-400" />
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-xl sm:text-2xl font-bold text-white">{stat.value}</p>
|
||||
<p className="text-[10px] sm:text-xs text-zinc-500 font-semibold uppercase tracking-wider">
|
||||
{stat.label}
|
||||
</p>
|
||||
</div>
|
||||
))}
|
||||
</motion.div>
|
||||
|
||||
{/* Trust Indicators */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.7, delay: 0.5 }}
|
||||
className="flex flex-wrap gap-3 sm:gap-4 pt-4"
|
||||
>
|
||||
{["FDA Compliant", "HIPAA Certified", "ISO 27001"].map((badge, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="flex items-center gap-2 px-3 sm:px-4 py-2 rounded-full bg-white/[0.03] border border-white/10 backdrop-blur-sm"
|
||||
>
|
||||
<CheckCircle className="w-3 h-3 sm:w-4 sm:h-4 text-green-400" />
|
||||
<span className="text-[10px] sm:text-xs text-zinc-400 font-semibold">{badge}</span>
|
||||
</div>
|
||||
))}
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user