@olwiba/ui 0.1.15 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,15 +1,19 @@
1
1
  import { cn } from './chunk-AMWLSHD6.js';
2
2
  export { MdxContent, cn } from './chunk-AMWLSHD6.js';
3
- import * as React30 from 'react';
3
+ import * as React32 from 'react';
4
4
  import { useRef, useEffect, useState } from 'react';
5
- import { Button as Button$1, Card as Card$1, Input as Input$1, Textarea as Textarea$1, Checkbox as Checkbox$1, Switch as Switch$1, useIsMobile, UIVariantProvider, Badge as Badge$1, cn as cn$1, Table, TableHeader, TableRow, TableHead, TableBody, TableCell, Avatar, AvatarImage, AvatarFallback, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem, Dialog, DialogTrigger, Progress, useUIVariant, Separator, Accordion, AccordionItem, AccordionTrigger, AccordionContent, Label, Sheet, SheetTrigger, SheetContent, StatusIndicator, CommandDialog, CommandInput, CommandList, CommandEmpty, CommandSeparator, CommandGroup, CommandItem, TooltipProvider, Tooltip, TooltipTrigger, TooltipContent, ContextMenu as ContextMenu$1, ContextMenuTrigger, ContextMenuContent, AlertDialog, AlertDialogContent, AlertDialogHeader, AlertDialogTitle, AlertDialogDescription, AlertDialogFooter, AlertDialogCancel, AlertDialogAction, CommandShortcut, Spinner, Popover, PopoverTrigger, PopoverContent, SidebarProvider, SidebarInset, CardHeader, CardTitle, CardDescription, CardContent, InputOTP, InputOTPGroup, InputOTPSlot, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter, DialogClose, ContextMenuSeparator, ContextMenuLabel, ContextMenuSub, ContextMenuSubTrigger, ContextMenuSubContent, ContextMenuItem, ContextMenuShortcut, Sidebar, SidebarHeader, SidebarMenu, SidebarMenuItem, SidebarMenuButton, SidebarContent, SidebarGroup, SidebarGroupContent, SidebarFooter, SidebarTrigger, useSidebar, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuGroup } from '@olwiba/cn';
5
+ import { Button as Button$1, Card as Card$1, Input as Input$1, Textarea as Textarea$1, Checkbox as Checkbox$1, Switch as Switch$1, useIsMobile, UIVariantProvider, Badge as Badge$1, cn as cn$1, Table, TableHeader, TableRow, TableHead, TableBody, TableCell, Avatar, AvatarImage, AvatarFallback, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem, Dialog, DialogTrigger, Progress, useUIVariant, Separator, Accordion, AccordionItem, AccordionTrigger, AccordionContent, Label, Sheet, SheetTrigger, SheetContent, StatusIndicator, CommandDialog, CommandInput, CommandList, CommandEmpty, CommandSeparator, CommandGroup, CommandItem, TooltipProvider, Tooltip, TooltipTrigger, TooltipContent, ContextMenu as ContextMenu$1, ContextMenuTrigger, ContextMenuContent, AlertDialog, AlertDialogContent, AlertDialogHeader, AlertDialogTitle, AlertDialogDescription, AlertDialogFooter, AlertDialogCancel, AlertDialogAction, CommandShortcut, Popover, PopoverTrigger, PopoverContent, Spinner, SidebarProvider, SidebarInset, CardHeader, CardTitle, CardDescription, CardContent, InputOTP, InputOTPGroup, InputOTPSlot, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter, DialogClose, ContextMenuSeparator, ContextMenuLabel, ContextMenuSub, ContextMenuSubTrigger, ContextMenuSubContent, ContextMenuItem, ContextMenuShortcut, Sidebar, SidebarHeader, SidebarMenu, SidebarMenuItem, SidebarMenuButton, SidebarContent, SidebarGroup, SidebarGroupContent, SidebarFooter, SidebarTrigger, useSidebar, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuGroup } from '@olwiba/cn';
6
6
  export { CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Enchanted } from '@olwiba/cn';
7
7
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
8
- import { Search, ArrowUp, ArrowDown, ArrowUpDown, ChevronLeft, ChevronRight, MoreHorizontal, UserPlus, Download, CreditCard, Check, ArrowLeft, ArrowUpRight, X, ArrowRight, Sparkles, Rocket, Minus, Quote, Star, Twitter, Github, Linkedin, Mail, MessageSquare, ChevronDown, Send, Menu, UploadCloud, File, Loader2, CheckCircle2, AlertCircle, TrendingUp, TrendingDown, Sun, Moon, Palette, AlertTriangle, Inbox, Info, Layers, LayoutGrid, ShieldCheck, Building2, MoreVerticalIcon, CreditCardIcon, BellIcon, LogOutIcon } from 'lucide-react';
8
+ import { Search, ArrowUp, ArrowDown, ArrowUpDown, ChevronLeft, ChevronRight, MoreHorizontal, UserPlus, Download, CreditCard, Check, Loader2, ArrowLeft, ArrowUpRight, X, ArrowRight, Sparkles, Minus, Quote, Star, Twitter, Github, Linkedin, Mail, MessageSquare, ChevronDown, Send, Menu, UploadCloud, File, CheckCircle2, AlertCircle, Bell, Inbox, Activity, TrendingUp, TrendingDown, Sun, Moon, Palette, AlertTriangle, Rocket, Info, Layers, LayoutGrid, ShieldCheck, Building2, MoreVerticalIcon, CreditCardIcon, BellIcon, LogOutIcon } from 'lucide-react';
9
9
  import { useReactTable, getPaginationRowModel, getSortedRowModel, getCoreRowModel, flexRender } from '@tanstack/react-table';
10
+ import { useSensors, useSensor, PointerSensor, KeyboardSensor, DndContext, closestCenter } from '@dnd-kit/core';
11
+ import { sortableKeyboardCoordinates, SortableContext, rectSortingStrategy, horizontalListSortingStrategy, verticalListSortingStrategy, arrayMove, useSortable } from '@dnd-kit/sortable';
12
+ import { CSS } from '@dnd-kit/utilities';
13
+ import { PieChart, Pie, Cell, BarChart, XAxis, YAxis, Bar, AreaChart, Area, LineChart, Line, ResponsiveContainer, Tooltip as Tooltip$1, CartesianGrid } from 'recharts';
10
14
  import { toast } from 'sonner';
11
15
 
12
- var OlwibaUIContext = React30.createContext({
16
+ var OlwibaUIContext = React32.createContext({
13
17
  isMobile: false,
14
18
  mode: "default",
15
19
  setMode: () => {
@@ -18,17 +22,17 @@ var OlwibaUIContext = React30.createContext({
18
22
  function OlwibaUIProvider({ children, isMobile: isMobileProp, mode: initialMode = "default" }) {
19
23
  const detectedMobile = useIsMobile();
20
24
  const isMobile = isMobileProp ?? detectedMobile;
21
- const [mode, setMode] = React30.useState(initialMode);
25
+ const [mode, setMode] = React32.useState(initialMode);
22
26
  const variant = mode !== "default" ? mode : void 0;
23
27
  return /* @__PURE__ */ jsx(OlwibaUIContext.Provider, { value: { isMobile, mode, setMode }, children: /* @__PURE__ */ jsx(UIVariantProvider, { mode: variant, children }) });
24
28
  }
25
29
  function useOlwibaUI() {
26
- return React30.useContext(OlwibaUIContext);
30
+ return React32.useContext(OlwibaUIContext);
27
31
  }
28
32
  function useUIMode() {
29
- return React30.useContext(OlwibaUIContext).mode;
33
+ return React32.useContext(OlwibaUIContext).mode;
30
34
  }
31
- var Button = React30.forwardRef(
35
+ var Button = React32.forwardRef(
32
36
  ({ mode, ...props }, ref) => {
33
37
  const ctxMode = useUIMode();
34
38
  const resolvedMode = mode ?? (ctxMode !== "default" ? ctxMode : void 0);
@@ -36,7 +40,7 @@ var Button = React30.forwardRef(
36
40
  }
37
41
  );
38
42
  Button.displayName = "Button";
39
- var Card = React30.forwardRef(
43
+ var Card = React32.forwardRef(
40
44
  ({ mode, ...props }, ref) => {
41
45
  const ctxMode = useUIMode();
42
46
  const resolvedMode = mode ?? (ctxMode !== "default" ? ctxMode : void 0);
@@ -47,7 +51,7 @@ Card.displayName = "Card";
47
51
  function Badge(props) {
48
52
  return /* @__PURE__ */ jsx(Badge$1, { ...props });
49
53
  }
50
- var Input = React30.forwardRef(
54
+ var Input = React32.forwardRef(
51
55
  ({ mode, ...props }, ref) => {
52
56
  const ctxMode = useUIMode();
53
57
  const resolvedMode = mode ?? (ctxMode !== "default" ? ctxMode : void 0);
@@ -55,7 +59,7 @@ var Input = React30.forwardRef(
55
59
  }
56
60
  );
57
61
  Input.displayName = "Input";
58
- var Textarea = React30.forwardRef(
62
+ var Textarea = React32.forwardRef(
59
63
  ({ mode, ...props }, ref) => {
60
64
  const ctxMode = useUIMode();
61
65
  const resolvedMode = mode ?? (ctxMode !== "default" ? ctxMode : void 0);
@@ -63,13 +67,13 @@ var Textarea = React30.forwardRef(
63
67
  }
64
68
  );
65
69
  Textarea.displayName = "Textarea";
66
- var Checkbox = React30.forwardRef(({ mode, ...props }, ref) => {
70
+ var Checkbox = React32.forwardRef(({ mode, ...props }, ref) => {
67
71
  const ctxMode = useUIMode();
68
72
  const resolvedMode = mode ?? (ctxMode !== "default" ? ctxMode : void 0);
69
73
  return /* @__PURE__ */ jsx(Checkbox$1, { mode: resolvedMode, ...props, ref });
70
74
  });
71
75
  Checkbox.displayName = "Checkbox";
72
- var Switch = React30.forwardRef(({ mode: _mode, ...props }, ref) => {
76
+ var Switch = React32.forwardRef(({ mode: _mode, ...props }, ref) => {
73
77
  return /* @__PURE__ */ jsx(Switch$1, { ...props, ref });
74
78
  });
75
79
  Switch.displayName = "Switch";
@@ -513,7 +517,7 @@ function DefaultForm({
513
517
  const isForgotPassword = mode === "forgot-password";
514
518
  const isResetPassword = mode === "reset-password";
515
519
  const isVerify = mode === "verify";
516
- const [code, setCode] = React30.useState("");
520
+ const [code, setCode] = React32.useState("");
517
521
  const hasPrefill = !!(defaultEmail || defaultPassword);
518
522
  const prefillStyle = (active) => active ? { animation: "auth-prefill 1.6s ease-out 0.35s 1 both" } : void 0;
519
523
  const copy = authCopy[mode];
@@ -586,11 +590,14 @@ function DefaultForm({
586
590
  /* @__PURE__ */ jsx("input", { type: "hidden", name: "code", value: code }),
587
591
  /* @__PURE__ */ jsx("div", { className: "flex justify-center py-2", children: /* @__PURE__ */ jsx(InputOTP, { maxLength: codeLength, value: code, onChange: setCode, containerClassName: "justify-center", children: /* @__PURE__ */ jsx(InputOTPGroup, { children: Array.from({ length: codeLength }).map((_, i) => /* @__PURE__ */ jsx(InputOTPSlot, { index: i }, i)) }) }) })
588
592
  ] }),
589
- error && /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-destructive", children: error }),
590
- success && /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-primary", children: success }),
593
+ error && /* @__PURE__ */ jsx("p", { role: "alert", className: "text-sm font-medium text-destructive", children: error }),
594
+ success && /* @__PURE__ */ jsx("p", { role: "status", className: "text-sm font-medium text-primary", children: success }),
591
595
  /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
592
- /* @__PURE__ */ jsx(Button, { type: "submit", className: "w-full", disabled: loading || isVerify && code.length < codeLength, children: loading ? "Please wait\u2026" : copy.submit }),
593
- onSso && mode === "signin" && /* @__PURE__ */ jsx(Button, { type: "button", variant: "outline", className: "w-full", onClick: onSso, disabled: loading, children: "Use SSO" })
596
+ /* @__PURE__ */ jsxs(Button, { type: "submit", className: "w-full", disabled: loading || isVerify && code.length < codeLength, children: [
597
+ loading && /* @__PURE__ */ jsx(Loader2, { className: "size-4 animate-spin" }),
598
+ loading ? "Please wait\u2026" : copy.submit
599
+ ] }),
600
+ onSso && (mode === "signin" || mode === "signup") && /* @__PURE__ */ jsx(Button, { type: "button", variant: "outline", className: "w-full", onClick: onSso, disabled: loading, children: "Use SSO" })
594
601
  ] })
595
602
  ] }),
596
603
  isVerify && onResend && /* @__PURE__ */ jsxs("p", { className: "text-center text-xs text-muted-foreground", children: [
@@ -670,10 +677,10 @@ function DataTable({
670
677
  emptyMessage = "No results.",
671
678
  className
672
679
  }) {
673
- const [sorting, setSorting] = React30.useState([]);
674
- const [rowSelection, setRowSelection] = React30.useState({});
675
- const [globalFilter, setGlobalFilter] = React30.useState("");
676
- const allColumns = React30.useMemo(
680
+ const [sorting, setSorting] = React32.useState([]);
681
+ const [rowSelection, setRowSelection] = React32.useState({});
682
+ const [globalFilter, setGlobalFilter] = React32.useState("");
683
+ const allColumns = React32.useMemo(
677
684
  () => selectable ? [selectColumn, ...columns] : columns,
678
685
  [selectable, columns]
679
686
  );
@@ -695,7 +702,7 @@ function DataTable({
695
702
  initialState: pageSize > 0 ? { pagination: { pageSize } } : void 0,
696
703
  enableRowSelection: selectable
697
704
  });
698
- React30.useEffect(() => {
705
+ React32.useEffect(() => {
699
706
  if (!onSelectionChange) return;
700
707
  onSelectionChange(table.getSelectedRowModel().rows.map((r) => r.original));
701
708
  }, [rowSelection]);
@@ -720,39 +727,48 @@ function DataTable({
720
727
  /* @__PURE__ */ jsx(TableHeader, { children: table.getHeaderGroups().map((headerGroup) => /* @__PURE__ */ jsx(TableRow, { children: headerGroup.headers.map((header) => {
721
728
  const canSort = header.column.getCanSort();
722
729
  const sortDir = header.column.getIsSorted();
723
- return /* @__PURE__ */ jsx(TableHead, { children: header.isPlaceholder ? null : canSort ? /* @__PURE__ */ jsxs(
724
- "button",
730
+ return /* @__PURE__ */ jsx(
731
+ TableHead,
725
732
  {
726
- type: "button",
727
- className: "flex items-center gap-1.5 font-medium hover:text-foreground",
728
- onClick: header.column.getToggleSortingHandler(),
729
- children: [
730
- flexRender(header.column.columnDef.header, header.getContext()),
731
- sortDir === "asc" ? /* @__PURE__ */ jsx(ArrowUp, { className: "size-3.5" }) : sortDir === "desc" ? /* @__PURE__ */ jsx(ArrowDown, { className: "size-3.5" }) : /* @__PURE__ */ jsx(ArrowUpDown, { className: "size-3.5 text-muted-foreground/50" })
732
- ]
733
- }
734
- ) : flexRender(header.column.columnDef.header, header.getContext()) }, header.id);
733
+ "aria-sort": sortDir === "asc" ? "ascending" : sortDir === "desc" ? "descending" : canSort ? "none" : void 0,
734
+ children: header.isPlaceholder ? null : canSort ? /* @__PURE__ */ jsxs(
735
+ "button",
736
+ {
737
+ type: "button",
738
+ className: "flex items-center gap-1.5 font-medium hover:text-foreground",
739
+ onClick: header.column.getToggleSortingHandler(),
740
+ children: [
741
+ flexRender(header.column.columnDef.header, header.getContext()),
742
+ sortDir === "asc" ? /* @__PURE__ */ jsx(ArrowUp, { className: "size-3.5" }) : sortDir === "desc" ? /* @__PURE__ */ jsx(ArrowDown, { className: "size-3.5" }) : /* @__PURE__ */ jsx(ArrowUpDown, { className: "size-3.5 text-muted-foreground/50" })
743
+ ]
744
+ }
745
+ ) : flexRender(header.column.columnDef.header, header.getContext())
746
+ },
747
+ header.id
748
+ );
735
749
  }) }, headerGroup.id)) }),
736
750
  /* @__PURE__ */ jsx(TableBody, { children: rows.length ? rows.map((row) => /* @__PURE__ */ jsx(
737
751
  TableRow,
738
752
  {
739
753
  "data-state": row.getIsSelected() ? "selected" : void 0,
740
754
  onClick: () => onRowClick?.(row.original),
741
- className: cn$1(onRowClick && "cursor-pointer"),
755
+ tabIndex: onRowClick ? 0 : void 0,
756
+ onKeyDown: onRowClick ? (e) => {
757
+ if (e.key === "Enter" && e.target === e.currentTarget) onRowClick(row.original);
758
+ } : void 0,
759
+ className: cn$1(onRowClick && "cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"),
742
760
  children: row.getVisibleCells().map((cell) => /* @__PURE__ */ jsx(TableCell, { children: flexRender(cell.column.columnDef.cell, cell.getContext()) }, cell.id))
743
761
  },
744
762
  row.id
745
763
  )) : /* @__PURE__ */ jsx(TableRow, { children: /* @__PURE__ */ jsx(TableCell, { colSpan: allColumns.length, className: "h-24 text-center text-muted-foreground", children: emptyMessage }) }) })
746
764
  ] }) }),
747
- pageSize > 0 && table.getPageCount() > 1 && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
765
+ (selectable || pageSize > 0 && table.getPageCount() > 1) && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
748
766
  /* @__PURE__ */ jsxs("p", { className: "text-sm text-muted-foreground", children: [
749
- selectable && `${table.getFilteredSelectedRowModel().rows.length} of ${table.getFilteredRowModel().rows.length} selected \xB7 `,
750
- "Page ",
751
- table.getState().pagination.pageIndex + 1,
752
- " of ",
753
- table.getPageCount()
767
+ selectable && `${table.getFilteredSelectedRowModel().rows.length} of ${table.getFilteredRowModel().rows.length} selected`,
768
+ selectable && pageSize > 0 && table.getPageCount() > 1 && " \xB7 ",
769
+ pageSize > 0 && table.getPageCount() > 1 && `Page ${table.getState().pagination.pageIndex + 1} of ${table.getPageCount()}`
754
770
  ] }),
755
- /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
771
+ pageSize > 0 && table.getPageCount() > 1 && /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
756
772
  /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", onClick: () => table.previousPage(), disabled: !table.getCanPreviousPage(), children: [
757
773
  /* @__PURE__ */ jsx(ChevronLeft, { className: "size-4" }),
758
774
  " Previous"
@@ -765,32 +781,42 @@ function DataTable({
765
781
  ] })
766
782
  ] });
767
783
  }
768
- function InviteDialog({ roles, onInvite }) {
769
- const [email, setEmail] = React30.useState("");
770
- const [role, setRole] = React30.useState(roles[roles.length - 1] ?? roles[0]);
771
- return /* @__PURE__ */ jsxs(DialogContent, { children: [
772
- /* @__PURE__ */ jsxs(DialogHeader, { children: [
773
- /* @__PURE__ */ jsx(DialogTitle, { children: "Invite a team member" }),
774
- /* @__PURE__ */ jsx(DialogDescription, { children: "They\u2019ll get an email invite to join this workspace." })
775
- ] }),
776
- /* @__PURE__ */ jsxs("div", { className: "space-y-4 py-2", children: [
777
- /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
778
- /* @__PURE__ */ jsx(Label, { htmlFor: "invite-email", children: "Email address" }),
779
- /* @__PURE__ */ jsx(Input, { id: "invite-email", type: "email", placeholder: "name@company.com", value: email, onChange: (e) => setEmail(e.target.value) })
780
- ] }),
781
- /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
782
- /* @__PURE__ */ jsx(Label, { children: "Role" }),
783
- /* @__PURE__ */ jsxs(Select, { value: role, onValueChange: setRole, children: [
784
- /* @__PURE__ */ jsx(SelectTrigger, { children: /* @__PURE__ */ jsx(SelectValue, {}) }),
785
- /* @__PURE__ */ jsx(SelectContent, { children: roles.map((r) => /* @__PURE__ */ jsx(SelectItem, { value: r, children: r }, r)) })
784
+ function InviteDialog({ roles, onInvite, onClose }) {
785
+ const [email, setEmail] = React32.useState("");
786
+ const [role, setRole] = React32.useState(roles[roles.length - 1] ?? roles[0]);
787
+ return /* @__PURE__ */ jsx(DialogContent, { children: /* @__PURE__ */ jsxs(
788
+ "form",
789
+ {
790
+ onSubmit: (e) => {
791
+ e.preventDefault();
792
+ onInvite?.(email, role);
793
+ onClose();
794
+ },
795
+ children: [
796
+ /* @__PURE__ */ jsxs(DialogHeader, { children: [
797
+ /* @__PURE__ */ jsx(DialogTitle, { children: "Invite a team member" }),
798
+ /* @__PURE__ */ jsx(DialogDescription, { children: "They\u2019ll get an email invite to join this workspace." })
799
+ ] }),
800
+ /* @__PURE__ */ jsxs("div", { className: "space-y-4 py-4", children: [
801
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
802
+ /* @__PURE__ */ jsx(Label, { htmlFor: "invite-email", children: "Email address" }),
803
+ /* @__PURE__ */ jsx(Input, { id: "invite-email", type: "email", required: true, placeholder: "name@company.com", value: email, onChange: (e) => setEmail(e.target.value) })
804
+ ] }),
805
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
806
+ /* @__PURE__ */ jsx(Label, { children: "Role" }),
807
+ /* @__PURE__ */ jsxs(Select, { value: role, onValueChange: setRole, children: [
808
+ /* @__PURE__ */ jsx(SelectTrigger, { children: /* @__PURE__ */ jsx(SelectValue, {}) }),
809
+ /* @__PURE__ */ jsx(SelectContent, { children: roles.map((r) => /* @__PURE__ */ jsx(SelectItem, { value: r, children: r }, r)) })
810
+ ] })
811
+ ] })
812
+ ] }),
813
+ /* @__PURE__ */ jsxs(DialogFooter, { children: [
814
+ /* @__PURE__ */ jsx(DialogClose, { asChild: true, children: /* @__PURE__ */ jsx(Button, { type: "button", variant: "outline", children: "Cancel" }) }),
815
+ /* @__PURE__ */ jsx(Button, { type: "submit", children: "Send invite" })
786
816
  ] })
787
- ] })
788
- ] }),
789
- /* @__PURE__ */ jsxs(DialogFooter, { children: [
790
- /* @__PURE__ */ jsx(DialogClose, { asChild: true, children: /* @__PURE__ */ jsx(Button, { variant: "outline", children: "Cancel" }) }),
791
- /* @__PURE__ */ jsx(DialogClose, { asChild: true, children: /* @__PURE__ */ jsx(Button, { disabled: !email, onClick: () => onInvite?.(email, role), children: "Send invite" }) })
792
- ] })
793
- ] });
817
+ ]
818
+ }
819
+ ) });
794
820
  }
795
821
  function TeamMembersPanel({
796
822
  members,
@@ -801,7 +827,8 @@ function TeamMembersPanel({
801
827
  title = "Team members",
802
828
  description = "Manage who has access to this workspace."
803
829
  }) {
804
- const columns = React30.useMemo(() => [
830
+ const [inviteOpen, setInviteOpen] = React32.useState(false);
831
+ const columns = React32.useMemo(() => [
805
832
  {
806
833
  accessorKey: "name",
807
834
  header: "Name",
@@ -824,7 +851,7 @@ function TeamMembersPanel({
824
851
  /* @__PURE__ */ jsx(SelectContent, { children: roles.map((r) => /* @__PURE__ */ jsx(SelectItem, { value: r, children: r }, r)) })
825
852
  ] }) : /* @__PURE__ */ jsx(Badge$1, { variant: "secondary", children: row.original.role })
826
853
  },
827
- {
854
+ ...onRemove ? [{
828
855
  id: "actions",
829
856
  header: "",
830
857
  cell: ({ row }) => /* @__PURE__ */ jsxs(DropdownMenu, { children: [
@@ -832,23 +859,23 @@ function TeamMembersPanel({
832
859
  /* @__PURE__ */ jsx(MoreHorizontal, { className: "size-4" }),
833
860
  /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Row actions" })
834
861
  ] }) }),
835
- /* @__PURE__ */ jsx(DropdownMenuContent, { align: "end", children: /* @__PURE__ */ jsx(DropdownMenuItem, { className: "text-destructive", onClick: () => onRemove?.(row.original.id), children: "Remove member" }) })
862
+ /* @__PURE__ */ jsx(DropdownMenuContent, { align: "end", children: /* @__PURE__ */ jsx(DropdownMenuItem, { className: "text-destructive", onClick: () => onRemove(row.original.id), children: "Remove member" }) })
836
863
  ] }),
837
864
  enableSorting: false
838
- }
865
+ }] : []
839
866
  ], [roles, onRoleChange, onRemove]);
840
867
  return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
841
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-4", children: [
868
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-between gap-4", children: [
842
869
  /* @__PURE__ */ jsxs("div", { children: [
843
870
  /* @__PURE__ */ jsx("h2", { className: "text-base font-semibold", children: title }),
844
871
  /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-muted-foreground", children: description })
845
872
  ] }),
846
- /* @__PURE__ */ jsxs(Dialog, { children: [
873
+ onInvite && /* @__PURE__ */ jsxs(Dialog, { open: inviteOpen, onOpenChange: setInviteOpen, children: [
847
874
  /* @__PURE__ */ jsx(DialogTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(Button, { children: [
848
875
  /* @__PURE__ */ jsx(UserPlus, { className: "size-4" }),
849
876
  " Invite member"
850
877
  ] }) }),
851
- /* @__PURE__ */ jsx(InviteDialog, { roles, onInvite })
878
+ /* @__PURE__ */ jsx(InviteDialog, { roles, onInvite, onClose: () => setInviteOpen(false) }, String(inviteOpen))
852
879
  ] })
853
880
  ] }),
854
881
  /* @__PURE__ */ jsx(DataTable, { columns, data: members, searchKey: "name", searchPlaceholder: "Search members\u2026", pageSize: 0 })
@@ -870,12 +897,13 @@ function BillingPanel({
870
897
  onUpdatePaymentMethod,
871
898
  className
872
899
  }) {
873
- const invoiceColumns = React30.useMemo(() => [
874
- { accessorKey: "date", header: "Date" },
875
- { accessorKey: "amount", header: "Amount" },
900
+ const invoiceColumns = React32.useMemo(() => [
901
+ { accessorKey: "date", header: "Date", enableSorting: false },
902
+ { accessorKey: "amount", header: "Amount", enableSorting: false },
876
903
  {
877
904
  accessorKey: "status",
878
905
  header: "Status",
906
+ enableSorting: false,
879
907
  cell: ({ row }) => /* @__PURE__ */ jsx(Badge$1, { variant: invoiceStatusVariant[row.original.status], className: "capitalize", children: row.original.status })
880
908
  },
881
909
  {
@@ -903,19 +931,23 @@ function BillingPanel({
903
931
  ] }),
904
932
  onManagePlan && /* @__PURE__ */ jsx(Button, { onClick: onManagePlan, children: "Manage plan" })
905
933
  ] }),
906
- usage && usage.length > 0 && /* @__PURE__ */ jsx("div", { className: "mt-4 space-y-4", children: usage.map((metric) => /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
907
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between text-sm", children: [
908
- /* @__PURE__ */ jsx("span", { children: metric.label }),
909
- /* @__PURE__ */ jsxs("span", { className: "text-muted-foreground", children: [
910
- metric.used,
911
- metric.unit,
912
- " / ",
913
- metric.limit,
914
- metric.unit
915
- ] })
916
- ] }),
917
- /* @__PURE__ */ jsx(Progress, { value: metric.used / metric.limit * 100 })
918
- ] }, metric.label)) })
934
+ usage && usage.length > 0 && /* @__PURE__ */ jsx("div", { className: "mt-4 space-y-4", children: usage.map((metric) => {
935
+ const overLimit = metric.limit > 0 && metric.used > metric.limit;
936
+ const percent = metric.limit > 0 ? Math.min(100, metric.used / metric.limit * 100) : 0;
937
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
938
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between text-sm", children: [
939
+ /* @__PURE__ */ jsx("span", { children: metric.label }),
940
+ /* @__PURE__ */ jsxs("span", { className: overLimit ? "font-medium text-destructive" : "text-muted-foreground", children: [
941
+ metric.used,
942
+ metric.unit,
943
+ " / ",
944
+ metric.limit,
945
+ metric.unit
946
+ ] })
947
+ ] }),
948
+ /* @__PURE__ */ jsx(Progress, { value: percent, className: overLimit ? "[&>div]:bg-destructive" : void 0 })
949
+ ] }, metric.label);
950
+ }) })
919
951
  ] }),
920
952
  paymentMethod && /* @__PURE__ */ jsx(SettingsSection, { title: "Payment method", description: "Used for your subscription renewal.", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-4 rounded-lg border bg-card/60 p-4", children: [
921
953
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
@@ -944,9 +976,9 @@ function OnboardingWizard({
944
976
  completeLabel = "Finish",
945
977
  className
946
978
  }) {
947
- const [internalStep, setInternalStep] = React30.useState(0);
948
- const [error, setError] = React30.useState(null);
949
- const [pending, setPending] = React30.useState(false);
979
+ const [internalStep, setInternalStep] = React32.useState(0);
980
+ const [error, setError] = React32.useState(null);
981
+ const [pending, setPending] = React32.useState(false);
950
982
  const index = stepProp ?? internalStep;
951
983
  const current = steps[index];
952
984
  const isLast = index === steps.length - 1;
@@ -959,8 +991,15 @@ function OnboardingWizard({
959
991
  const handleNext = async () => {
960
992
  if (current.onNext) {
961
993
  setPending(true);
962
- const result = await current.onNext();
963
- setPending(false);
994
+ let result;
995
+ try {
996
+ result = await current.onNext();
997
+ } catch (err) {
998
+ setError(err instanceof Error ? err.message : "Something went wrong. Please try again.");
999
+ return;
1000
+ } finally {
1001
+ setPending(false);
1002
+ }
964
1003
  if (result === false) {
965
1004
  setError("Please complete this step before continuing.");
966
1005
  return;
@@ -980,30 +1019,47 @@ function OnboardingWizard({
980
1019
  if (index > 0) goTo(index - 1);
981
1020
  };
982
1021
  return /* @__PURE__ */ jsxs("div", { className: cn$1("w-full max-w-lg space-y-8", className), children: [
983
- /* @__PURE__ */ jsx("ol", { className: "flex items-center", children: steps.map((step, i) => /* @__PURE__ */ jsxs("li", { className: cn$1("flex items-center", i !== steps.length - 1 && "flex-1"), children: [
984
- /* @__PURE__ */ jsx(
985
- "div",
986
- {
987
- className: cn$1(
988
- "flex size-8 shrink-0 items-center justify-center rounded-full border text-xs font-medium",
989
- i < index ? "border-primary bg-primary text-primary-foreground" : i === index ? "border-primary text-primary" : "border-border text-muted-foreground"
1022
+ /* @__PURE__ */ jsx("ol", { className: "flex items-center", "aria-label": "Progress", children: steps.map((step, i) => /* @__PURE__ */ jsxs(
1023
+ "li",
1024
+ {
1025
+ "aria-current": i === index ? "step" : void 0,
1026
+ className: cn$1("flex items-center", i !== steps.length - 1 && "flex-1"),
1027
+ children: [
1028
+ /* @__PURE__ */ jsxs(
1029
+ "div",
1030
+ {
1031
+ className: cn$1(
1032
+ "flex size-8 shrink-0 items-center justify-center rounded-full border text-xs font-medium",
1033
+ i < index ? "border-primary bg-primary text-primary-foreground" : i === index ? "border-primary text-primary" : "border-border text-muted-foreground"
1034
+ ),
1035
+ children: [
1036
+ i < index ? /* @__PURE__ */ jsx(Check, { className: "size-4" }) : i + 1,
1037
+ /* @__PURE__ */ jsxs("span", { className: "sr-only", children: [
1038
+ step.title,
1039
+ i < index ? " (completed)" : ""
1040
+ ] })
1041
+ ]
1042
+ }
990
1043
  ),
991
- children: i < index ? /* @__PURE__ */ jsx(Check, { className: "size-4" }) : i + 1
992
- }
993
- ),
994
- i !== steps.length - 1 && /* @__PURE__ */ jsx("div", { className: cn$1("mx-2 h-px flex-1", i < index ? "bg-primary" : "bg-border") })
995
- ] }, step.id)) }),
1044
+ i !== steps.length - 1 && /* @__PURE__ */ jsx("div", { className: cn$1("mx-2 h-px flex-1", i < index ? "bg-primary" : "bg-border") })
1045
+ ]
1046
+ },
1047
+ step.id
1048
+ )) }),
996
1049
  /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
997
1050
  /* @__PURE__ */ jsxs("div", { children: [
998
1051
  /* @__PURE__ */ jsx("h2", { className: "text-xl font-semibold tracking-tight", children: current.title }),
999
1052
  current.description && /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-muted-foreground", children: current.description })
1000
1053
  ] }),
1001
1054
  /* @__PURE__ */ jsx("div", { children: current.content }),
1002
- error && /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-destructive", children: error })
1055
+ error && /* @__PURE__ */ jsx("p", { role: "alert", className: "text-sm font-medium text-destructive", children: error })
1003
1056
  ] }),
1004
1057
  /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
1005
1058
  /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", onClick: handleBack, disabled: index === 0 || pending, children: "Back" }),
1006
- /* @__PURE__ */ jsx(Button, { type: "button", onClick: handleNext, disabled: pending, children: pending ? "Please wait\u2026" : isLast ? completeLabel : "Continue" })
1059
+ /* @__PURE__ */ jsxs(Button, { type: "button", onClick: handleNext, disabled: pending, children: [
1060
+ pending && /* @__PURE__ */ jsx(Loader2, { className: "size-4 animate-spin" }),
1061
+ pending ? "Please wait\u2026" : isLast ? completeLabel : "Continue"
1062
+ ] })
1007
1063
  ] })
1008
1064
  ] });
1009
1065
  }
@@ -1256,9 +1312,9 @@ function FadeIn({
1256
1312
  style,
1257
1313
  ...props
1258
1314
  }) {
1259
- const ref = React30.useRef(null);
1260
- const [visible, setVisible] = React30.useState(false);
1261
- React30.useEffect(() => {
1315
+ const ref = React32.useRef(null);
1316
+ const [visible, setVisible] = React32.useState(false);
1317
+ React32.useEffect(() => {
1262
1318
  const el = ref.current;
1263
1319
  if (!el) return;
1264
1320
  const observer = new IntersectionObserver(
@@ -1365,27 +1421,28 @@ function HeroSection({
1365
1421
  socialProofText,
1366
1422
  renderLink = defaultRenderLink4
1367
1423
  }) {
1424
+ const mode = useUIVariant();
1368
1425
  return /* @__PURE__ */ jsx("section", { className: "overflow-hidden", children: /* @__PURE__ */ jsxs("div", { className: marketingSectionSpacing.hero, children: [
1369
1426
  /* @__PURE__ */ jsxs("div", { className: "mx-auto grid max-w-6xl items-center gap-10 lg:grid-cols-2 lg:gap-16", children: [
1370
1427
  /* @__PURE__ */ jsx(FadeIn, { direction: "left", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-6", children: [
1371
- badge && /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(Badge$1, { variant: "secondary", children: badge }) }),
1428
+ badge && /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(Badge, { variant: "secondary", children: badge }) }),
1372
1429
  /* @__PURE__ */ jsx("h1", { className: "text-balance text-4xl font-semibold tracking-tight sm:text-5xl lg:text-6xl", children: heading }),
1373
1430
  /* @__PURE__ */ jsx("p", { className: "max-w-lg text-pretty text-lg text-muted-foreground", children: description }),
1374
1431
  /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-3", children: [
1375
1432
  renderLink({
1376
1433
  href: primaryCta.href,
1377
- children: /* @__PURE__ */ jsxs(Button$1, { size: "lg", children: [
1434
+ children: /* @__PURE__ */ jsxs(Button, { size: "lg", children: [
1378
1435
  primaryCta.label,
1379
1436
  /* @__PURE__ */ jsx(ArrowRight, { className: "ml-2 size-4" })
1380
1437
  ] })
1381
1438
  }),
1382
1439
  secondaryCta && renderLink({
1383
1440
  href: secondaryCta.href,
1384
- children: /* @__PURE__ */ jsx(Button$1, { size: "lg", variant: "outline", children: secondaryCta.label })
1441
+ children: /* @__PURE__ */ jsx(Button, { size: "lg", variant: "outline", children: secondaryCta.label })
1385
1442
  })
1386
1443
  ] })
1387
1444
  ] }) }),
1388
- media !== "none" && /* @__PURE__ */ jsx(FadeIn, { direction: "right", delay: 200, children: media === "phone" ? /* @__PURE__ */ jsx("div", { className: "flex justify-center", children: /* @__PURE__ */ jsx(PhoneFrame, { size: phoneSize, children: heroImage }) }) : /* @__PURE__ */ jsx("div", { className: "overflow-hidden rounded-2xl", children: heroImage }) })
1445
+ media !== "none" && /* @__PURE__ */ jsx(FadeIn, { direction: "right", delay: 200, children: media === "phone" ? /* @__PURE__ */ jsx("div", { className: "flex justify-center", children: /* @__PURE__ */ jsx(PhoneFrame, { size: phoneSize, children: heroImage }) }) : /* @__PURE__ */ jsx("div", { className: cn$1("overflow-hidden", mode === "smooth" ? "rounded-3xl" : "rounded-2xl"), children: heroImage }) })
1389
1446
  ] }),
1390
1447
  (avatarUrls?.length || socialProofText) && /* @__PURE__ */ jsx(FadeIn, { direction: "up", delay: 400, children: /* @__PURE__ */ jsxs("div", { className: "mx-auto mt-12 flex max-w-6xl items-center gap-4", children: [
1391
1448
  avatarUrls && avatarUrls.length > 0 && /* @__PURE__ */ jsx("div", { className: "flex -space-x-3", children: avatarUrls.map((url, i) => /* @__PURE__ */ jsxs(Avatar, { className: "size-9 border-2 border-background", children: [
@@ -1444,6 +1501,88 @@ function FeatureCard({ icon: Icon, title, description, href, className, ...props
1444
1501
  }
1445
1502
  return content;
1446
1503
  }
1504
+ function Carousel({
1505
+ children,
1506
+ itemClassName = "w-[280px] sm:w-[320px]",
1507
+ controls = "top-right",
1508
+ ariaLabel,
1509
+ className
1510
+ }) {
1511
+ const mode = useUIVariant();
1512
+ const trackRef = React32.useRef(null);
1513
+ const [canPrev, setCanPrev] = React32.useState(false);
1514
+ const [canNext, setCanNext] = React32.useState(true);
1515
+ const updateScrollState = React32.useCallback(() => {
1516
+ const track = trackRef.current;
1517
+ if (!track) return;
1518
+ setCanPrev(track.scrollLeft > 8);
1519
+ setCanNext(track.scrollLeft < track.scrollWidth - track.clientWidth - 8);
1520
+ }, []);
1521
+ React32.useEffect(() => {
1522
+ const track = trackRef.current;
1523
+ if (!track) return;
1524
+ updateScrollState();
1525
+ track.addEventListener("scroll", updateScrollState, { passive: true });
1526
+ const observer = new ResizeObserver(updateScrollState);
1527
+ observer.observe(track);
1528
+ return () => {
1529
+ track.removeEventListener("scroll", updateScrollState);
1530
+ observer.disconnect();
1531
+ };
1532
+ }, [updateScrollState]);
1533
+ const scrollByItem = (direction) => {
1534
+ const track = trackRef.current;
1535
+ if (!track) return;
1536
+ const item = track.querySelector("[data-carousel-item]");
1537
+ const gap = parseFloat(getComputedStyle(track).columnGap) || 24;
1538
+ const step = item ? item.offsetWidth + gap : track.clientWidth * 0.8;
1539
+ track.scrollBy({ left: direction * step, behavior: "smooth" });
1540
+ };
1541
+ const controlClasses = cn$1(
1542
+ "flex size-9 items-center justify-center border bg-background text-foreground transition-colors",
1543
+ "hover:bg-muted disabled:opacity-40 disabled:hover:bg-background",
1544
+ mode === "smooth" ? "rounded-2xl" : "rounded-xl"
1545
+ );
1546
+ return /* @__PURE__ */ jsxs("div", { className, children: [
1547
+ controls === "top-right" && /* @__PURE__ */ jsxs("div", { className: "mb-4 flex items-center justify-end gap-2", children: [
1548
+ /* @__PURE__ */ jsx(
1549
+ "button",
1550
+ {
1551
+ type: "button",
1552
+ "aria-label": "Previous",
1553
+ className: controlClasses,
1554
+ onClick: () => scrollByItem(-1),
1555
+ disabled: !canPrev,
1556
+ children: /* @__PURE__ */ jsx(ChevronLeft, { className: "size-4" })
1557
+ }
1558
+ ),
1559
+ /* @__PURE__ */ jsx(
1560
+ "button",
1561
+ {
1562
+ type: "button",
1563
+ "aria-label": "Next",
1564
+ className: controlClasses,
1565
+ onClick: () => scrollByItem(1),
1566
+ disabled: !canNext,
1567
+ children: /* @__PURE__ */ jsx(ChevronRight, { className: "size-4" })
1568
+ }
1569
+ )
1570
+ ] }),
1571
+ /* @__PURE__ */ jsx(
1572
+ "div",
1573
+ {
1574
+ ref: trackRef,
1575
+ role: "region",
1576
+ "aria-label": ariaLabel,
1577
+ className: "flex snap-x snap-mandatory gap-6 overflow-x-auto pb-4 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden",
1578
+ children: React32.Children.map(
1579
+ children,
1580
+ (child) => child == null ? null : /* @__PURE__ */ jsx("div", { "data-carousel-item": true, className: cn$1("shrink-0 snap-start", itemClassName), children: child })
1581
+ )
1582
+ }
1583
+ )
1584
+ ] });
1585
+ }
1447
1586
  var translateMap2 = {
1448
1587
  up: [0, 20],
1449
1588
  down: [0, -20],
@@ -1461,9 +1600,9 @@ function StaggerChildren({
1461
1600
  className,
1462
1601
  ...props
1463
1602
  }) {
1464
- const ref = React30.useRef(null);
1465
- const [visible, setVisible] = React30.useState(false);
1466
- React30.useEffect(() => {
1603
+ const ref = React32.useRef(null);
1604
+ const [visible, setVisible] = React32.useState(false);
1605
+ React32.useEffect(() => {
1467
1606
  const el = ref.current;
1468
1607
  if (!el) return;
1469
1608
  const observer = new IntersectionObserver(
@@ -1481,7 +1620,7 @@ function StaggerChildren({
1481
1620
  return () => observer.disconnect();
1482
1621
  }, [once]);
1483
1622
  const [tx, ty] = translateMap2[direction];
1484
- return /* @__PURE__ */ jsx("div", { ref, className: cn$1(className), ...props, children: React30.Children.map(children, (child, i) => /* @__PURE__ */ jsx(
1623
+ return /* @__PURE__ */ jsx("div", { ref, className: cn$1(className), ...props, children: React32.Children.map(children, (child, i) => /* @__PURE__ */ jsx(
1485
1624
  "div",
1486
1625
  {
1487
1626
  className: "h-full",
@@ -1499,7 +1638,8 @@ function FeaturesSection({
1499
1638
  title = "Everything you need to ship fast",
1500
1639
  description = "A complete system of components, blocks, and hooks designed to work together - and get out of your way.",
1501
1640
  badge = "Features",
1502
- features
1641
+ features,
1642
+ layout = "grid"
1503
1643
  }) {
1504
1644
  const mode = useUIVariant();
1505
1645
  const sectionClasses = cn$1(
@@ -1508,18 +1648,19 @@ function FeaturesSection({
1508
1648
  mode === "playful" && "rounded-2xl border-primary/25 border",
1509
1649
  !mode && "rounded-2xl border"
1510
1650
  );
1511
- return /* @__PURE__ */ jsx("section", { className: sectionClasses, children: /* @__PURE__ */ jsx("div", { className: "px-6 py-14 sm:px-10 sm:py-20", children: /* @__PURE__ */ jsxs("div", { className: "mx-auto max-w-4xl", children: [
1651
+ const cards = features.map((feature) => /* @__PURE__ */ jsx(
1652
+ FeatureCard,
1653
+ {
1654
+ icon: feature.icon,
1655
+ title: feature.title,
1656
+ description: feature.description,
1657
+ href: feature.href
1658
+ },
1659
+ feature.title
1660
+ ));
1661
+ return /* @__PURE__ */ jsx("section", { className: sectionClasses, children: /* @__PURE__ */ jsx("div", { className: "px-6 py-14 sm:px-10 sm:py-20", children: /* @__PURE__ */ jsxs("div", { className: cn$1("mx-auto", layout === "carousel" ? "max-w-5xl" : "max-w-4xl"), children: [
1512
1662
  /* @__PURE__ */ jsx(SectionTitle, { title, description, badge }),
1513
- /* @__PURE__ */ jsx(StaggerChildren, { className: "mt-12 grid gap-8 sm:grid-cols-2 lg:grid-cols-3", children: features.map((feature) => /* @__PURE__ */ jsx(
1514
- FeatureCard,
1515
- {
1516
- icon: feature.icon,
1517
- title: feature.title,
1518
- description: feature.description,
1519
- href: feature.href
1520
- },
1521
- feature.title
1522
- )) })
1663
+ layout === "carousel" ? /* @__PURE__ */ jsx(FadeIn, { direction: "up", children: /* @__PURE__ */ jsx(Carousel, { className: "mt-10", ariaLabel: title, children: cards }) }) : /* @__PURE__ */ jsx(StaggerChildren, { className: "mt-12 grid gap-8 sm:grid-cols-2 lg:grid-cols-3", children: cards })
1523
1664
  ] }) }) });
1524
1665
  }
1525
1666
  function GroupedFeaturesSection({
@@ -1649,106 +1790,6 @@ function TechStackSection({
1649
1790
  }) })
1650
1791
  ] }) }) });
1651
1792
  }
1652
- function CarouselSection({
1653
- title = "Explore the platform",
1654
- description,
1655
- badge = "Features",
1656
- features
1657
- }) {
1658
- const mode = useUIVariant();
1659
- const trackRef = React30.useRef(null);
1660
- const [canPrev, setCanPrev] = React30.useState(false);
1661
- const [canNext, setCanNext] = React30.useState(true);
1662
- const updateScrollState = React30.useCallback(() => {
1663
- const track = trackRef.current;
1664
- if (!track) return;
1665
- setCanPrev(track.scrollLeft > 8);
1666
- setCanNext(track.scrollLeft < track.scrollWidth - track.clientWidth - 8);
1667
- }, []);
1668
- React30.useEffect(() => {
1669
- const track = trackRef.current;
1670
- if (!track) return;
1671
- updateScrollState();
1672
- track.addEventListener("scroll", updateScrollState, { passive: true });
1673
- const observer = new ResizeObserver(updateScrollState);
1674
- observer.observe(track);
1675
- return () => {
1676
- track.removeEventListener("scroll", updateScrollState);
1677
- observer.disconnect();
1678
- };
1679
- }, [updateScrollState]);
1680
- const scrollByCard = (direction) => {
1681
- const track = trackRef.current;
1682
- if (!track) return;
1683
- const card = track.querySelector("[data-carousel-item]");
1684
- const step = card ? card.offsetWidth + 24 : track.clientWidth * 0.8;
1685
- track.scrollBy({ left: direction * step, behavior: "smooth" });
1686
- };
1687
- const sectionClasses = cn$1(
1688
- "overflow-hidden bg-card",
1689
- mode === "smooth" && "rounded-3xl border",
1690
- mode === "playful" && "rounded-2xl border-primary/25 border",
1691
- !mode && "rounded-2xl border"
1692
- );
1693
- const controlClasses = cn$1(
1694
- "flex size-9 items-center justify-center border bg-background text-foreground transition-colors",
1695
- "hover:bg-muted disabled:opacity-40 disabled:hover:bg-background",
1696
- mode === "smooth" ? "rounded-2xl" : "rounded-xl"
1697
- );
1698
- return /* @__PURE__ */ jsx("section", { className: sectionClasses, children: /* @__PURE__ */ jsx("div", { className: "px-6 py-14 sm:px-10 sm:py-20", children: /* @__PURE__ */ jsxs("div", { className: "mx-auto max-w-5xl", children: [
1699
- /* @__PURE__ */ jsx(SectionTitle, { title, description, badge }),
1700
- /* @__PURE__ */ jsxs(FadeIn, { direction: "up", children: [
1701
- /* @__PURE__ */ jsxs("div", { className: "mt-10 flex items-center justify-end gap-2", children: [
1702
- /* @__PURE__ */ jsx(
1703
- "button",
1704
- {
1705
- type: "button",
1706
- "aria-label": "Previous",
1707
- className: controlClasses,
1708
- onClick: () => scrollByCard(-1),
1709
- disabled: !canPrev,
1710
- children: /* @__PURE__ */ jsx(ChevronLeft, { className: "size-4" })
1711
- }
1712
- ),
1713
- /* @__PURE__ */ jsx(
1714
- "button",
1715
- {
1716
- type: "button",
1717
- "aria-label": "Next",
1718
- className: controlClasses,
1719
- onClick: () => scrollByCard(1),
1720
- disabled: !canNext,
1721
- children: /* @__PURE__ */ jsx(ChevronRight, { className: "size-4" })
1722
- }
1723
- )
1724
- ] }),
1725
- /* @__PURE__ */ jsx(
1726
- "div",
1727
- {
1728
- ref: trackRef,
1729
- className: "mt-4 flex snap-x snap-mandatory gap-6 overflow-x-auto pb-4 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden",
1730
- children: features.map((feature) => /* @__PURE__ */ jsx(
1731
- "div",
1732
- {
1733
- "data-carousel-item": true,
1734
- className: "w-[280px] shrink-0 snap-start sm:w-[320px]",
1735
- children: /* @__PURE__ */ jsx(
1736
- FeatureCard,
1737
- {
1738
- icon: feature.icon,
1739
- title: feature.title,
1740
- description: feature.description,
1741
- href: feature.href
1742
- }
1743
- )
1744
- },
1745
- feature.title
1746
- ))
1747
- }
1748
- )
1749
- ] })
1750
- ] }) }) });
1751
- }
1752
1793
  var speedDuration = {
1753
1794
  slow: "280s",
1754
1795
  normal: "180s",
@@ -1829,79 +1870,42 @@ function FeatureMarqueeSection({
1829
1870
  ` })
1830
1871
  ] });
1831
1872
  }
1832
- var defaultRenderLink5 = ({ href, children, className }) => /* @__PURE__ */ jsx("a", { href, className, children });
1833
- function CtaSection({
1834
- heading,
1835
- description,
1836
- primaryCta,
1837
- secondaryCta,
1838
- footnote,
1839
- renderLink = defaultRenderLink5
1840
- }) {
1841
- const mode = useUIVariant();
1842
- const sectionClasses = cn$1(
1843
- "overflow-hidden bg-card",
1844
- mode === "smooth" && "rounded-3xl border",
1845
- mode === "playful" && "rounded-2xl border-primary/25 border",
1846
- !mode && "rounded-2xl border"
1847
- );
1848
- return /* @__PURE__ */ jsx("section", { className: sectionClasses, children: /* @__PURE__ */ jsxs("div", { className: "relative px-6 py-16 sm:px-10 sm:py-24", children: [
1849
- /* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-[radial-gradient(ellipse_at_center,hsl(var(--primary)/0.15),transparent_70%)]" }),
1850
- /* @__PURE__ */ jsx(FadeIn, { direction: "up", children: /* @__PURE__ */ jsxs("div", { className: "relative mx-auto max-w-2xl text-center", children: [
1851
- /* @__PURE__ */ jsx("div", { className: cn$1("mb-4 inline-flex h-10 w-10 items-center justify-center bg-primary/10 text-primary", mode === "smooth" ? "rounded-3xl" : "rounded-2xl"), children: /* @__PURE__ */ jsx(Sparkles, { className: "size-5" }) }),
1852
- /* @__PURE__ */ jsx("h2", { className: "text-balance text-3xl font-semibold tracking-tight sm:text-4xl", children: heading }),
1853
- /* @__PURE__ */ jsx("p", { className: "mx-auto mt-4 max-w-lg text-pretty text-muted-foreground", children: description }),
1854
- /* @__PURE__ */ jsxs("div", { className: "mt-8 flex flex-wrap items-center justify-center gap-3", children: [
1855
- renderLink({
1856
- href: primaryCta.href,
1857
- children: /* @__PURE__ */ jsxs(Button$1, { size: "lg", children: [
1858
- primaryCta.label,
1859
- /* @__PURE__ */ jsx(ArrowRight, { className: "ml-2 size-4" })
1860
- ] })
1861
- }),
1862
- secondaryCta && renderLink({
1863
- href: secondaryCta.href,
1864
- children: /* @__PURE__ */ jsx(Button$1, { size: "lg", variant: "outline", children: secondaryCta.label })
1865
- })
1866
- ] }),
1867
- footnote && /* @__PURE__ */ jsx("p", { className: "mt-4 text-sm text-muted-foreground", children: footnote })
1868
- ] }) })
1869
- ] }) });
1873
+ function useIntersectionObserver(options) {
1874
+ const ref = React32.useRef(null);
1875
+ const [isIntersecting, setIsIntersecting] = React32.useState(false);
1876
+ React32.useEffect(() => {
1877
+ const el = ref.current;
1878
+ if (!el) return;
1879
+ const observer = new IntersectionObserver(([entry]) => {
1880
+ setIsIntersecting(entry.isIntersecting);
1881
+ }, options);
1882
+ observer.observe(el);
1883
+ return () => observer.disconnect();
1884
+ }, []);
1885
+ return [ref, isIntersecting];
1870
1886
  }
1871
- var defaultRenderLink6 = ({ href, children, className }) => /* @__PURE__ */ jsx("a", { href, className, children });
1872
- function CtaCardSection({
1887
+ var defaultRenderLink5 = ({ href, children, className }) => /* @__PURE__ */ jsx("a", { href, className, children });
1888
+ function ShowcaseCta({
1873
1889
  heading,
1874
1890
  description,
1875
1891
  primaryCta,
1876
1892
  footnote,
1877
1893
  icon,
1878
- renderLink = defaultRenderLink6
1894
+ renderLink = defaultRenderLink5,
1895
+ sectionClasses
1879
1896
  }) {
1880
- const ref = React30.useRef(null);
1881
- const [visible, setVisible] = React30.useState(false);
1882
- React30.useEffect(() => {
1883
- const el = ref.current;
1884
- if (!el) return;
1885
- const observer = new IntersectionObserver(
1886
- ([entry]) => {
1887
- if (entry.isIntersecting) {
1888
- setVisible(true);
1889
- observer.disconnect();
1890
- }
1891
- },
1892
- { threshold: 0.15 }
1893
- );
1894
- observer.observe(el);
1895
- return () => observer.disconnect();
1896
- }, []);
1897
+ const [ref, intersecting] = useIntersectionObserver({ threshold: 0.15 });
1898
+ const [visible, setVisible] = React32.useState(false);
1899
+ React32.useEffect(() => {
1900
+ if (intersecting) setVisible(true);
1901
+ }, [intersecting]);
1897
1902
  return /* @__PURE__ */ jsxs(
1898
1903
  "section",
1899
1904
  {
1900
1905
  ref,
1901
1906
  className: cn$1(
1902
- "relative overflow-hidden rounded-3xl border border-gray-200 shadow-sm dark:border-border",
1903
- "bg-[#E0DFDB] dark:bg-card",
1904
- "transition-[opacity,transform] duration-700 ease-out",
1907
+ sectionClasses,
1908
+ "relative transition-[opacity,transform] duration-700 ease-out",
1905
1909
  visible ? "opacity-100 translate-y-0" : "opacity-0 translate-y-6"
1906
1910
  ),
1907
1911
  children: [
@@ -1910,11 +1914,11 @@ function CtaCardSection({
1910
1914
  {
1911
1915
  className: "pointer-events-none absolute inset-0 flex items-center justify-center",
1912
1916
  style: {
1913
- opacity: visible ? 0.12 : 0,
1917
+ opacity: visible ? 0.1 : 0,
1914
1918
  transform: visible ? "scale(1) rotate(0deg)" : "scale(0.9) rotate(-5deg)",
1915
1919
  transition: "opacity 700ms ease 200ms, transform 700ms ease 200ms"
1916
1920
  },
1917
- children: icon ?? /* @__PURE__ */ jsx(Rocket, { className: "size-72 text-foreground dark:text-foreground", strokeWidth: 0.75 })
1921
+ children: icon ?? /* @__PURE__ */ jsx(Rocket, { className: "size-72 text-foreground", strokeWidth: 0.75 })
1918
1922
  }
1919
1923
  ),
1920
1924
  /* @__PURE__ */ jsxs("div", { className: "relative z-10 px-6 py-20 text-center sm:px-10 sm:py-28", children: [
@@ -1922,9 +1926,9 @@ function CtaCardSection({
1922
1926
  description && /* @__PURE__ */ jsx("p", { className: "mx-auto mt-4 max-w-lg text-pretty text-muted-foreground", children: description }),
1923
1927
  /* @__PURE__ */ jsx("div", { className: "mt-8", children: renderLink({
1924
1928
  href: primaryCta.href,
1925
- children: /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-2 rounded-full bg-foreground px-6 py-3 text-sm font-medium text-background transition-opacity hover:opacity-80", children: [
1929
+ children: /* @__PURE__ */ jsxs(Button, { size: "lg", className: "rounded-full px-6", children: [
1926
1930
  primaryCta.label,
1927
- /* @__PURE__ */ jsx(ArrowRight, { className: "size-4" })
1931
+ /* @__PURE__ */ jsx(ArrowRight, { className: "ml-2 size-4" })
1928
1932
  ] })
1929
1933
  }) }),
1930
1934
  footnote && /* @__PURE__ */ jsx("p", { className: "mt-5 text-sm text-muted-foreground", children: footnote })
@@ -1933,6 +1937,49 @@ function CtaCardSection({
1933
1937
  }
1934
1938
  );
1935
1939
  }
1940
+ function CtaSection(props) {
1941
+ const {
1942
+ heading,
1943
+ description,
1944
+ primaryCta,
1945
+ secondaryCta,
1946
+ footnote,
1947
+ variant = "default",
1948
+ renderLink = defaultRenderLink5
1949
+ } = props;
1950
+ const mode = useUIVariant();
1951
+ const sectionClasses = cn$1(
1952
+ "overflow-hidden bg-card",
1953
+ mode === "smooth" && "rounded-3xl border",
1954
+ mode === "playful" && "rounded-2xl border-primary/25 border",
1955
+ !mode && "rounded-2xl border"
1956
+ );
1957
+ if (variant === "showcase") {
1958
+ return /* @__PURE__ */ jsx(ShowcaseCta, { ...props, sectionClasses });
1959
+ }
1960
+ return /* @__PURE__ */ jsx("section", { className: sectionClasses, children: /* @__PURE__ */ jsxs("div", { className: "relative px-6 py-16 sm:px-10 sm:py-24", children: [
1961
+ /* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-[radial-gradient(ellipse_at_center,hsl(var(--primary)/0.15),transparent_70%)]" }),
1962
+ /* @__PURE__ */ jsx(FadeIn, { direction: "up", children: /* @__PURE__ */ jsxs("div", { className: "relative mx-auto max-w-2xl text-center", children: [
1963
+ /* @__PURE__ */ jsx("div", { className: cn$1("mb-4 inline-flex h-10 w-10 items-center justify-center bg-primary/10 text-primary", mode === "smooth" ? "rounded-3xl" : "rounded-2xl"), children: /* @__PURE__ */ jsx(Sparkles, { className: "size-5" }) }),
1964
+ /* @__PURE__ */ jsx("h2", { className: "text-balance text-3xl font-semibold tracking-tight sm:text-4xl", children: heading }),
1965
+ description && /* @__PURE__ */ jsx("p", { className: "mx-auto mt-4 max-w-lg text-pretty text-muted-foreground", children: description }),
1966
+ /* @__PURE__ */ jsxs("div", { className: "mt-8 flex flex-wrap items-center justify-center gap-3", children: [
1967
+ renderLink({
1968
+ href: primaryCta.href,
1969
+ children: /* @__PURE__ */ jsxs(Button, { size: "lg", children: [
1970
+ primaryCta.label,
1971
+ /* @__PURE__ */ jsx(ArrowRight, { className: "ml-2 size-4" })
1972
+ ] })
1973
+ }),
1974
+ secondaryCta && renderLink({
1975
+ href: secondaryCta.href,
1976
+ children: /* @__PURE__ */ jsx(Button, { size: "lg", variant: "outline", children: secondaryCta.label })
1977
+ })
1978
+ ] }),
1979
+ footnote && /* @__PURE__ */ jsx("p", { className: "mt-4 text-sm text-muted-foreground", children: footnote })
1980
+ ] }) })
1981
+ ] }) });
1982
+ }
1936
1983
  function PricingCard({
1937
1984
  name,
1938
1985
  price,
@@ -2026,8 +2073,8 @@ function CountdownTimer({
2026
2073
  className,
2027
2074
  compact = false
2028
2075
  }) {
2029
- const [timeLeft, setTimeLeft] = React30.useState(() => getTimeLeft(deadline));
2030
- React30.useEffect(() => {
2076
+ const [timeLeft, setTimeLeft] = React32.useState(() => getTimeLeft(deadline));
2077
+ React32.useEffect(() => {
2031
2078
  setTimeLeft(getTimeLeft(deadline));
2032
2079
  const id = setInterval(() => setTimeLeft(getTimeLeft(deadline)), 1e3);
2033
2080
  return () => clearInterval(id);
@@ -2053,7 +2100,7 @@ function CountdownTimer({
2053
2100
  ];
2054
2101
  return /* @__PURE__ */ jsxs("div", { className: cn$1("flex items-center justify-center gap-2 text-sm", className), children: [
2055
2102
  /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: label }),
2056
- /* @__PURE__ */ jsx("span", { className: "flex items-center gap-1 font-mono font-semibold tabular-nums", children: units.map(({ value, unit }, i) => /* @__PURE__ */ jsxs(React30.Fragment, { children: [
2103
+ /* @__PURE__ */ jsx("span", { className: "flex items-center gap-1 font-mono font-semibold tabular-nums", children: units.map(({ value, unit }, i) => /* @__PURE__ */ jsxs(React32.Fragment, { children: [
2057
2104
  /* @__PURE__ */ jsxs("span", { children: [
2058
2105
  String(value).padStart(2, "0"),
2059
2106
  /* @__PURE__ */ jsx("span", { className: "font-normal text-muted-foreground", children: unit })
@@ -2062,7 +2109,7 @@ function CountdownTimer({
2062
2109
  ] }, unit)) })
2063
2110
  ] });
2064
2111
  }
2065
- var defaultRenderLink7 = ({ href, children, className }) => /* @__PURE__ */ jsx("a", { href, className, children });
2112
+ var defaultRenderLink6 = ({ href, children, className }) => /* @__PURE__ */ jsx("a", { href, className, children });
2066
2113
  function PricingSection({
2067
2114
  title = "Simple, transparent pricing",
2068
2115
  description = "Start for free. Scale as you grow. No hidden fees.",
@@ -2070,13 +2117,13 @@ function PricingSection({
2070
2117
  plans,
2071
2118
  saveBadge = "Save 34%",
2072
2119
  isAuthenticated,
2073
- renderLink = defaultRenderLink7,
2120
+ renderLink = defaultRenderLink6,
2074
2121
  footnote,
2075
2122
  mode = "subscription",
2076
2123
  foundingDeadline,
2077
2124
  renderPlanFooter
2078
2125
  }) {
2079
- const [annual, setAnnual] = React30.useState(false);
2126
+ const [annual, setAnnual] = React32.useState(false);
2080
2127
  const isOneTime = mode === "one-time";
2081
2128
  const uiMode = useUIVariant();
2082
2129
  const sectionClasses = cn$1(
@@ -2280,9 +2327,16 @@ function TeamSection({
2280
2327
  title = "The people behind Olwiba",
2281
2328
  description = "A small team with deep experience in design systems, open source, and developer tooling."
2282
2329
  }) {
2283
- return /* @__PURE__ */ jsx("section", { className: "overflow-hidden rounded-2xl border bg-card", children: /* @__PURE__ */ jsx("div", { className: "px-6 py-14 sm:px-10 sm:py-20", children: /* @__PURE__ */ jsxs("div", { className: "mx-auto max-w-5xl", children: [
2330
+ const mode = useUIVariant();
2331
+ const sectionClasses = cn$1(
2332
+ "overflow-hidden bg-card",
2333
+ mode === "smooth" && "rounded-3xl border",
2334
+ mode === "playful" && "rounded-2xl border-primary/25 border",
2335
+ !mode && "rounded-2xl border"
2336
+ );
2337
+ return /* @__PURE__ */ jsx("section", { className: sectionClasses, children: /* @__PURE__ */ jsx("div", { className: "px-6 py-14 sm:px-10 sm:py-20", children: /* @__PURE__ */ jsxs("div", { className: "mx-auto max-w-5xl", children: [
2284
2338
  /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
2285
- /* @__PURE__ */ jsx(Badge$1, { variant: "secondary", className: "mb-4", children: badge }),
2339
+ /* @__PURE__ */ jsx(Badge, { variant: "secondary", className: "mb-4", children: badge }),
2286
2340
  /* @__PURE__ */ jsx("h2", { className: "text-balance text-3xl font-semibold tracking-tight sm:text-4xl", children: title }),
2287
2341
  /* @__PURE__ */ jsx("p", { className: "mx-auto mt-4 max-w-xl text-pretty text-muted-foreground", children: description })
2288
2342
  ] }),
@@ -2338,10 +2392,10 @@ function CountUp({
2338
2392
  className,
2339
2393
  ...props
2340
2394
  }) {
2341
- const ref = React30.useRef(null);
2342
- const [value, setValue] = React30.useState(from);
2343
- const hasStarted = React30.useRef(false);
2344
- React30.useEffect(() => {
2395
+ const ref = React32.useRef(null);
2396
+ const [value, setValue] = React32.useState(from);
2397
+ const hasStarted = React32.useRef(false);
2398
+ React32.useEffect(() => {
2345
2399
  const el = ref.current;
2346
2400
  if (!el) return;
2347
2401
  const observer = new IntersectionObserver(
@@ -2434,20 +2488,27 @@ function NewsletterSection({
2434
2488
  successDescription = defaults.successDescription,
2435
2489
  onSubscribe
2436
2490
  } = {}) {
2437
- const [email, setEmail] = React30.useState("");
2438
- const [submitted, setSubmitted] = React30.useState(false);
2491
+ const [email, setEmail] = React32.useState("");
2492
+ const [submitted, setSubmitted] = React32.useState(false);
2493
+ const mode = useUIVariant();
2494
+ const sectionClasses = cn$1(
2495
+ "overflow-hidden bg-card",
2496
+ mode === "smooth" && "rounded-3xl border",
2497
+ mode === "playful" && "rounded-2xl border-primary/25 border",
2498
+ !mode && "rounded-2xl border"
2499
+ );
2439
2500
  async function handleSubmit(e) {
2440
2501
  e.preventDefault();
2441
2502
  if (!email) return;
2442
2503
  await onSubscribe?.(email);
2443
2504
  setSubmitted(true);
2444
2505
  }
2445
- return /* @__PURE__ */ jsx("section", { className: "overflow-hidden rounded-2xl border bg-card", children: /* @__PURE__ */ jsxs("div", { className: "relative px-6 py-14 sm:px-10 sm:py-20", children: [
2506
+ return /* @__PURE__ */ jsx("section", { className: sectionClasses, children: /* @__PURE__ */ jsxs("div", { className: "relative px-6 py-14 sm:px-10 sm:py-20", children: [
2446
2507
  /* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-[radial-gradient(ellipse_at_top_right,hsl(var(--primary)/0.1),transparent_60%)]" }),
2447
2508
  /* @__PURE__ */ jsxs("div", { className: "relative mx-auto max-w-xl text-center", children: [
2448
2509
  /* @__PURE__ */ jsxs("div", { className: "mb-4 flex items-center justify-center gap-2", children: [
2449
2510
  /* @__PURE__ */ jsx("div", { className: "inline-flex h-8 w-8 items-center justify-center rounded-xl bg-primary/10 text-primary", children: /* @__PURE__ */ jsx(Mail, { className: "size-4" }) }),
2450
- /* @__PURE__ */ jsx(Badge$1, { variant: "secondary", children: badge })
2511
+ /* @__PURE__ */ jsx(Badge, { variant: "secondary", children: badge })
2451
2512
  ] }),
2452
2513
  /* @__PURE__ */ jsx("h2", { className: "text-balance text-3xl font-semibold tracking-tight sm:text-4xl", children: title }),
2453
2514
  /* @__PURE__ */ jsx("p", { className: "mx-auto mt-4 max-w-sm text-pretty text-muted-foreground", children: description }),
@@ -2468,7 +2529,7 @@ function NewsletterSection({
2468
2529
  }
2469
2530
  ),
2470
2531
  /* @__PURE__ */ jsx(
2471
- Input$1,
2532
+ Input,
2472
2533
  {
2473
2534
  type: "email",
2474
2535
  placeholder,
@@ -2478,7 +2539,7 @@ function NewsletterSection({
2478
2539
  className: "flex-1"
2479
2540
  }
2480
2541
  ),
2481
- /* @__PURE__ */ jsxs(Button$1, { type: "submit", children: [
2542
+ /* @__PURE__ */ jsxs(Button, { type: "submit", children: [
2482
2543
  submitLabel,
2483
2544
  /* @__PURE__ */ jsx(ArrowRight, { className: "ml-2 size-4" })
2484
2545
  ] })
@@ -2519,15 +2580,22 @@ function ContactSection({
2519
2580
  sendAnotherLabel = defaults2.sendAnotherLabel,
2520
2581
  onSubmit
2521
2582
  } = {}) {
2522
- const [submitted, setSubmitted] = React30.useState(false);
2583
+ const [submitted, setSubmitted] = React32.useState(false);
2584
+ const mode = useUIVariant();
2585
+ const sectionClasses = cn$1(
2586
+ "overflow-hidden bg-card",
2587
+ mode === "smooth" && "rounded-3xl border",
2588
+ mode === "playful" && "rounded-2xl border-primary/25 border",
2589
+ !mode && "rounded-2xl border"
2590
+ );
2523
2591
  async function handleSubmit(e) {
2524
2592
  e.preventDefault();
2525
2593
  await onSubmit?.(e);
2526
2594
  setSubmitted(true);
2527
2595
  }
2528
- return /* @__PURE__ */ jsx("section", { className: "overflow-hidden rounded-2xl border bg-card", children: /* @__PURE__ */ jsxs("div", { className: "px-6 py-14 sm:px-10 sm:py-20", children: [
2596
+ return /* @__PURE__ */ jsx("section", { className: sectionClasses, children: /* @__PURE__ */ jsxs("div", { className: "px-6 py-14 sm:px-10 sm:py-20", children: [
2529
2597
  /* @__PURE__ */ jsxs("div", { className: "mx-auto max-w-2xl text-center", children: [
2530
- badge && /* @__PURE__ */ jsx("div", { className: "mb-4 flex justify-center", children: /* @__PURE__ */ jsx(Badge$1, { variant: "secondary", children: badge }) }),
2598
+ badge && /* @__PURE__ */ jsx("div", { className: "mb-4 flex justify-center", children: /* @__PURE__ */ jsx(Badge, { variant: "secondary", children: badge }) }),
2531
2599
  /* @__PURE__ */ jsx("h2", { className: "text-balance text-4xl font-semibold tracking-tight sm:text-5xl", children: title }),
2532
2600
  /* @__PURE__ */ jsx("p", { className: "mt-4 text-lg text-muted-foreground", children: description }),
2533
2601
  contactInfo && contactInfo.length > 0 && /* @__PURE__ */ jsx("div", { className: "mt-8 flex flex-wrap justify-center gap-6", children: contactInfo.map(({ icon: Icon = Mail, label, value }) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-sm text-muted-foreground", children: [
@@ -2539,7 +2607,7 @@ function ContactSection({
2539
2607
  /* @__PURE__ */ jsx("div", { className: "mx-auto flex h-14 w-14 items-center justify-center rounded-2xl bg-primary/10 text-primary", children: /* @__PURE__ */ jsx(MessageSquare, { className: "size-6" }) }),
2540
2608
  /* @__PURE__ */ jsx("h3", { className: "mt-5 text-lg font-semibold", children: successTitle }),
2541
2609
  /* @__PURE__ */ jsx("p", { className: "mt-2 text-sm text-muted-foreground", children: successDescription }),
2542
- /* @__PURE__ */ jsx(Button$1, { variant: "outline", size: "sm", className: "mt-6", onClick: () => setSubmitted(false), children: sendAnotherLabel })
2610
+ /* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", className: "mt-6", onClick: () => setSubmitted(false), children: sendAnotherLabel })
2543
2611
  ] }) : /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, className: "mx-auto mt-16 max-w-xl sm:mt-20", children: [
2544
2612
  /* @__PURE__ */ jsx(
2545
2613
  "input",
@@ -2556,7 +2624,7 @@ function ContactSection({
2556
2624
  /* @__PURE__ */ jsxs("div", { children: [
2557
2625
  /* @__PURE__ */ jsx(Label, { htmlFor: "contact-first", className: "block text-sm/6 font-semibold", children: "First name" }),
2558
2626
  /* @__PURE__ */ jsx("div", { className: "mt-2.5", children: /* @__PURE__ */ jsx(
2559
- Input$1,
2627
+ Input,
2560
2628
  {
2561
2629
  id: "contact-first",
2562
2630
  name: "firstName",
@@ -2569,7 +2637,7 @@ function ContactSection({
2569
2637
  /* @__PURE__ */ jsxs("div", { children: [
2570
2638
  /* @__PURE__ */ jsx(Label, { htmlFor: "contact-last", className: "block text-sm/6 font-semibold", children: "Last name" }),
2571
2639
  /* @__PURE__ */ jsx("div", { className: "mt-2.5", children: /* @__PURE__ */ jsx(
2572
- Input$1,
2640
+ Input,
2573
2641
  {
2574
2642
  id: "contact-last",
2575
2643
  name: "lastName",
@@ -2586,7 +2654,7 @@ function ContactSection({
2586
2654
  /* @__PURE__ */ jsx("span", { className: "font-normal text-muted-foreground", children: "(optional)" })
2587
2655
  ] }),
2588
2656
  /* @__PURE__ */ jsx("div", { className: "mt-2.5", children: /* @__PURE__ */ jsx(
2589
- Input$1,
2657
+ Input,
2590
2658
  {
2591
2659
  id: "contact-company",
2592
2660
  name: "company",
@@ -2598,7 +2666,7 @@ function ContactSection({
2598
2666
  /* @__PURE__ */ jsxs("div", { className: "sm:col-span-2", children: [
2599
2667
  /* @__PURE__ */ jsx(Label, { htmlFor: "contact-email", className: "block text-sm/6 font-semibold", children: "Email" }),
2600
2668
  /* @__PURE__ */ jsx("div", { className: "mt-2.5", children: /* @__PURE__ */ jsx(
2601
- Input$1,
2669
+ Input,
2602
2670
  {
2603
2671
  id: "contact-email",
2604
2672
  name: "email",
@@ -2644,7 +2712,7 @@ function ContactSection({
2644
2712
  /* @__PURE__ */ jsxs("div", { className: "sm:col-span-2", children: [
2645
2713
  /* @__PURE__ */ jsx(Label, { htmlFor: "contact-message", className: "block text-sm/6 font-semibold", children: "Message" }),
2646
2714
  /* @__PURE__ */ jsx("div", { className: "mt-2.5", children: /* @__PURE__ */ jsx(
2647
- Textarea$1,
2715
+ Textarea,
2648
2716
  {
2649
2717
  id: "contact-message",
2650
2718
  name: "message",
@@ -2692,7 +2760,7 @@ function ContactSection({
2692
2760
  )
2693
2761
  ] })
2694
2762
  ] }),
2695
- /* @__PURE__ */ jsx("div", { className: "mt-10", children: /* @__PURE__ */ jsxs(Button$1, { type: "submit", className: "w-full", children: [
2763
+ /* @__PURE__ */ jsx("div", { className: "mt-10", children: /* @__PURE__ */ jsxs(Button, { type: "submit", className: "w-full", children: [
2696
2764
  "Let's talk",
2697
2765
  /* @__PURE__ */ jsx(Send, { className: "ml-2 size-4" })
2698
2766
  ] }) })
@@ -2802,15 +2870,15 @@ function QualificationSection({
2802
2870
  }) })
2803
2871
  ] }) }) });
2804
2872
  }
2805
- var defaultRenderLink8 = ({ href, children, className }) => /* @__PURE__ */ jsx("a", { href, className, children });
2873
+ var defaultRenderLink7 = ({ href, children, className }) => /* @__PURE__ */ jsx("a", { href, className, children });
2806
2874
  function Navbar({
2807
2875
  brand,
2808
2876
  navLinks,
2809
2877
  cta,
2810
2878
  controls,
2811
- renderLink = defaultRenderLink8
2879
+ renderLink = defaultRenderLink7
2812
2880
  }) {
2813
- const [open, setOpen] = React30.useState(false);
2881
+ const [open, setOpen] = React32.useState(false);
2814
2882
  const brandHref = brand.href ?? "/";
2815
2883
  const hasRightContent = controls?.length || cta?.primary || cta?.secondary;
2816
2884
  return /* @__PURE__ */ jsx("section", { className: "overflow-hidden rounded-2xl border bg-card", children: /* @__PURE__ */ jsxs("nav", { className: "grid h-16 grid-cols-[1fr_auto_1fr] items-center px-6", children: [
@@ -2827,7 +2895,7 @@ function Navbar({
2827
2895
  children: link.label
2828
2896
  }) }, link.label)) }),
2829
2897
  hasRightContent && /* @__PURE__ */ jsxs("div", { className: "hidden items-center justify-end gap-2 md:flex", children: [
2830
- controls?.map((control, i) => /* @__PURE__ */ jsx(React30.Fragment, { children: control }, i)),
2898
+ controls?.map((control, i) => /* @__PURE__ */ jsx(React32.Fragment, { children: control }, i)),
2831
2899
  (cta?.secondary || cta?.primary) && controls?.length ? /* @__PURE__ */ jsx("div", { className: "mx-1 h-4 w-px bg-border" }) : null,
2832
2900
  cta?.secondary && renderLink({
2833
2901
  href: cta.secondary.href,
@@ -2851,7 +2919,7 @@ function Navbar({
2851
2919
  /* @__PURE__ */ jsx("span", { children: brand.name })
2852
2920
  ] })
2853
2921
  }) }) }),
2854
- controls?.length ? /* @__PURE__ */ jsx("div", { className: "mb-4 flex w-full items-center justify-between gap-1", children: controls.map((control, i) => /* @__PURE__ */ jsx(React30.Fragment, { children: control }, i)) }) : null,
2922
+ controls?.length ? /* @__PURE__ */ jsx("div", { className: "mb-4 flex w-full items-center justify-between gap-1", children: controls.map((control, i) => /* @__PURE__ */ jsx(React32.Fragment, { children: control }, i)) }) : null,
2855
2923
  /* @__PURE__ */ jsx(Separator, {}),
2856
2924
  /* @__PURE__ */ jsx("nav", { className: "mt-4 flex flex-col gap-1", children: navLinks.map((link) => /* @__PURE__ */ jsx("span", { onClick: () => setOpen(false), children: renderLink({
2857
2925
  href: link.href,
@@ -2872,14 +2940,14 @@ function Navbar({
2872
2940
  ] })
2873
2941
  ] }) });
2874
2942
  }
2875
- var defaultRenderLink9 = ({ href, children, className }) => /* @__PURE__ */ jsx("a", { href, className, children });
2943
+ var defaultRenderLink8 = ({ href, children, className }) => /* @__PURE__ */ jsx("a", { href, className, children });
2876
2944
  function Footer({
2877
2945
  brand,
2878
2946
  navLinks,
2879
2947
  socialLinks,
2880
2948
  legal,
2881
2949
  status,
2882
- renderLink = defaultRenderLink9
2950
+ renderLink = defaultRenderLink8
2883
2951
  }) {
2884
2952
  const brandHref = brand.href ?? "/";
2885
2953
  const copyrightText = legal ?? `\xA9 ${(/* @__PURE__ */ new Date()).getFullYear()} ${brand.name}. All rights reserved.`;
@@ -3000,7 +3068,7 @@ function LogoStrip({
3000
3068
  ] });
3001
3069
  }
3002
3070
  function GridUnderlay({ variant, size = 24, className, ...props }) {
3003
- const id = React30.useId();
3071
+ const id = React32.useId();
3004
3072
  const pattern = variant === "dots" ? /* @__PURE__ */ jsx("circle", { cx: size / 2, cy: size / 2, r: 1, fill: "currentColor" }) : variant === "lines" ? /* @__PURE__ */ jsx("path", { d: `M ${size} 0 L 0 0 0 ${size}`, fill: "none", stroke: "currentColor", strokeWidth: 0.5 }) : /* @__PURE__ */ jsxs(Fragment, { children: [
3005
3073
  /* @__PURE__ */ jsx("line", { x1: size / 2, y1: 0, x2: size / 2, y2: size, stroke: "currentColor", strokeWidth: 0.5 }),
3006
3074
  /* @__PURE__ */ jsx("line", { x1: 0, y1: size / 2, x2: size, y2: size / 2, stroke: "currentColor", strokeWidth: 0.5 })
@@ -3028,9 +3096,9 @@ function GlowUnderlay({
3028
3096
  style,
3029
3097
  ...props
3030
3098
  }) {
3031
- const [pos, setPos] = React30.useState({ x: "50%", y: "50%" });
3032
- const containerRef = React30.useRef(null);
3033
- React30.useEffect(() => {
3099
+ const [pos, setPos] = React32.useState({ x: "50%", y: "50%" });
3100
+ const containerRef = React32.useRef(null);
3101
+ React32.useEffect(() => {
3034
3102
  if (!followCursor) return;
3035
3103
  const handleMouseMove = (e) => {
3036
3104
  const el = containerRef.current;
@@ -3169,8 +3237,8 @@ function PageTransition({
3169
3237
  style,
3170
3238
  ...props
3171
3239
  }) {
3172
- const [mounted, setMounted] = React30.useState(false);
3173
- React30.useEffect(() => {
3240
+ const [mounted, setMounted] = React32.useState(false);
3241
+ React32.useEffect(() => {
3174
3242
  const id = requestAnimationFrame(() => setMounted(true));
3175
3243
  return () => cancelAnimationFrame(id);
3176
3244
  }, []);
@@ -3190,6 +3258,66 @@ function PageTransition({
3190
3258
  }
3191
3259
  );
3192
3260
  }
3261
+ var strategies = {
3262
+ vertical: verticalListSortingStrategy,
3263
+ horizontal: horizontalListSortingStrategy,
3264
+ grid: rectSortingStrategy
3265
+ };
3266
+ var containerClasses = {
3267
+ vertical: "flex flex-col gap-2",
3268
+ horizontal: "flex gap-2",
3269
+ grid: "grid gap-2"
3270
+ };
3271
+ function SortableItem({
3272
+ id,
3273
+ disabled,
3274
+ className,
3275
+ children
3276
+ }) {
3277
+ const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ id, disabled });
3278
+ return /* @__PURE__ */ jsx(
3279
+ "div",
3280
+ {
3281
+ ref: setNodeRef,
3282
+ style: { transform: CSS.Transform.toString(transform), transition },
3283
+ className: cn$1(
3284
+ "touch-none",
3285
+ !disabled && "cursor-grab active:cursor-grabbing",
3286
+ isDragging && "relative z-10 opacity-80",
3287
+ className
3288
+ ),
3289
+ ...attributes,
3290
+ ...listeners,
3291
+ children
3292
+ }
3293
+ );
3294
+ }
3295
+ function Sortable({
3296
+ items,
3297
+ onReorder,
3298
+ children,
3299
+ direction = "vertical",
3300
+ disabled,
3301
+ className,
3302
+ itemClassName
3303
+ }) {
3304
+ const sensors = useSensors(
3305
+ useSensor(PointerSensor, { activationConstraint: { distance: 4 } }),
3306
+ useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates })
3307
+ );
3308
+ const handleDragEnd = (event) => {
3309
+ const { active, over } = event;
3310
+ if (!over || active.id === over.id) return;
3311
+ const oldIndex = items.indexOf(String(active.id));
3312
+ const newIndex = items.indexOf(String(over.id));
3313
+ if (oldIndex === -1 || newIndex === -1) return;
3314
+ onReorder(arrayMove(items, oldIndex, newIndex));
3315
+ };
3316
+ const childArray = React32.Children.toArray(children);
3317
+ return /* @__PURE__ */ jsx(DndContext, { sensors, collisionDetection: closestCenter, onDragEnd: handleDragEnd, children: /* @__PURE__ */ jsx(SortableContext, { items, strategy: strategies[direction], children: /* @__PURE__ */ jsx("div", { className: cn$1(containerClasses[direction], className), children: childArray.map(
3318
+ (child, i) => items[i] === void 0 ? null : /* @__PURE__ */ jsx(SortableItem, { id: items[i], disabled, className: itemClassName, children: child }, items[i])
3319
+ ) }) }) });
3320
+ }
3193
3321
  function Spotlight({
3194
3322
  groups,
3195
3323
  placeholder = "Search anything...",
@@ -3197,8 +3325,8 @@ function Spotlight({
3197
3325
  triggerClassName,
3198
3326
  shortcut = "\u2318K"
3199
3327
  }) {
3200
- const [open, setOpen] = React30.useState(false);
3201
- React30.useEffect(() => {
3328
+ const [open, setOpen] = React32.useState(false);
3329
+ React32.useEffect(() => {
3202
3330
  const down = (e) => {
3203
3331
  if (e.key === "k" && (e.metaKey || e.ctrlKey)) {
3204
3332
  e.preventDefault();
@@ -3228,7 +3356,7 @@ function Spotlight({
3228
3356
  /* @__PURE__ */ jsx(CommandInput, { placeholder }),
3229
3357
  /* @__PURE__ */ jsxs(CommandList, { children: [
3230
3358
  /* @__PURE__ */ jsx(CommandEmpty, { children: "No results found." }),
3231
- groups.map((group, i) => /* @__PURE__ */ jsxs(React30.Fragment, { children: [
3359
+ groups.map((group, i) => /* @__PURE__ */ jsxs(React32.Fragment, { children: [
3232
3360
  i > 0 && /* @__PURE__ */ jsx(CommandSeparator, {}),
3233
3361
  /* @__PURE__ */ jsx(CommandGroup, { heading: group.heading, children: group.items.map((item) => /* @__PURE__ */ jsxs(
3234
3362
  CommandItem,
@@ -3253,8 +3381,8 @@ function Spotlight({
3253
3381
  var BASE = 40;
3254
3382
  var MAGNIFY_RADIUS = 80;
3255
3383
  function Dock({ items, iconSize = BASE, magnification = 1.7, className, ...props }) {
3256
- const [mouseX, setMouseX] = React30.useState(null);
3257
- const containerRef = React30.useRef(null);
3384
+ const [mouseX, setMouseX] = React32.useState(null);
3385
+ const containerRef = React32.useRef(null);
3258
3386
  function getScale(index) {
3259
3387
  if (mouseX === null || !containerRef.current) return 1;
3260
3388
  const container = containerRef.current;
@@ -3353,7 +3481,7 @@ function ConfirmDialog({ isOpen, options, handleConfirm, handleCancel, destructi
3353
3481
  ] }) });
3354
3482
  }
3355
3483
  function RegisterHotkeys({ hotkeys }) {
3356
- React30.useEffect(() => {
3484
+ React32.useEffect(() => {
3357
3485
  const handleKeyDown = (event) => {
3358
3486
  for (const hotkey of hotkeys) {
3359
3487
  const matches = event.key.toLowerCase() === hotkey.key.toLowerCase() && !!event.ctrlKey === !!hotkey.ctrl && !!event.metaKey === !!hotkey.meta && !!event.shiftKey === !!hotkey.shift && !!event.altKey === !!hotkey.alt;
@@ -3370,10 +3498,10 @@ function RegisterHotkeys({ hotkeys }) {
3370
3498
  return null;
3371
3499
  }
3372
3500
  function useControlledOpen(defaultOpen = false) {
3373
- const [isOpen, setIsOpen] = React30.useState(defaultOpen);
3374
- const open = React30.useCallback(() => setIsOpen(true), []);
3375
- const close = React30.useCallback(() => setIsOpen(false), []);
3376
- const toggle = React30.useCallback(() => setIsOpen((prev) => !prev), []);
3501
+ const [isOpen, setIsOpen] = React32.useState(defaultOpen);
3502
+ const open = React32.useCallback(() => setIsOpen(true), []);
3503
+ const close = React32.useCallback(() => setIsOpen(false), []);
3504
+ const toggle = React32.useCallback(() => setIsOpen((prev) => !prev), []);
3377
3505
  return { isOpen, open, close, toggle, setIsOpen };
3378
3506
  }
3379
3507
  function CommandMenu({
@@ -3386,7 +3514,13 @@ function CommandMenu({
3386
3514
  }) {
3387
3515
  const internal = useControlledOpen(false);
3388
3516
  const isOpen = openProp ?? internal.isOpen;
3389
- const setOpen = onOpenChange ?? internal.setIsOpen;
3517
+ const setOpen = React32.useCallback(
3518
+ (next) => {
3519
+ if (openProp === void 0) internal.setIsOpen(next);
3520
+ onOpenChange?.(next);
3521
+ },
3522
+ [openProp, onOpenChange, internal.setIsOpen]
3523
+ );
3390
3524
  const runItem = (item) => {
3391
3525
  setOpen(false);
3392
3526
  item.onSelect();
@@ -3405,7 +3539,7 @@ function CommandMenu({
3405
3539
  /* @__PURE__ */ jsx(CommandInput, { placeholder }),
3406
3540
  /* @__PURE__ */ jsxs(CommandList, { children: [
3407
3541
  /* @__PURE__ */ jsx(CommandEmpty, { children: emptyMessage }),
3408
- groups.map((group, i) => /* @__PURE__ */ jsxs(React30.Fragment, { children: [
3542
+ groups.map((group, i) => /* @__PURE__ */ jsxs(React32.Fragment, { children: [
3409
3543
  i > 0 && /* @__PURE__ */ jsx(CommandSeparator, {}),
3410
3544
  /* @__PURE__ */ jsx(CommandGroup, { heading: group.heading, children: group.items.map((item) => /* @__PURE__ */ jsxs(
3411
3545
  CommandItem,
@@ -3425,6 +3559,157 @@ function CommandMenu({
3425
3559
  ] })
3426
3560
  ] });
3427
3561
  }
3562
+ var TOKEN_COLORS = ["var(--chart-1)", "var(--chart-2)", "var(--chart-3)", "var(--chart-4)", "var(--chart-5)"];
3563
+ var seriesColor = (s, i) => s.color ?? TOKEN_COLORS[i % TOKEN_COLORS.length];
3564
+ function ChartTooltip({
3565
+ active,
3566
+ payload,
3567
+ label,
3568
+ valueFormatter
3569
+ }) {
3570
+ if (!active || !payload?.length) return null;
3571
+ return /* @__PURE__ */ jsxs("div", { className: "rounded-lg border bg-popover px-3 py-2 text-xs text-popover-foreground shadow-md", children: [
3572
+ label !== void 0 && /* @__PURE__ */ jsx("p", { className: "mb-1 font-medium", children: label }),
3573
+ /* @__PURE__ */ jsx("div", { className: "space-y-1", children: payload.map((entry, i) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
3574
+ /* @__PURE__ */ jsx(
3575
+ "span",
3576
+ {
3577
+ "aria-hidden": true,
3578
+ className: "size-2 shrink-0 rounded-full",
3579
+ style: { background: entry.color ?? entry.payload?.fill }
3580
+ }
3581
+ ),
3582
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: entry.name }),
3583
+ /* @__PURE__ */ jsx("span", { className: "ml-auto pl-3 font-medium tabular-nums", children: typeof entry.value === "number" ? valueFormatter(entry.value) : entry.value })
3584
+ ] }, i)) })
3585
+ ] });
3586
+ }
3587
+ function ChartLegend({ entries }) {
3588
+ return /* @__PURE__ */ jsx("div", { className: "mt-3 flex flex-wrap items-center justify-center gap-x-4 gap-y-1", children: entries.map((entry) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 text-xs text-muted-foreground", children: [
3589
+ /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "size-2 rounded-full", style: { background: entry.color } }),
3590
+ entry.label
3591
+ ] }, entry.label)) });
3592
+ }
3593
+ var axisProps = {
3594
+ tickLine: false,
3595
+ axisLine: false,
3596
+ tick: { fill: "var(--muted-foreground)", fontSize: 12 },
3597
+ tickMargin: 8
3598
+ };
3599
+ function Chart({
3600
+ type = "line",
3601
+ data,
3602
+ xKey,
3603
+ series,
3604
+ height = 300,
3605
+ grid = true,
3606
+ legend,
3607
+ valueFormatter = (v) => String(v),
3608
+ className
3609
+ }) {
3610
+ const showLegend = legend ?? (type === "donut" || series.length > 1);
3611
+ const tooltip = /* @__PURE__ */ jsx(
3612
+ Tooltip$1,
3613
+ {
3614
+ cursor: type === "bar" ? { fill: "var(--muted)", opacity: 0.4 } : { stroke: "var(--border)" },
3615
+ content: /* @__PURE__ */ jsx(ChartTooltip, { valueFormatter })
3616
+ }
3617
+ );
3618
+ const gridEl = grid ? /* @__PURE__ */ jsx(CartesianGrid, { vertical: false, stroke: "var(--border)", strokeOpacity: 0.6 }) : null;
3619
+ let chart;
3620
+ let legendEntries;
3621
+ if (type === "donut") {
3622
+ const valueKey = series[0]?.key;
3623
+ legendEntries = data.map((row, i) => ({
3624
+ label: String(row[xKey]),
3625
+ color: TOKEN_COLORS[i % TOKEN_COLORS.length]
3626
+ }));
3627
+ chart = /* @__PURE__ */ jsxs(PieChart, { children: [
3628
+ /* @__PURE__ */ jsx(
3629
+ Pie,
3630
+ {
3631
+ data,
3632
+ dataKey: valueKey,
3633
+ nameKey: xKey,
3634
+ innerRadius: "60%",
3635
+ outerRadius: "85%",
3636
+ paddingAngle: 2,
3637
+ stroke: "var(--card)",
3638
+ strokeWidth: 2,
3639
+ children: data.map((row, i) => /* @__PURE__ */ jsx(Cell, { fill: TOKEN_COLORS[i % TOKEN_COLORS.length] }, String(row[xKey])))
3640
+ }
3641
+ ),
3642
+ tooltip
3643
+ ] });
3644
+ } else {
3645
+ legendEntries = series.map((s, i) => ({ label: s.label ?? s.key, color: seriesColor(s, i) }));
3646
+ if (type === "bar") {
3647
+ chart = /* @__PURE__ */ jsxs(BarChart, { data, barCategoryGap: "25%", children: [
3648
+ gridEl,
3649
+ /* @__PURE__ */ jsx(XAxis, { dataKey: xKey, ...axisProps }),
3650
+ /* @__PURE__ */ jsx(YAxis, { ...axisProps, width: 48, tickFormatter: valueFormatter }),
3651
+ tooltip,
3652
+ series.map((s, i) => /* @__PURE__ */ jsx(
3653
+ Bar,
3654
+ {
3655
+ dataKey: s.key,
3656
+ name: s.label ?? s.key,
3657
+ fill: seriesColor(s, i),
3658
+ radius: [4, 4, 0, 0],
3659
+ maxBarSize: 40
3660
+ },
3661
+ s.key
3662
+ ))
3663
+ ] });
3664
+ } else if (type === "area") {
3665
+ chart = /* @__PURE__ */ jsxs(AreaChart, { data, children: [
3666
+ gridEl,
3667
+ /* @__PURE__ */ jsx(XAxis, { dataKey: xKey, ...axisProps }),
3668
+ /* @__PURE__ */ jsx(YAxis, { ...axisProps, width: 48, tickFormatter: valueFormatter }),
3669
+ tooltip,
3670
+ series.map((s, i) => /* @__PURE__ */ jsx(
3671
+ Area,
3672
+ {
3673
+ type: "monotone",
3674
+ dataKey: s.key,
3675
+ name: s.label ?? s.key,
3676
+ stroke: seriesColor(s, i),
3677
+ strokeWidth: 2,
3678
+ fill: seriesColor(s, i),
3679
+ fillOpacity: 0.12,
3680
+ dot: false,
3681
+ activeDot: { r: 4 }
3682
+ },
3683
+ s.key
3684
+ ))
3685
+ ] });
3686
+ } else {
3687
+ chart = /* @__PURE__ */ jsxs(LineChart, { data, children: [
3688
+ gridEl,
3689
+ /* @__PURE__ */ jsx(XAxis, { dataKey: xKey, ...axisProps }),
3690
+ /* @__PURE__ */ jsx(YAxis, { ...axisProps, width: 48, tickFormatter: valueFormatter }),
3691
+ tooltip,
3692
+ series.map((s, i) => /* @__PURE__ */ jsx(
3693
+ Line,
3694
+ {
3695
+ type: "monotone",
3696
+ dataKey: s.key,
3697
+ name: s.label ?? s.key,
3698
+ stroke: seriesColor(s, i),
3699
+ strokeWidth: 2,
3700
+ dot: false,
3701
+ activeDot: { r: 4 }
3702
+ },
3703
+ s.key
3704
+ ))
3705
+ ] });
3706
+ }
3707
+ }
3708
+ return /* @__PURE__ */ jsxs("div", { className: cn$1("w-full", className), children: [
3709
+ /* @__PURE__ */ jsx("div", { style: { height }, children: /* @__PURE__ */ jsx(ResponsiveContainer, { width: "100%", height: "100%", children: chart }) }),
3710
+ showLegend && /* @__PURE__ */ jsx(ChartLegend, { entries: legendEntries })
3711
+ ] });
3712
+ }
3428
3713
  function formatBytes(bytes) {
3429
3714
  if (bytes < 1024) return `${bytes} B`;
3430
3715
  if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(0)} KB`;
@@ -3446,19 +3731,20 @@ function FileUpload({
3446
3731
  hint,
3447
3732
  className
3448
3733
  }) {
3449
- const [internalFiles, setInternalFiles] = React30.useState(defaultFiles ?? []);
3450
- const [isDragging, setIsDragging] = React30.useState(false);
3451
- const [validationError, setValidationError] = React30.useState(null);
3452
- const inputRef = React30.useRef(null);
3734
+ const [internalFiles, setInternalFiles] = React32.useState(defaultFiles ?? []);
3735
+ const [isDragging, setIsDragging] = React32.useState(false);
3736
+ const [validationError, setValidationError] = React32.useState(null);
3737
+ const inputRef = React32.useRef(null);
3738
+ const dragDepth = React32.useRef(0);
3453
3739
  const files = filesProp ?? internalFiles;
3454
- const setFiles = React30.useCallback(
3740
+ const setFiles = React32.useCallback(
3455
3741
  (next) => {
3456
3742
  if (!filesProp) setInternalFiles(next);
3457
3743
  onFilesChange?.(next);
3458
3744
  },
3459
3745
  [filesProp, onFilesChange]
3460
3746
  );
3461
- const acceptList = React30.useMemo(
3747
+ const acceptList = React32.useMemo(
3462
3748
  () => accept?.split(",").map((a) => a.trim().toLowerCase()).filter(Boolean) ?? [],
3463
3749
  [accept]
3464
3750
  );
@@ -3472,7 +3758,7 @@ function FileUpload({
3472
3758
  const handleFiles = (fileList) => {
3473
3759
  if (!fileList || disabled) return;
3474
3760
  const incoming = Array.from(fileList);
3475
- const room = maxFiles ? Math.max(0, maxFiles - files.length) : Infinity;
3761
+ const room = maxFiles && multiple ? Math.max(0, maxFiles - files.length) : maxFiles || Infinity;
3476
3762
  if (maxFiles && room <= 0) {
3477
3763
  setValidationError(`You can only add up to ${maxFiles} file${maxFiles === 1 ? "" : "s"}.`);
3478
3764
  return;
@@ -3507,15 +3793,25 @@ function FileUpload({
3507
3793
  tabIndex: disabled ? -1 : 0,
3508
3794
  onClick: () => !disabled && inputRef.current?.click(),
3509
3795
  onKeyDown: (e) => {
3510
- if (e.key === "Enter" || e.key === " ") inputRef.current?.click();
3796
+ if (disabled) return;
3797
+ if (e.key === "Enter" || e.key === " ") {
3798
+ e.preventDefault();
3799
+ inputRef.current?.click();
3800
+ }
3511
3801
  },
3512
- onDragOver: (e) => {
3802
+ onDragOver: (e) => e.preventDefault(),
3803
+ onDragEnter: (e) => {
3513
3804
  e.preventDefault();
3805
+ dragDepth.current += 1;
3514
3806
  if (!disabled) setIsDragging(true);
3515
3807
  },
3516
- onDragLeave: () => setIsDragging(false),
3808
+ onDragLeave: () => {
3809
+ dragDepth.current = Math.max(0, dragDepth.current - 1);
3810
+ if (dragDepth.current === 0) setIsDragging(false);
3811
+ },
3517
3812
  onDrop: (e) => {
3518
3813
  e.preventDefault();
3814
+ dragDepth.current = 0;
3519
3815
  setIsDragging(false);
3520
3816
  handleFiles(e.dataTransfer.files);
3521
3817
  },
@@ -3549,7 +3845,7 @@ function FileUpload({
3549
3845
  ]
3550
3846
  }
3551
3847
  ),
3552
- validationError && /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-destructive", children: validationError }),
3848
+ validationError && /* @__PURE__ */ jsx("p", { role: "alert", className: "text-sm font-medium text-destructive", children: validationError }),
3553
3849
  files.length > 0 && /* @__PURE__ */ jsx("ul", { className: "space-y-2", children: files.map(({ id, file, progress, status = "pending", error }) => /* @__PURE__ */ jsxs("li", { className: "flex items-center gap-3 rounded-lg border bg-card/60 px-3 py-2.5", children: [
3554
3850
  /* @__PURE__ */ jsx(File, { className: "size-5 shrink-0 text-muted-foreground" }),
3555
3851
  /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
@@ -3586,6 +3882,8 @@ function FileUpload({
3586
3882
  var variantIcon = {
3587
3883
  success: /* @__PURE__ */ jsx(CheckCircle2, { className: "size-5 text-primary" }),
3588
3884
  info: /* @__PURE__ */ jsx(Info, { className: "size-5 text-muted-foreground" }),
3885
+ warning: /* @__PURE__ */ jsx(AlertTriangle, { className: "size-5 text-amber-500 dark:text-amber-400" }),
3886
+ error: /* @__PURE__ */ jsx(AlertCircle, { className: "size-5 text-destructive" }),
3589
3887
  message: /* @__PURE__ */ jsx(Inbox, { className: "size-5 text-muted-foreground" })
3590
3888
  };
3591
3889
  function NotificationToast({
@@ -3620,6 +3918,104 @@ function notify(options) {
3620
3918
  const { duration, ...toastProps } = options;
3621
3919
  return toast.custom((id) => /* @__PURE__ */ jsx(NotificationToast, { ...toastProps, onDismiss: () => toast.dismiss(id) }), { duration });
3622
3920
  }
3921
+ function NotificationsPopover({
3922
+ notifications,
3923
+ onNotificationClick,
3924
+ onMarkAllRead,
3925
+ title = "Notifications",
3926
+ emptyMessage = "Nothing new \u2014 you\u2019re all caught up.",
3927
+ align = "end",
3928
+ open,
3929
+ onOpenChange,
3930
+ className
3931
+ }) {
3932
+ const unreadCount = notifications.filter((n) => !n.read).length;
3933
+ return /* @__PURE__ */ jsxs(Popover, { open, onOpenChange, children: [
3934
+ /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(Button, { variant: "ghost", size: "icon", className: cn$1("relative size-8", className), children: [
3935
+ /* @__PURE__ */ jsx(Bell, { className: "size-4" }),
3936
+ unreadCount > 0 && /* @__PURE__ */ jsx("span", { className: "absolute -right-0.5 -top-0.5 flex min-w-4 items-center justify-center rounded-full bg-primary px-1 text-[10px] font-semibold leading-4 text-primary-foreground", children: unreadCount > 9 ? "9+" : unreadCount }),
3937
+ /* @__PURE__ */ jsxs("span", { className: "sr-only", children: [
3938
+ title,
3939
+ unreadCount > 0 ? ` (${unreadCount} unread)` : ""
3940
+ ] })
3941
+ ] }) }),
3942
+ /* @__PURE__ */ jsxs(PopoverContent, { align, className: "w-80 p-0", children: [
3943
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between border-b px-4 py-3", children: [
3944
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-semibold", children: title }),
3945
+ onMarkAllRead && unreadCount > 0 && /* @__PURE__ */ jsx(
3946
+ "button",
3947
+ {
3948
+ type: "button",
3949
+ onClick: onMarkAllRead,
3950
+ className: "text-xs font-medium text-muted-foreground hover:text-foreground",
3951
+ children: "Mark all read"
3952
+ }
3953
+ )
3954
+ ] }),
3955
+ notifications.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-2 px-4 py-10 text-center", children: [
3956
+ /* @__PURE__ */ jsx(Inbox, { className: "size-6 text-muted-foreground" }),
3957
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: emptyMessage })
3958
+ ] }) : /* @__PURE__ */ jsx("ul", { className: "max-h-80 overflow-y-auto", children: notifications.map((notification) => /* @__PURE__ */ jsx("li", { className: "border-b last:border-b-0", children: /* @__PURE__ */ jsxs(
3959
+ "button",
3960
+ {
3961
+ type: "button",
3962
+ onClick: () => onNotificationClick?.(notification),
3963
+ className: cn$1(
3964
+ "flex w-full items-start gap-3 px-4 py-3 text-left transition-colors hover:bg-muted/60",
3965
+ !notification.read && "bg-muted/40"
3966
+ ),
3967
+ children: [
3968
+ /* @__PURE__ */ jsx(
3969
+ "span",
3970
+ {
3971
+ "aria-hidden": true,
3972
+ className: cn$1(
3973
+ "mt-1.5 size-2 shrink-0 rounded-full",
3974
+ notification.read ? "bg-transparent" : "bg-primary"
3975
+ )
3976
+ }
3977
+ ),
3978
+ notification.avatar ? /* @__PURE__ */ jsxs(Avatar, { className: "size-8 shrink-0", children: [
3979
+ /* @__PURE__ */ jsx(AvatarImage, { src: notification.avatar, alt: "" }),
3980
+ /* @__PURE__ */ jsx(AvatarFallback, { children: notification.title.slice(0, 2).toUpperCase() })
3981
+ ] }) : notification.icon ? /* @__PURE__ */ jsx("span", { className: "mt-0.5 shrink-0 text-muted-foreground", children: notification.icon }) : null,
3982
+ /* @__PURE__ */ jsxs("span", { className: "min-w-0 flex-1", children: [
3983
+ /* @__PURE__ */ jsx("span", { className: cn$1("block truncate text-sm", !notification.read && "font-medium"), children: notification.title }),
3984
+ notification.description && /* @__PURE__ */ jsx("span", { className: "mt-0.5 line-clamp-2 block text-xs text-muted-foreground", children: notification.description }),
3985
+ notification.timestamp && /* @__PURE__ */ jsx("span", { className: "mt-1 block text-xs text-muted-foreground/70", children: notification.timestamp })
3986
+ ] })
3987
+ ]
3988
+ }
3989
+ ) }, notification.id)) })
3990
+ ] })
3991
+ ] });
3992
+ }
3993
+ function ActivityFeed({
3994
+ items,
3995
+ emptyMessage = "No activity yet.",
3996
+ className
3997
+ }) {
3998
+ if (items.length === 0) {
3999
+ return /* @__PURE__ */ jsxs("div", { className: cn$1("flex flex-col items-center gap-2 py-10 text-center", className), children: [
4000
+ /* @__PURE__ */ jsx(Activity, { className: "size-6 text-muted-foreground" }),
4001
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: emptyMessage })
4002
+ ] });
4003
+ }
4004
+ return /* @__PURE__ */ jsx("ol", { className, children: items.map((item, i) => /* @__PURE__ */ jsxs("li", { className: "relative flex gap-3 pb-6 last:pb-0", children: [
4005
+ i !== items.length - 1 && /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "absolute left-4 top-9 bottom-0 w-px -translate-x-1/2 bg-border" }),
4006
+ /* @__PURE__ */ jsx("span", { className: "relative z-10 flex size-8 shrink-0 items-center justify-center", children: item.avatar ? /* @__PURE__ */ jsxs(Avatar, { className: "size-8", children: [
4007
+ /* @__PURE__ */ jsx(AvatarImage, { src: item.avatar, alt: "" }),
4008
+ /* @__PURE__ */ jsx(AvatarFallback, { className: "text-xs", children: item.initials ?? "?" })
4009
+ ] }) : /* @__PURE__ */ jsx("span", { className: "flex size-8 items-center justify-center rounded-full border bg-card text-muted-foreground [&>svg]:size-4", children: item.icon ?? /* @__PURE__ */ jsx(Activity, {}) }) }),
4010
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1 pt-1", children: [
4011
+ /* @__PURE__ */ jsxs("div", { className: "flex items-baseline justify-between gap-2", children: [
4012
+ /* @__PURE__ */ jsx("p", { className: "text-sm", children: item.title }),
4013
+ item.timestamp && /* @__PURE__ */ jsx("span", { className: "shrink-0 text-xs text-muted-foreground", children: item.timestamp })
4014
+ ] }),
4015
+ item.description && /* @__PURE__ */ jsx("p", { className: "mt-0.5 text-xs text-muted-foreground", children: item.description })
4016
+ ] })
4017
+ ] }, item.id)) });
4018
+ }
3623
4019
  function AppScreenRoot({ children, className }) {
3624
4020
  return /* @__PURE__ */ jsx("div", { className: cn("flex h-full flex-col", className), children });
3625
4021
  }
@@ -4002,20 +4398,20 @@ function FullPageSpinner({ message }) {
4002
4398
  message && /* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-sm", children: message })
4003
4399
  ] });
4004
4400
  }
4005
- var defaultRenderLink10 = ({ href, children, className }) => /* @__PURE__ */ jsx("a", { href, className, children });
4401
+ var defaultRenderLink9 = ({ href, children, className }) => /* @__PURE__ */ jsx("a", { href, className, children });
4006
4402
  function PageHeader({
4007
4403
  title,
4008
4404
  description,
4009
4405
  breadcrumbs,
4010
4406
  backButton,
4011
- renderLink = defaultRenderLink10,
4407
+ renderLink = defaultRenderLink9,
4012
4408
  className,
4013
4409
  children
4014
4410
  }) {
4015
4411
  return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col gap-1 pb-6", className), children: [
4016
4412
  breadcrumbs && breadcrumbs.length > 0 && /* @__PURE__ */ jsx("nav", { "aria-label": "Breadcrumb", className: "mb-2 flex items-center gap-1 text-sm text-muted-foreground", children: breadcrumbs.map((crumb, i) => {
4017
4413
  const isLast = i === breadcrumbs.length - 1;
4018
- return /* @__PURE__ */ jsxs(React30.Fragment, { children: [
4414
+ return /* @__PURE__ */ jsxs(React32.Fragment, { children: [
4019
4415
  i > 0 && /* @__PURE__ */ jsx(ChevronRight, { className: "size-3.5 shrink-0" }),
4020
4416
  isLast || !crumb.href ? /* @__PURE__ */ jsx("span", { className: cn(isLast && "text-foreground font-medium"), children: crumb.label }) : renderLink({
4021
4417
  href: crumb.href,
@@ -4051,7 +4447,7 @@ function PageHeader({
4051
4447
  ] });
4052
4448
  }
4053
4449
  function Suspensed({ children, fallback }) {
4054
- return /* @__PURE__ */ jsx(React30.Suspense, { fallback: fallback ?? /* @__PURE__ */ jsx(Spinner, {}), children });
4450
+ return /* @__PURE__ */ jsx(React32.Suspense, { fallback: fallback ?? /* @__PURE__ */ jsx(Spinner, {}), children });
4055
4451
  }
4056
4452
  function getInitialTheme() {
4057
4453
  if (typeof document === "undefined") return "dark";
@@ -4059,7 +4455,7 @@ function getInitialTheme() {
4059
4455
  return stored ?? (document.documentElement.classList.contains("dark") ? "dark" : "light");
4060
4456
  }
4061
4457
  function ThemeSwitchMinimal() {
4062
- const [theme, setThemeState] = React30.useState(getInitialTheme);
4458
+ const [theme, setThemeState] = React32.useState(getInitialTheme);
4063
4459
  const toggle = () => {
4064
4460
  const next = theme === "dark" ? "light" : "dark";
4065
4461
  setThemeState(next);
@@ -4185,9 +4581,9 @@ function getInitialColorName() {
4185
4581
  return localStorage.getItem(STORAGE_KEY) ?? "emerald";
4186
4582
  }
4187
4583
  function BrandColorSwitchMinimal() {
4188
- const [active, setActive] = React30.useState(getInitialColorName);
4189
- const [open, setOpen] = React30.useState(false);
4190
- React30.useEffect(() => {
4584
+ const [active, setActive] = React32.useState(getInitialColorName);
4585
+ const [open, setOpen] = React32.useState(false);
4586
+ React32.useEffect(() => {
4191
4587
  const saved = localStorage.getItem(STORAGE_KEY) ?? "emerald";
4192
4588
  const color = BRAND_COLORS.find((c) => c.name === saved);
4193
4589
  if (color) applyColor(color);
@@ -4248,7 +4644,7 @@ function BrandColorSwitchMinimal() {
4248
4644
  ] });
4249
4645
  }
4250
4646
  function ThemeColorUpdater({ colorTheme }) {
4251
- React30.useEffect(() => {
4647
+ React32.useEffect(() => {
4252
4648
  if (colorTheme) {
4253
4649
  document.documentElement.setAttribute("data-theme", colorTheme);
4254
4650
  } else {
@@ -4258,7 +4654,7 @@ function ThemeColorUpdater({ colorTheme }) {
4258
4654
  return null;
4259
4655
  }
4260
4656
  function VersionBanner({ version, message, className, onDismiss }) {
4261
- const [dismissed, setDismissed] = React30.useState(false);
4657
+ const [dismissed, setDismissed] = React32.useState(false);
4262
4658
  if (dismissed) return null;
4263
4659
  return /* @__PURE__ */ jsxs("div", { className: cn("flex items-center justify-between gap-4 bg-primary/10 px-4 py-2 text-sm", className), children: [
4264
4660
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
@@ -4515,29 +4911,29 @@ function useMounted() {
4515
4911
  return mounted;
4516
4912
  }
4517
4913
  function useConfirm() {
4518
- const [isOpen, setIsOpen] = React30.useState(false);
4519
- const [options, setOptions] = React30.useState({});
4520
- const resolveRef = React30.useRef(null);
4521
- const confirm = React30.useCallback((opts = {}) => {
4914
+ const [isOpen, setIsOpen] = React32.useState(false);
4915
+ const [options, setOptions] = React32.useState({});
4916
+ const resolveRef = React32.useRef(null);
4917
+ const confirm = React32.useCallback((opts = {}) => {
4522
4918
  setOptions(opts);
4523
4919
  setIsOpen(true);
4524
4920
  return new Promise((resolve) => {
4525
4921
  resolveRef.current = resolve;
4526
4922
  });
4527
4923
  }, []);
4528
- const handleConfirm = React30.useCallback(() => {
4924
+ const handleConfirm = React32.useCallback(() => {
4529
4925
  resolveRef.current?.(true);
4530
4926
  setIsOpen(false);
4531
4927
  }, []);
4532
- const handleCancel = React30.useCallback(() => {
4928
+ const handleCancel = React32.useCallback(() => {
4533
4929
  resolveRef.current?.(false);
4534
4930
  setIsOpen(false);
4535
4931
  }, []);
4536
4932
  return { confirm, isOpen, options, handleConfirm, handleCancel };
4537
4933
  }
4538
4934
  function useScrolledPast(threshold) {
4539
- const [scrolledPast, setScrolledPast] = React30.useState(false);
4540
- React30.useEffect(() => {
4935
+ const [scrolledPast, setScrolledPast] = React32.useState(false);
4936
+ React32.useEffect(() => {
4541
4937
  const handleScroll = () => {
4542
4938
  setScrolledPast(window.scrollY > threshold);
4543
4939
  };
@@ -4548,42 +4944,28 @@ function useScrolledPast(threshold) {
4548
4944
  return scrolledPast;
4549
4945
  }
4550
4946
  function useCopyToClipboard() {
4551
- const [copied, setCopied] = React30.useState(false);
4552
- const timeoutRef = React30.useRef(void 0);
4553
- const copy = React30.useCallback((text) => {
4947
+ const [copied, setCopied] = React32.useState(false);
4948
+ const timeoutRef = React32.useRef(void 0);
4949
+ const copy = React32.useCallback((text) => {
4554
4950
  navigator.clipboard.writeText(text).then(() => {
4555
4951
  setCopied(true);
4556
4952
  clearTimeout(timeoutRef.current);
4557
4953
  timeoutRef.current = setTimeout(() => setCopied(false), 2e3);
4558
4954
  });
4559
4955
  }, []);
4560
- React30.useEffect(() => () => clearTimeout(timeoutRef.current), []);
4956
+ React32.useEffect(() => () => clearTimeout(timeoutRef.current), []);
4561
4957
  return [copied, copy];
4562
4958
  }
4563
4959
  function useDebounce(value, delay) {
4564
- const [debounced, setDebounced] = React30.useState(value);
4565
- React30.useEffect(() => {
4960
+ const [debounced, setDebounced] = React32.useState(value);
4961
+ React32.useEffect(() => {
4566
4962
  const timer = setTimeout(() => setDebounced(value), delay);
4567
4963
  return () => clearTimeout(timer);
4568
4964
  }, [value, delay]);
4569
4965
  return debounced;
4570
4966
  }
4571
- function useIntersectionObserver(options) {
4572
- const ref = React30.useRef(null);
4573
- const [isIntersecting, setIsIntersecting] = React30.useState(false);
4574
- React30.useEffect(() => {
4575
- const el = ref.current;
4576
- if (!el) return;
4577
- const observer = new IntersectionObserver(([entry]) => {
4578
- setIsIntersecting(entry.isIntersecting);
4579
- }, options);
4580
- observer.observe(el);
4581
- return () => observer.disconnect();
4582
- }, []);
4583
- return [ref, isIntersecting];
4584
- }
4585
4967
  function useLocalStorage(key, initialValue) {
4586
- const [storedValue, setStoredValue] = React30.useState(() => {
4968
+ const [storedValue, setStoredValue] = React32.useState(() => {
4587
4969
  if (typeof window === "undefined") return initialValue;
4588
4970
  try {
4589
4971
  const item = window.localStorage.getItem(key);
@@ -4592,7 +4974,7 @@ function useLocalStorage(key, initialValue) {
4592
4974
  return initialValue;
4593
4975
  }
4594
4976
  });
4595
- const setValue = React30.useCallback(
4977
+ const setValue = React32.useCallback(
4596
4978
  (value) => {
4597
4979
  try {
4598
4980
  setStoredValue(value);
@@ -4607,11 +4989,11 @@ function useLocalStorage(key, initialValue) {
4607
4989
  return [storedValue, setValue];
4608
4990
  }
4609
4991
  function useMediaQuery(query) {
4610
- const [matches, setMatches] = React30.useState(() => {
4992
+ const [matches, setMatches] = React32.useState(() => {
4611
4993
  if (typeof window === "undefined") return false;
4612
4994
  return window.matchMedia(query).matches;
4613
4995
  });
4614
- React30.useEffect(() => {
4996
+ React32.useEffect(() => {
4615
4997
  if (typeof window === "undefined") return;
4616
4998
  const mql = window.matchMedia(query);
4617
4999
  setMatches(mql.matches);
@@ -4622,18 +5004,18 @@ function useMediaQuery(query) {
4622
5004
  return matches;
4623
5005
  }
4624
5006
  function usePagination(total, pageSize) {
4625
- const [page, setPage] = React30.useState(1);
5007
+ const [page, setPage] = React32.useState(1);
4626
5008
  const totalPages = Math.max(1, Math.ceil(total / pageSize));
4627
5009
  const offset = (page - 1) * pageSize;
4628
- const goTo = React30.useCallback(
5010
+ const goTo = React32.useCallback(
4629
5011
  (p) => setPage(Math.min(Math.max(1, p), totalPages)),
4630
5012
  [totalPages]
4631
5013
  );
4632
- const next = React30.useCallback(() => goTo(page + 1), [page, goTo]);
4633
- const prev = React30.useCallback(() => goTo(page - 1), [page, goTo]);
5014
+ const next = React32.useCallback(() => goTo(page + 1), [page, goTo]);
5015
+ const prev = React32.useCallback(() => goTo(page - 1), [page, goTo]);
4634
5016
  return { page, pageSize, totalPages, offset, hasPrev: page > 1, hasNext: page < totalPages, goTo, next, prev };
4635
5017
  }
4636
5018
 
4637
- export { AnimatedPill, AppContent, AppGrid, AppGridCell, AppScreen, AppShell, AuthSection, Badge, BillingPanel, BrandColorSwitchMinimal, Button, Card, CarouselSection, ChangelogCard, ChangelogList, Checkbox, CommandMenu, ComparisonSection, ConfirmDialog, ContactSection, ContextMenu, CountUp, CountdownTimer, CtaCardSection, CtaSection, DataTable, DevBanner, Dock, EmptyState, ErrorPage, FadeIn, FaqSection, FeatureCard, FeatureMarqueeSection, FeaturesSection, FileUpload, FlowBracket, FlowConnector, Footer, FullPageSpinner, GlassCard, Grid, GridItem, GroupedFeaturesSection, HeroSection, ImageCard, Input, LogoStrip, ModeSwitchMinimal, Navbar, NewsletterSection, NotificationToast, OlwibaUIProvider, OnboardingWizard, Overlay, PageHeader, PageTransition, PhoneFrame, PostCard, PostList, PricingCard, PricingSection, PublicPageFrame, QualificationSection, RegisterHotkeys, RootErrorFallback, Section, SectionTitle, SettingsSection, Spotlight, Stack, StaggerChildren, StatCard, StatsSection, StepsSection, Suspensed, Switch, TeamMembersPanel, TeamSection, TechStackSection, TestimonialCard, TestimonialsSection, Textarea, ThemeColorUpdater, ThemeSwitchMinimal, Underlay, UpgradePrompt, VersionBanner, marketingSectionSpacing, notify, useConfirm, useControlledOpen, useCopyToClipboard, useDebounce, useIntersectionObserver, useLocalStorage, useMediaQuery, useMounted, useOlwibaUI, usePagination, useScrolledPast, useUIMode };
5019
+ export { ActivityFeed, AnimatedPill, AppContent, AppGrid, AppGridCell, AppScreen, AppShell, AuthSection, Badge, BillingPanel, BrandColorSwitchMinimal, Button, Card, Carousel, ChangelogCard, ChangelogList, Chart, Checkbox, CommandMenu, ComparisonSection, ConfirmDialog, ContactSection, ContextMenu, CountUp, CountdownTimer, CtaSection, DataTable, DevBanner, Dock, EmptyState, ErrorPage, FadeIn, FaqSection, FeatureCard, FeatureMarqueeSection, FeaturesSection, FileUpload, FlowBracket, FlowConnector, Footer, FullPageSpinner, GlassCard, Grid, GridItem, GroupedFeaturesSection, HeroSection, ImageCard, Input, LogoStrip, ModeSwitchMinimal, Navbar, NewsletterSection, NotificationToast, NotificationsPopover, OlwibaUIProvider, OnboardingWizard, Overlay, PageHeader, PageTransition, PhoneFrame, PostCard, PostList, PricingCard, PricingSection, PublicPageFrame, QualificationSection, RegisterHotkeys, RootErrorFallback, Section, SectionTitle, SettingsSection, Sortable, Spotlight, Stack, StaggerChildren, StatCard, StatsSection, StepsSection, Suspensed, Switch, TeamMembersPanel, TeamSection, TechStackSection, TestimonialCard, TestimonialsSection, Textarea, ThemeColorUpdater, ThemeSwitchMinimal, Underlay, UpgradePrompt, VersionBanner, marketingSectionSpacing, notify, useConfirm, useControlledOpen, useCopyToClipboard, useDebounce, useIntersectionObserver, useLocalStorage, useMediaQuery, useMounted, useOlwibaUI, usePagination, useScrolledPast, useUIMode };
4638
5020
  //# sourceMappingURL=index.js.map
4639
5021
  //# sourceMappingURL=index.js.map