diff --git a/landing.html b/landing.html
new file mode 100644
index 0000000000000000000000000000000000000000..542903f3c9dacba155b54bb6ffb2d4441b44e56b
--- /dev/null
+++ b/landing.html
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+ Create professional landing page
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/package.json b/package.json
new file mode 100644
index 0000000000000000000000000000000000000000..48444848920da68b221f5b0e5192b61904e488a0
--- /dev/null
+++ b/package.json
@@ -0,0 +1,60 @@
+
+ {
+ "name": "Create professional landing page",
+ "version": "0.1.0",
+ "private": true,
+ "dependencies": {
+ "@radix-ui/react-accordion": "^1.2.3",
+ "@radix-ui/react-alert-dialog": "^1.1.6",
+ "@radix-ui/react-aspect-ratio": "^1.1.2",
+ "@radix-ui/react-avatar": "^1.1.3",
+ "@radix-ui/react-checkbox": "^1.1.4",
+ "@radix-ui/react-collapsible": "^1.1.3",
+ "@radix-ui/react-context-menu": "^2.2.6",
+ "@radix-ui/react-dialog": "^1.1.6",
+ "@radix-ui/react-dropdown-menu": "^2.1.6",
+ "@radix-ui/react-hover-card": "^1.1.6",
+ "@radix-ui/react-label": "^2.1.2",
+ "@radix-ui/react-menubar": "^1.1.6",
+ "@radix-ui/react-navigation-menu": "^1.2.5",
+ "@radix-ui/react-popover": "^1.1.6",
+ "@radix-ui/react-progress": "^1.1.2",
+ "@radix-ui/react-radio-group": "^1.2.3",
+ "@radix-ui/react-scroll-area": "^1.2.3",
+ "@radix-ui/react-select": "^2.1.6",
+ "@radix-ui/react-separator": "^1.1.2",
+ "@radix-ui/react-slider": "^1.2.3",
+ "@radix-ui/react-slot": "^1.1.2",
+ "@radix-ui/react-switch": "^1.1.3",
+ "@radix-ui/react-tabs": "^1.1.3",
+ "@radix-ui/react-toggle": "^1.1.2",
+ "@radix-ui/react-toggle-group": "^1.1.2",
+ "@radix-ui/react-tooltip": "^1.1.8",
+ "class-variance-authority": "^0.7.1",
+ "clsx": "*",
+ "cmdk": "^1.1.1",
+ "embla-carousel-react": "^8.6.0",
+ "input-otp": "^1.4.2",
+ "lucide-react": "^0.487.0",
+ "motion": "*",
+ "next-themes": "^0.4.6",
+ "react": "^18.3.1",
+ "react-day-picker": "^8.10.1",
+ "react-dom": "^18.3.1",
+ "react-hook-form": "^7.55.0",
+ "react-resizable-panels": "^2.1.7",
+ "recharts": "^2.15.2",
+ "sonner": "^2.0.3",
+ "tailwind-merge": "*",
+ "vaul": "^1.1.2"
+ },
+ "devDependencies": {
+ "@types/node": "^20.10.0",
+ "@vitejs/plugin-react-swc": "^3.10.2",
+ "vite": "6.3.5"
+ },
+ "scripts": {
+ "dev": "vite",
+ "build": "vite build"
+ }
+ }
\ No newline at end of file
diff --git a/src/App.tsx b/src/App.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..8ad15b5e0875daf31dd3920bc6824d6fa57c2439
--- /dev/null
+++ b/src/App.tsx
@@ -0,0 +1,19 @@
+import { Hero } from './components/Hero';
+import { Features } from './components/Features';
+import { HowItWorks } from './components/HowItWorks';
+import { UseCases } from './components/UseCases';
+import { Statistics } from './components/Statistics';
+import { Footer } from './components/Footer';
+
+export default function App() {
+ return (
+
+
+
+
+
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/src/Attributions.md b/src/Attributions.md
new file mode 100644
index 0000000000000000000000000000000000000000..9b7cd4e13487db2e20f4f3844255f0b967db8448
--- /dev/null
+++ b/src/Attributions.md
@@ -0,0 +1,3 @@
+This Figma Make file includes components from [shadcn/ui](https://ui.shadcn.com/) used under [MIT license](https://github.com/shadcn-ui/ui/blob/main/LICENSE.md).
+
+This Figma Make file includes photos from [Unsplash](https://unsplash.com) used under [license](https://unsplash.com/license).
\ No newline at end of file
diff --git a/src/components/Features.tsx b/src/components/Features.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..51cbc1ff12a664c3e388baab7092be2a8a5e7db0
--- /dev/null
+++ b/src/components/Features.tsx
@@ -0,0 +1,86 @@
+import { motion } from 'motion/react';
+import { Zap, Brain, Lock, Clock, BarChart3, Globe } from 'lucide-react';
+
+const features = [
+ {
+ icon: Brain,
+ title: 'AI-Powered Detection',
+ description: 'Advanced neural networks trained on millions of samples to identify even the most sophisticated deepfakes.',
+ color: 'from-blue-500 to-cyan-500',
+ },
+ {
+ icon: Zap,
+ title: 'Real-Time Analysis',
+ description: 'Process videos and images in seconds with our optimized detection algorithms for instant results.',
+ color: 'from-purple-500 to-pink-500',
+ },
+ {
+ icon: Lock,
+ title: 'Secure & Private',
+ description: 'Your content is processed with end-to-end encryption and automatically deleted after analysis.',
+ color: 'from-green-500 to-emerald-500',
+ },
+ {
+ icon: Clock,
+ title: 'Batch Processing',
+ description: 'Upload and analyze multiple files simultaneously to save time and streamline your workflow.',
+ color: 'from-orange-500 to-red-500',
+ },
+ {
+ icon: BarChart3,
+ title: 'Detailed Reports',
+ description: 'Comprehensive analysis with confidence scores, manipulation indicators, and visual heatmaps.',
+ color: 'from-indigo-500 to-purple-500',
+ },
+ {
+ icon: Globe,
+ title: 'API Integration',
+ description: 'Seamlessly integrate deepfake detection into your existing platforms with our RESTful API.',
+ color: 'from-teal-500 to-blue-500',
+ },
+];
+
+export function Features() {
+ return (
+
+
+
+
+ Powerful Features for Complete Protection
+
+
+ Everything you need to detect, analyze, and verify the authenticity of digital media content.
+
+
+
+
+ {features.map((feature, index) => (
+
+
+
+
+
+ {feature.title}
+ {feature.description}
+
+ ))}
+
+
+
+ );
+}
diff --git a/src/components/Footer.tsx b/src/components/Footer.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..5dd1a9ddeb42183f35d119f81ed19e5493e78e73
--- /dev/null
+++ b/src/components/Footer.tsx
@@ -0,0 +1,13 @@
+export function Footer() {
+ return (
+
+
+
+
+ © 2026 DeepTrust. All rights reserved.
+
+
+
+
+ );
+}
diff --git a/src/components/Hero.tsx b/src/components/Hero.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..db0eedd9a75cd79e2aa4046389ab696ac7338dd7
--- /dev/null
+++ b/src/components/Hero.tsx
@@ -0,0 +1,112 @@
+import { motion } from 'motion/react';
+import { Shield, ArrowRight } from 'lucide-react';
+
+export function Hero() {
+ return (
+
+ {/* Navigation */}
+
+
+
+
+ {/* Hero Content */}
+
+
+
+
+
+ Advanced AI Detection Technology
+
+
+ Detect Deepfakes with Precision
+
+
+ Protect your content and brand integrity with cutting-edge AI technology that identifies manipulated media in real-time. Stay ahead of misinformation.
+
+
+
+
+
99.7%
+
Accuracy Rate
+
+
+
500+
+
Images Analyzed
+
+
+
+
+
+
+
+
+
+ {/* Floating cards */}
+
+
+ No manipulation detected
+
+
+
+ 97% confidence level
+
+
+
+
+
+ {/* Background decorative elements */}
+
+
+ );
+}
\ No newline at end of file
diff --git a/src/components/HowItWorks.tsx b/src/components/HowItWorks.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..15e80f00b179f533b5e412670befad0c971aa902
--- /dev/null
+++ b/src/components/HowItWorks.tsx
@@ -0,0 +1,93 @@
+import { motion } from 'motion/react';
+import { Upload, Scan, FileCheck, Download } from 'lucide-react';
+
+const steps = [
+ {
+ icon: Upload,
+ title: 'Upload Media',
+ description: 'Upload your video or image files securely through our platform or API.',
+ step: '01',
+ },
+ {
+ icon: Scan,
+ title: 'AI Analysis',
+ description: 'Our advanced AI models analyze facial features, artifacts, and inconsistencies.',
+ step: '02',
+ },
+ {
+ icon: FileCheck,
+ title: 'Get Results',
+ description: 'Receive detailed reports with confidence scores and manipulation indicators.',
+ step: '03',
+ },
+ {
+ icon: Download,
+ title: 'Export & Share',
+ description: 'Download reports, export data, and share results with your team.',
+ step: '04',
+ },
+];
+
+export function HowItWorks() {
+ return (
+
+
+
+
+ How It Works
+
+
+ Simple, fast, and accurate deepfake detection in just four easy steps.
+
+
+
+
+ {/* Connection lines for desktop */}
+
+
+ {steps.map((step, index) => (
+
+
+
+
+
+
+
+ {step.step}
+
+
+
{step.title}
+
{step.description}
+
+
+ ))}
+
+
+
+
+ Try It Now - It's Free
+
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/src/components/Statistics.tsx b/src/components/Statistics.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..ab1cc4707ca61df9f99b81a44519db7f19ecaa05
--- /dev/null
+++ b/src/components/Statistics.tsx
@@ -0,0 +1,87 @@
+import { motion } from 'motion/react';
+import { TrendingUp, Shield, Clock, Users } from 'lucide-react';
+
+const stats = [
+ {
+ icon: Shield,
+ value: '99.7%',
+ label: 'Detection Accuracy',
+ description: 'Industry-leading precision in identifying deepfakes',
+ },
+ {
+ icon: Users,
+ value: '250+',
+ label: 'Active Users',
+ description: 'Organizations trusting our platform',
+ },
+ {
+ icon: Clock,
+ value: '5K+',
+ label: 'Files Analyzed',
+ description: 'Images scanned to date',
+ },
+];
+
+export function Statistics() {
+ return (
+
+ {/* Background decoration */}
+
+
+
+
+
+ Proven Results at Scale
+
+
+ Numbers that speak to our commitment to excellence and innovation.
+
+
+
+
+ {stats.map((stat, index) => (
+
+
+
+
+ {stat.value}
+ {stat.label}
+ {stat.description}
+
+ ))}
+
+
+
+
+
+
24/7
+
Support Available
+
+
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/src/components/UseCases.tsx b/src/components/UseCases.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..ef7b23d311974fe4c233d85f489e16fa6fd89d2d
--- /dev/null
+++ b/src/components/UseCases.tsx
@@ -0,0 +1,93 @@
+import { motion } from 'motion/react';
+import { Building2, Newspaper, Video, Briefcase, GraduationCap, Users } from 'lucide-react';
+
+const useCases = [
+ {
+ icon: Newspaper,
+ title: 'Media & Journalism',
+ description: 'Verify the authenticity of user-generated content and protect your reputation from fake news.',
+ image: 'https://images.unsplash.com/photo-1634812932028-3baa37d90b52?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHx2aWRlbyUyMHByb2R1Y3Rpb24lMjBwcm9mZXNzaW9uYWx8ZW58MXx8fHwxNzY5NzY3NzQxfDA&ixlib=rb-4.1.0&q=80&w=1080',
+ },
+ {
+ icon: Building2,
+ title: 'Enterprise Security',
+ description: 'Protect your organization from fraudulent content, phishing attempts, and identity theft.',
+ image: 'https://images.unsplash.com/photo-1767972464040-8bfee42d7bed?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxzZWN1cml0eSUyMGRpZ2l0YWwlMjBwcm90ZWN0aW9ufGVufDF8fHx8MTc2OTc5ODQzM3ww&ixlib=rb-4.1.0&q=80&w=1080',
+ },
+ {
+ icon: Video,
+ title: 'Social Media Platforms',
+ description: 'Automatically detect and flag manipulated content to maintain platform integrity.',
+ image: 'https://images.unsplash.com/photo-1610913721979-b20ede600e63?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxkYXRhJTIwYW5hbHlzaXMlMjBkYXNoYm9hcmR8ZW58MXx8fHwxNzY5NzY3MTgxfDA&ixlib=rb-4.1.0&q=80&w=1080',
+ },
+ {
+ icon: Briefcase,
+ title: 'Legal & Compliance',
+ description: 'Authenticate evidence and digital media for legal proceedings and forensic analysis.',
+ image: 'https://images.unsplash.com/photo-1697577418970-95d99b5a55cf?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxhcnRpZmljaWFsJTIwaW50ZWxsaWdlbmNlJTIwdGVjaG5vbG9neXxlbnwxfHx8fDE3Njk3MTAxODd8MA&ixlib=rb-4.1.0&q=80&w=1080',
+ },
+ {
+ icon: GraduationCap,
+ title: 'Education & Research',
+ description: 'Support academic research and teach students about media literacy and deepfake technology.',
+ image: 'https://images.unsplash.com/photo-1634812932028-3baa37d90b52?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHx2aWRlbyUyMHByb2R1Y3Rpb24lMjBwcm9mZXNzaW9uYWx8ZW58MXx8fHwxNzY5NzY3NzQxfDA&ixlib=rb-4.1.0&q=80&w=1080',
+ },
+ {
+ icon: Users,
+ title: 'Government & Public Sector',
+ description: 'Combat misinformation campaigns and protect public figures from malicious deepfakes.',
+ image: 'https://images.unsplash.com/photo-1767972464040-8bfee42d7bed?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxzZWN1cml0eSUyMGRpZ2l0YWwlMjBwcm90ZWN0aW9ufGVufDF8fHx8MTc2OTc5ODQzM3ww&ixlib=rb-4.1.0&q=80&w=1080',
+ },
+];
+
+export function UseCases() {
+ return (
+
+
+
+
+ Trusted Across Industries
+
+
+ Organizations worldwide rely on our technology to protect against deepfakes and maintain content authenticity.
+
+
+
+
+ {useCases.map((useCase, index) => (
+
+
+
+
+
+
+
+
+
+
{useCase.title}
+
{useCase.description}
+
+
+ ))}
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/src/components/figma/ImageWithFallback.tsx b/src/components/figma/ImageWithFallback.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..0e26139bea20f0f26f899190b21161e44510e740
--- /dev/null
+++ b/src/components/figma/ImageWithFallback.tsx
@@ -0,0 +1,27 @@
+import React, { useState } from 'react'
+
+const ERROR_IMG_SRC =
+ 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iODgiIGhlaWdodD0iODgiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgc3Ryb2tlPSIjMDAwIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBvcGFjaXR5PSIuMyIgZmlsbD0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIzLjciPjxyZWN0IHg9IjE2IiB5PSIxNiIgd2lkdGg9IjU2IiBoZWlnaHQ9IjU2IiByeD0iNiIvPjxwYXRoIGQ9Im0xNiA1OCAxNi0xOCAzMiAzMiIvPjxjaXJjbGUgY3g9IjUzIiBjeT0iMzUiIHI9IjciLz48L3N2Zz4KCg=='
+
+export function ImageWithFallback(props: React.ImgHTMLAttributes) {
+ const [didError, setDidError] = useState(false)
+
+ const handleError = () => {
+ setDidError(true)
+ }
+
+ const { src, alt, style, className, ...rest } = props
+
+ return didError ? (
+
+
+
+
+
+ ) : (
+
+ )
+}
diff --git a/src/components/ui/accordion.tsx b/src/components/ui/accordion.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..aa2c37b252a6f4fa36c608f91e09f10740bdbfaa
--- /dev/null
+++ b/src/components/ui/accordion.tsx
@@ -0,0 +1,66 @@
+"use client";
+
+import * as React from "react";
+import * as AccordionPrimitive from "@radix-ui/react-accordion@1.2.3";
+import { ChevronDownIcon } from "lucide-react@0.487.0";
+
+import { cn } from "./utils";
+
+function Accordion({
+ ...props
+}: React.ComponentProps) {
+ return ;
+}
+
+function AccordionItem({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ );
+}
+
+function AccordionTrigger({
+ className,
+ children,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ svg]:rotate-180",
+ className,
+ )}
+ {...props}
+ >
+ {children}
+
+
+
+ );
+}
+
+function AccordionContent({
+ className,
+ children,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ {children}
+
+ );
+}
+
+export { Accordion, AccordionItem, AccordionTrigger, AccordionContent };
diff --git a/src/components/ui/alert-dialog.tsx b/src/components/ui/alert-dialog.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..68f3605cf204b73a0fc98b493900a9569e98c684
--- /dev/null
+++ b/src/components/ui/alert-dialog.tsx
@@ -0,0 +1,157 @@
+"use client";
+
+import * as React from "react";
+import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog@1.1.6";
+
+import { cn } from "./utils";
+import { buttonVariants } from "./button";
+
+function AlertDialog({
+ ...props
+}: React.ComponentProps) {
+ return ;
+}
+
+function AlertDialogTrigger({
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ );
+}
+
+function AlertDialogPortal({
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ );
+}
+
+function AlertDialogOverlay({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ );
+}
+
+function AlertDialogContent({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+
+
+
+ );
+}
+
+function AlertDialogHeader({
+ className,
+ ...props
+}: React.ComponentProps<"div">) {
+ return (
+
+ );
+}
+
+function AlertDialogFooter({
+ className,
+ ...props
+}: React.ComponentProps<"div">) {
+ return (
+
+ );
+}
+
+function AlertDialogTitle({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ );
+}
+
+function AlertDialogDescription({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ );
+}
+
+function AlertDialogAction({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ );
+}
+
+function AlertDialogCancel({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ );
+}
+
+export {
+ AlertDialog,
+ AlertDialogPortal,
+ AlertDialogOverlay,
+ AlertDialogTrigger,
+ AlertDialogContent,
+ AlertDialogHeader,
+ AlertDialogFooter,
+ AlertDialogTitle,
+ AlertDialogDescription,
+ AlertDialogAction,
+ AlertDialogCancel,
+};
diff --git a/src/components/ui/alert.tsx b/src/components/ui/alert.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..856b94db245b98afb9b458567b8f050804179f76
--- /dev/null
+++ b/src/components/ui/alert.tsx
@@ -0,0 +1,66 @@
+import * as React from "react";
+import { cva, type VariantProps } from "class-variance-authority@0.7.1";
+
+import { cn } from "./utils";
+
+const alertVariants = cva(
+ "relative w-full rounded-lg border px-4 py-3 text-sm grid has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] grid-cols-[0_1fr] has-[>svg]:gap-x-3 gap-y-0.5 items-start [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current",
+ {
+ variants: {
+ variant: {
+ default: "bg-card text-card-foreground",
+ destructive:
+ "text-destructive bg-card [&>svg]:text-current *:data-[slot=alert-description]:text-destructive/90",
+ },
+ },
+ defaultVariants: {
+ variant: "default",
+ },
+ },
+);
+
+function Alert({
+ className,
+ variant,
+ ...props
+}: React.ComponentProps<"div"> & VariantProps) {
+ return (
+
+ );
+}
+
+function AlertTitle({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ );
+}
+
+function AlertDescription({
+ className,
+ ...props
+}: React.ComponentProps<"div">) {
+ return (
+
+ );
+}
+
+export { Alert, AlertTitle, AlertDescription };
diff --git a/src/components/ui/aspect-ratio.tsx b/src/components/ui/aspect-ratio.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..2a2f462e2485fed8eed98afc7ea1377d6c5014d8
--- /dev/null
+++ b/src/components/ui/aspect-ratio.tsx
@@ -0,0 +1,11 @@
+"use client";
+
+import * as AspectRatioPrimitive from "@radix-ui/react-aspect-ratio@1.1.2";
+
+function AspectRatio({
+ ...props
+}: React.ComponentProps) {
+ return ;
+}
+
+export { AspectRatio };
diff --git a/src/components/ui/avatar.tsx b/src/components/ui/avatar.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..589b166583f59799d32a83b2e9eec64c542839df
--- /dev/null
+++ b/src/components/ui/avatar.tsx
@@ -0,0 +1,53 @@
+"use client";
+
+import * as React from "react";
+import * as AvatarPrimitive from "@radix-ui/react-avatar@1.1.3";
+
+import { cn } from "./utils";
+
+function Avatar({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ );
+}
+
+function AvatarImage({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ );
+}
+
+function AvatarFallback({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ );
+}
+
+export { Avatar, AvatarImage, AvatarFallback };
diff --git a/src/components/ui/badge.tsx b/src/components/ui/badge.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..3f8eff87fb40c75325cf0b27aa175d3bc1e8a27f
--- /dev/null
+++ b/src/components/ui/badge.tsx
@@ -0,0 +1,46 @@
+import * as React from "react";
+import { Slot } from "@radix-ui/react-slot@1.1.2";
+import { cva, type VariantProps } from "class-variance-authority@0.7.1";
+
+import { cn } from "./utils";
+
+const badgeVariants = cva(
+ "inline-flex items-center justify-center rounded-md border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden",
+ {
+ variants: {
+ variant: {
+ default:
+ "border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90",
+ secondary:
+ "border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90",
+ destructive:
+ "border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
+ outline:
+ "text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
+ },
+ },
+ defaultVariants: {
+ variant: "default",
+ },
+ },
+);
+
+function Badge({
+ className,
+ variant,
+ asChild = false,
+ ...props
+}: React.ComponentProps<"span"> &
+ VariantProps & { asChild?: boolean }) {
+ const Comp = asChild ? Slot : "span";
+
+ return (
+
+ );
+}
+
+export { Badge, badgeVariants };
diff --git a/src/components/ui/breadcrumb.tsx b/src/components/ui/breadcrumb.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..d2adf987870332563506c7aa93346f0c44d958c9
--- /dev/null
+++ b/src/components/ui/breadcrumb.tsx
@@ -0,0 +1,109 @@
+import * as React from "react";
+import { Slot } from "@radix-ui/react-slot@1.1.2";
+import { ChevronRight, MoreHorizontal } from "lucide-react@0.487.0";
+
+import { cn } from "./utils";
+
+function Breadcrumb({ ...props }: React.ComponentProps<"nav">) {
+ return ;
+}
+
+function BreadcrumbList({ className, ...props }: React.ComponentProps<"ol">) {
+ return (
+
+ );
+}
+
+function BreadcrumbItem({ className, ...props }: React.ComponentProps<"li">) {
+ return (
+
+ );
+}
+
+function BreadcrumbLink({
+ asChild,
+ className,
+ ...props
+}: React.ComponentProps<"a"> & {
+ asChild?: boolean;
+}) {
+ const Comp = asChild ? Slot : "a";
+
+ return (
+
+ );
+}
+
+function BreadcrumbPage({ className, ...props }: React.ComponentProps<"span">) {
+ return (
+
+ );
+}
+
+function BreadcrumbSeparator({
+ children,
+ className,
+ ...props
+}: React.ComponentProps<"li">) {
+ return (
+ svg]:size-3.5", className)}
+ {...props}
+ >
+ {children ?? }
+
+ );
+}
+
+function BreadcrumbEllipsis({
+ className,
+ ...props
+}: React.ComponentProps<"span">) {
+ return (
+
+
+ More
+
+ );
+}
+
+export {
+ Breadcrumb,
+ BreadcrumbList,
+ BreadcrumbItem,
+ BreadcrumbLink,
+ BreadcrumbPage,
+ BreadcrumbSeparator,
+ BreadcrumbEllipsis,
+};
diff --git a/src/components/ui/button.tsx b/src/components/ui/button.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..308ad2bda121a27dd16bfec17769a3eb56b66693
--- /dev/null
+++ b/src/components/ui/button.tsx
@@ -0,0 +1,58 @@
+import * as React from "react";
+import { Slot } from "@radix-ui/react-slot@1.1.2";
+import { cva, type VariantProps } from "class-variance-authority@0.7.1";
+
+import { cn } from "./utils";
+
+const buttonVariants = cva(
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
+ {
+ variants: {
+ variant: {
+ default: "bg-primary text-primary-foreground hover:bg-primary/90",
+ destructive:
+ "bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
+ outline:
+ "border bg-background text-foreground hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
+ secondary:
+ "bg-secondary text-secondary-foreground hover:bg-secondary/80",
+ ghost:
+ "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
+ link: "text-primary underline-offset-4 hover:underline",
+ },
+ size: {
+ default: "h-9 px-4 py-2 has-[>svg]:px-3",
+ sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
+ lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
+ icon: "size-9 rounded-md",
+ },
+ },
+ defaultVariants: {
+ variant: "default",
+ size: "default",
+ },
+ },
+);
+
+function Button({
+ className,
+ variant,
+ size,
+ asChild = false,
+ ...props
+}: React.ComponentProps<"button"> &
+ VariantProps & {
+ asChild?: boolean;
+ }) {
+ const Comp = asChild ? Slot : "button";
+
+ return (
+
+ );
+}
+
+export { Button, buttonVariants };
diff --git a/src/components/ui/calendar.tsx b/src/components/ui/calendar.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..b30236faaeb97276eda9777d5da8bfcc7a3e1cea
--- /dev/null
+++ b/src/components/ui/calendar.tsx
@@ -0,0 +1,75 @@
+"use client";
+
+import * as React from "react";
+import { ChevronLeft, ChevronRight } from "lucide-react@0.487.0";
+import { DayPicker } from "react-day-picker@8.10.1";
+
+import { cn } from "./utils";
+import { buttonVariants } from "./button";
+
+function Calendar({
+ className,
+ classNames,
+ showOutsideDays = true,
+ ...props
+}: React.ComponentProps) {
+ return (
+ .day-range-end)]:rounded-r-md [&:has(>.day-range-start)]:rounded-l-md first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md"
+ : "[&:has([aria-selected])]:rounded-md",
+ ),
+ day: cn(
+ buttonVariants({ variant: "ghost" }),
+ "size-8 p-0 font-normal aria-selected:opacity-100",
+ ),
+ day_range_start:
+ "day-range-start aria-selected:bg-primary aria-selected:text-primary-foreground",
+ day_range_end:
+ "day-range-end aria-selected:bg-primary aria-selected:text-primary-foreground",
+ day_selected:
+ "bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground",
+ day_today: "bg-accent text-accent-foreground",
+ day_outside:
+ "day-outside text-muted-foreground aria-selected:text-muted-foreground",
+ day_disabled: "text-muted-foreground opacity-50",
+ day_range_middle:
+ "aria-selected:bg-accent aria-selected:text-accent-foreground",
+ day_hidden: "invisible",
+ ...classNames,
+ }}
+ components={{
+ IconLeft: ({ className, ...props }) => (
+
+ ),
+ IconRight: ({ className, ...props }) => (
+
+ ),
+ }}
+ {...props}
+ />
+ );
+}
+
+export { Calendar };
diff --git a/src/components/ui/card.tsx b/src/components/ui/card.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..5f9d58a566782b3bd04c13e9e4aa31eca7d6e9f7
--- /dev/null
+++ b/src/components/ui/card.tsx
@@ -0,0 +1,92 @@
+import * as React from "react";
+
+import { cn } from "./utils";
+
+function Card({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ );
+}
+
+function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ );
+}
+
+function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ );
+}
+
+function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ );
+}
+
+function CardAction({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ );
+}
+
+function CardContent({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ );
+}
+
+function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ );
+}
+
+export {
+ Card,
+ CardHeader,
+ CardFooter,
+ CardTitle,
+ CardAction,
+ CardDescription,
+ CardContent,
+};
diff --git a/src/components/ui/carousel.tsx b/src/components/ui/carousel.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..2752b36c7a87383674ab853aa489083c21a8d5d3
--- /dev/null
+++ b/src/components/ui/carousel.tsx
@@ -0,0 +1,241 @@
+"use client";
+
+import * as React from "react";
+import useEmblaCarousel, {
+ type UseEmblaCarouselType,
+} from "embla-carousel-react@8.6.0";
+import { ArrowLeft, ArrowRight } from "lucide-react@0.487.0";
+
+import { cn } from "./utils";
+import { Button } from "./button";
+
+type CarouselApi = UseEmblaCarouselType[1];
+type UseCarouselParameters = Parameters;
+type CarouselOptions = UseCarouselParameters[0];
+type CarouselPlugin = UseCarouselParameters[1];
+
+type CarouselProps = {
+ opts?: CarouselOptions;
+ plugins?: CarouselPlugin;
+ orientation?: "horizontal" | "vertical";
+ setApi?: (api: CarouselApi) => void;
+};
+
+type CarouselContextProps = {
+ carouselRef: ReturnType[0];
+ api: ReturnType[1];
+ scrollPrev: () => void;
+ scrollNext: () => void;
+ canScrollPrev: boolean;
+ canScrollNext: boolean;
+} & CarouselProps;
+
+const CarouselContext = React.createContext(null);
+
+function useCarousel() {
+ const context = React.useContext(CarouselContext);
+
+ if (!context) {
+ throw new Error("useCarousel must be used within a ");
+ }
+
+ return context;
+}
+
+function Carousel({
+ orientation = "horizontal",
+ opts,
+ setApi,
+ plugins,
+ className,
+ children,
+ ...props
+}: React.ComponentProps<"div"> & CarouselProps) {
+ const [carouselRef, api] = useEmblaCarousel(
+ {
+ ...opts,
+ axis: orientation === "horizontal" ? "x" : "y",
+ },
+ plugins,
+ );
+ const [canScrollPrev, setCanScrollPrev] = React.useState(false);
+ const [canScrollNext, setCanScrollNext] = React.useState(false);
+
+ const onSelect = React.useCallback((api: CarouselApi) => {
+ if (!api) return;
+ setCanScrollPrev(api.canScrollPrev());
+ setCanScrollNext(api.canScrollNext());
+ }, []);
+
+ const scrollPrev = React.useCallback(() => {
+ api?.scrollPrev();
+ }, [api]);
+
+ const scrollNext = React.useCallback(() => {
+ api?.scrollNext();
+ }, [api]);
+
+ const handleKeyDown = React.useCallback(
+ (event: React.KeyboardEvent) => {
+ if (event.key === "ArrowLeft") {
+ event.preventDefault();
+ scrollPrev();
+ } else if (event.key === "ArrowRight") {
+ event.preventDefault();
+ scrollNext();
+ }
+ },
+ [scrollPrev, scrollNext],
+ );
+
+ React.useEffect(() => {
+ if (!api || !setApi) return;
+ setApi(api);
+ }, [api, setApi]);
+
+ React.useEffect(() => {
+ if (!api) return;
+ onSelect(api);
+ api.on("reInit", onSelect);
+ api.on("select", onSelect);
+
+ return () => {
+ api?.off("select", onSelect);
+ };
+ }, [api, onSelect]);
+
+ return (
+
+
+ {children}
+
+
+ );
+}
+
+function CarouselContent({ className, ...props }: React.ComponentProps<"div">) {
+ const { carouselRef, orientation } = useCarousel();
+
+ return (
+
+ );
+}
+
+function CarouselItem({ className, ...props }: React.ComponentProps<"div">) {
+ const { orientation } = useCarousel();
+
+ return (
+
+ );
+}
+
+function CarouselPrevious({
+ className,
+ variant = "outline",
+ size = "icon",
+ ...props
+}: React.ComponentProps) {
+ const { orientation, scrollPrev, canScrollPrev } = useCarousel();
+
+ return (
+
+
+ Previous slide
+
+ );
+}
+
+function CarouselNext({
+ className,
+ variant = "outline",
+ size = "icon",
+ ...props
+}: React.ComponentProps) {
+ const { orientation, scrollNext, canScrollNext } = useCarousel();
+
+ return (
+
+
+ Next slide
+
+ );
+}
+
+export {
+ type CarouselApi,
+ Carousel,
+ CarouselContent,
+ CarouselItem,
+ CarouselPrevious,
+ CarouselNext,
+};
diff --git a/src/components/ui/chart.tsx b/src/components/ui/chart.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..d558e197cddf5a44baceec57a935e9cc1ba8980f
--- /dev/null
+++ b/src/components/ui/chart.tsx
@@ -0,0 +1,353 @@
+"use client";
+
+import * as React from "react";
+import * as RechartsPrimitive from "recharts@2.15.2";
+
+import { cn } from "./utils";
+
+// Format: { THEME_NAME: CSS_SELECTOR }
+const THEMES = { light: "", dark: ".dark" } as const;
+
+export type ChartConfig = {
+ [k in string]: {
+ label?: React.ReactNode;
+ icon?: React.ComponentType;
+ } & (
+ | { color?: string; theme?: never }
+ | { color?: never; theme: Record }
+ );
+};
+
+type ChartContextProps = {
+ config: ChartConfig;
+};
+
+const ChartContext = React.createContext(null);
+
+function useChart() {
+ const context = React.useContext(ChartContext);
+
+ if (!context) {
+ throw new Error("useChart must be used within a ");
+ }
+
+ return context;
+}
+
+function ChartContainer({
+ id,
+ className,
+ children,
+ config,
+ ...props
+}: React.ComponentProps<"div"> & {
+ config: ChartConfig;
+ children: React.ComponentProps<
+ typeof RechartsPrimitive.ResponsiveContainer
+ >["children"];
+}) {
+ const uniqueId = React.useId();
+ const chartId = `chart-${id || uniqueId.replace(/:/g, "")}`;
+
+ return (
+
+
+
+
+ {children}
+
+
+
+ );
+}
+
+const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => {
+ const colorConfig = Object.entries(config).filter(
+ ([, config]) => config.theme || config.color,
+ );
+
+ if (!colorConfig.length) {
+ return null;
+ }
+
+ return (
+