@olwiba/ui 0.1.15 → 0.2.0

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,15 @@
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 React31 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
10
  import { toast } from 'sonner';
11
11
 
12
- var OlwibaUIContext = React30.createContext({
12
+ var OlwibaUIContext = React31.createContext({
13
13
  isMobile: false,
14
14
  mode: "default",
15
15
  setMode: () => {
@@ -18,17 +18,17 @@ var OlwibaUIContext = React30.createContext({
18
18
  function OlwibaUIProvider({ children, isMobile: isMobileProp, mode: initialMode = "default" }) {
19
19
  const detectedMobile = useIsMobile();
20
20
  const isMobile = isMobileProp ?? detectedMobile;
21
- const [mode, setMode] = React30.useState(initialMode);
21
+ const [mode, setMode] = React31.useState(initialMode);
22
22
  const variant = mode !== "default" ? mode : void 0;
23
23
  return /* @__PURE__ */ jsx(OlwibaUIContext.Provider, { value: { isMobile, mode, setMode }, children: /* @__PURE__ */ jsx(UIVariantProvider, { mode: variant, children }) });
24
24
  }
25
25
  function useOlwibaUI() {
26
- return React30.useContext(OlwibaUIContext);
26
+ return React31.useContext(OlwibaUIContext);
27
27
  }
28
28
  function useUIMode() {
29
- return React30.useContext(OlwibaUIContext).mode;
29
+ return React31.useContext(OlwibaUIContext).mode;
30
30
  }
31
- var Button = React30.forwardRef(
31
+ var Button = React31.forwardRef(
32
32
  ({ mode, ...props }, ref) => {
33
33
  const ctxMode = useUIMode();
34
34
  const resolvedMode = mode ?? (ctxMode !== "default" ? ctxMode : void 0);
@@ -36,7 +36,7 @@ var Button = React30.forwardRef(
36
36
  }
37
37
  );
38
38
  Button.displayName = "Button";
39
- var Card = React30.forwardRef(
39
+ var Card = React31.forwardRef(
40
40
  ({ mode, ...props }, ref) => {
41
41
  const ctxMode = useUIMode();
42
42
  const resolvedMode = mode ?? (ctxMode !== "default" ? ctxMode : void 0);
@@ -47,7 +47,7 @@ Card.displayName = "Card";
47
47
  function Badge(props) {
48
48
  return /* @__PURE__ */ jsx(Badge$1, { ...props });
49
49
  }
50
- var Input = React30.forwardRef(
50
+ var Input = React31.forwardRef(
51
51
  ({ mode, ...props }, ref) => {
52
52
  const ctxMode = useUIMode();
53
53
  const resolvedMode = mode ?? (ctxMode !== "default" ? ctxMode : void 0);
@@ -55,7 +55,7 @@ var Input = React30.forwardRef(
55
55
  }
56
56
  );
57
57
  Input.displayName = "Input";
58
- var Textarea = React30.forwardRef(
58
+ var Textarea = React31.forwardRef(
59
59
  ({ mode, ...props }, ref) => {
60
60
  const ctxMode = useUIMode();
61
61
  const resolvedMode = mode ?? (ctxMode !== "default" ? ctxMode : void 0);
@@ -63,13 +63,13 @@ var Textarea = React30.forwardRef(
63
63
  }
64
64
  );
65
65
  Textarea.displayName = "Textarea";
66
- var Checkbox = React30.forwardRef(({ mode, ...props }, ref) => {
66
+ var Checkbox = React31.forwardRef(({ mode, ...props }, ref) => {
67
67
  const ctxMode = useUIMode();
68
68
  const resolvedMode = mode ?? (ctxMode !== "default" ? ctxMode : void 0);
69
69
  return /* @__PURE__ */ jsx(Checkbox$1, { mode: resolvedMode, ...props, ref });
70
70
  });
71
71
  Checkbox.displayName = "Checkbox";
72
- var Switch = React30.forwardRef(({ mode: _mode, ...props }, ref) => {
72
+ var Switch = React31.forwardRef(({ mode: _mode, ...props }, ref) => {
73
73
  return /* @__PURE__ */ jsx(Switch$1, { ...props, ref });
74
74
  });
75
75
  Switch.displayName = "Switch";
@@ -513,7 +513,7 @@ function DefaultForm({
513
513
  const isForgotPassword = mode === "forgot-password";
514
514
  const isResetPassword = mode === "reset-password";
515
515
  const isVerify = mode === "verify";
516
- const [code, setCode] = React30.useState("");
516
+ const [code, setCode] = React31.useState("");
517
517
  const hasPrefill = !!(defaultEmail || defaultPassword);
518
518
  const prefillStyle = (active) => active ? { animation: "auth-prefill 1.6s ease-out 0.35s 1 both" } : void 0;
519
519
  const copy = authCopy[mode];
@@ -586,11 +586,14 @@ function DefaultForm({
586
586
  /* @__PURE__ */ jsx("input", { type: "hidden", name: "code", value: code }),
587
587
  /* @__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
588
  ] }),
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 }),
589
+ error && /* @__PURE__ */ jsx("p", { role: "alert", className: "text-sm font-medium text-destructive", children: error }),
590
+ success && /* @__PURE__ */ jsx("p", { role: "status", className: "text-sm font-medium text-primary", children: success }),
591
591
  /* @__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" })
592
+ /* @__PURE__ */ jsxs(Button, { type: "submit", className: "w-full", disabled: loading || isVerify && code.length < codeLength, children: [
593
+ loading && /* @__PURE__ */ jsx(Loader2, { className: "size-4 animate-spin" }),
594
+ loading ? "Please wait\u2026" : copy.submit
595
+ ] }),
596
+ onSso && (mode === "signin" || mode === "signup") && /* @__PURE__ */ jsx(Button, { type: "button", variant: "outline", className: "w-full", onClick: onSso, disabled: loading, children: "Use SSO" })
594
597
  ] })
595
598
  ] }),
596
599
  isVerify && onResend && /* @__PURE__ */ jsxs("p", { className: "text-center text-xs text-muted-foreground", children: [
@@ -670,10 +673,10 @@ function DataTable({
670
673
  emptyMessage = "No results.",
671
674
  className
672
675
  }) {
673
- const [sorting, setSorting] = React30.useState([]);
674
- const [rowSelection, setRowSelection] = React30.useState({});
675
- const [globalFilter, setGlobalFilter] = React30.useState("");
676
- const allColumns = React30.useMemo(
676
+ const [sorting, setSorting] = React31.useState([]);
677
+ const [rowSelection, setRowSelection] = React31.useState({});
678
+ const [globalFilter, setGlobalFilter] = React31.useState("");
679
+ const allColumns = React31.useMemo(
677
680
  () => selectable ? [selectColumn, ...columns] : columns,
678
681
  [selectable, columns]
679
682
  );
@@ -695,7 +698,7 @@ function DataTable({
695
698
  initialState: pageSize > 0 ? { pagination: { pageSize } } : void 0,
696
699
  enableRowSelection: selectable
697
700
  });
698
- React30.useEffect(() => {
701
+ React31.useEffect(() => {
699
702
  if (!onSelectionChange) return;
700
703
  onSelectionChange(table.getSelectedRowModel().rows.map((r) => r.original));
701
704
  }, [rowSelection]);
@@ -720,39 +723,48 @@ function DataTable({
720
723
  /* @__PURE__ */ jsx(TableHeader, { children: table.getHeaderGroups().map((headerGroup) => /* @__PURE__ */ jsx(TableRow, { children: headerGroup.headers.map((header) => {
721
724
  const canSort = header.column.getCanSort();
722
725
  const sortDir = header.column.getIsSorted();
723
- return /* @__PURE__ */ jsx(TableHead, { children: header.isPlaceholder ? null : canSort ? /* @__PURE__ */ jsxs(
724
- "button",
726
+ return /* @__PURE__ */ jsx(
727
+ TableHead,
725
728
  {
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);
729
+ "aria-sort": sortDir === "asc" ? "ascending" : sortDir === "desc" ? "descending" : canSort ? "none" : void 0,
730
+ children: header.isPlaceholder ? null : canSort ? /* @__PURE__ */ jsxs(
731
+ "button",
732
+ {
733
+ type: "button",
734
+ className: "flex items-center gap-1.5 font-medium hover:text-foreground",
735
+ onClick: header.column.getToggleSortingHandler(),
736
+ children: [
737
+ flexRender(header.column.columnDef.header, header.getContext()),
738
+ 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" })
739
+ ]
740
+ }
741
+ ) : flexRender(header.column.columnDef.header, header.getContext())
742
+ },
743
+ header.id
744
+ );
735
745
  }) }, headerGroup.id)) }),
736
746
  /* @__PURE__ */ jsx(TableBody, { children: rows.length ? rows.map((row) => /* @__PURE__ */ jsx(
737
747
  TableRow,
738
748
  {
739
749
  "data-state": row.getIsSelected() ? "selected" : void 0,
740
750
  onClick: () => onRowClick?.(row.original),
741
- className: cn$1(onRowClick && "cursor-pointer"),
751
+ tabIndex: onRowClick ? 0 : void 0,
752
+ onKeyDown: onRowClick ? (e) => {
753
+ if (e.key === "Enter" && e.target === e.currentTarget) onRowClick(row.original);
754
+ } : void 0,
755
+ className: cn$1(onRowClick && "cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"),
742
756
  children: row.getVisibleCells().map((cell) => /* @__PURE__ */ jsx(TableCell, { children: flexRender(cell.column.columnDef.cell, cell.getContext()) }, cell.id))
743
757
  },
744
758
  row.id
745
759
  )) : /* @__PURE__ */ jsx(TableRow, { children: /* @__PURE__ */ jsx(TableCell, { colSpan: allColumns.length, className: "h-24 text-center text-muted-foreground", children: emptyMessage }) }) })
746
760
  ] }) }),
747
- pageSize > 0 && table.getPageCount() > 1 && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
761
+ (selectable || pageSize > 0 && table.getPageCount() > 1) && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
748
762
  /* @__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()
763
+ selectable && `${table.getFilteredSelectedRowModel().rows.length} of ${table.getFilteredRowModel().rows.length} selected`,
764
+ selectable && pageSize > 0 && table.getPageCount() > 1 && " \xB7 ",
765
+ pageSize > 0 && table.getPageCount() > 1 && `Page ${table.getState().pagination.pageIndex + 1} of ${table.getPageCount()}`
754
766
  ] }),
755
- /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
767
+ pageSize > 0 && table.getPageCount() > 1 && /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
756
768
  /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", onClick: () => table.previousPage(), disabled: !table.getCanPreviousPage(), children: [
757
769
  /* @__PURE__ */ jsx(ChevronLeft, { className: "size-4" }),
758
770
  " Previous"
@@ -765,32 +777,42 @@ function DataTable({
765
777
  ] })
766
778
  ] });
767
779
  }
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)) })
780
+ function InviteDialog({ roles, onInvite, onClose }) {
781
+ const [email, setEmail] = React31.useState("");
782
+ const [role, setRole] = React31.useState(roles[roles.length - 1] ?? roles[0]);
783
+ return /* @__PURE__ */ jsx(DialogContent, { children: /* @__PURE__ */ jsxs(
784
+ "form",
785
+ {
786
+ onSubmit: (e) => {
787
+ e.preventDefault();
788
+ onInvite?.(email, role);
789
+ onClose();
790
+ },
791
+ children: [
792
+ /* @__PURE__ */ jsxs(DialogHeader, { children: [
793
+ /* @__PURE__ */ jsx(DialogTitle, { children: "Invite a team member" }),
794
+ /* @__PURE__ */ jsx(DialogDescription, { children: "They\u2019ll get an email invite to join this workspace." })
795
+ ] }),
796
+ /* @__PURE__ */ jsxs("div", { className: "space-y-4 py-4", children: [
797
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
798
+ /* @__PURE__ */ jsx(Label, { htmlFor: "invite-email", children: "Email address" }),
799
+ /* @__PURE__ */ jsx(Input, { id: "invite-email", type: "email", required: true, placeholder: "name@company.com", value: email, onChange: (e) => setEmail(e.target.value) })
800
+ ] }),
801
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
802
+ /* @__PURE__ */ jsx(Label, { children: "Role" }),
803
+ /* @__PURE__ */ jsxs(Select, { value: role, onValueChange: setRole, children: [
804
+ /* @__PURE__ */ jsx(SelectTrigger, { children: /* @__PURE__ */ jsx(SelectValue, {}) }),
805
+ /* @__PURE__ */ jsx(SelectContent, { children: roles.map((r) => /* @__PURE__ */ jsx(SelectItem, { value: r, children: r }, r)) })
806
+ ] })
807
+ ] })
808
+ ] }),
809
+ /* @__PURE__ */ jsxs(DialogFooter, { children: [
810
+ /* @__PURE__ */ jsx(DialogClose, { asChild: true, children: /* @__PURE__ */ jsx(Button, { type: "button", variant: "outline", children: "Cancel" }) }),
811
+ /* @__PURE__ */ jsx(Button, { type: "submit", children: "Send invite" })
786
812
  ] })
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
- ] });
813
+ ]
814
+ }
815
+ ) });
794
816
  }
795
817
  function TeamMembersPanel({
796
818
  members,
@@ -801,7 +823,8 @@ function TeamMembersPanel({
801
823
  title = "Team members",
802
824
  description = "Manage who has access to this workspace."
803
825
  }) {
804
- const columns = React30.useMemo(() => [
826
+ const [inviteOpen, setInviteOpen] = React31.useState(false);
827
+ const columns = React31.useMemo(() => [
805
828
  {
806
829
  accessorKey: "name",
807
830
  header: "Name",
@@ -824,7 +847,7 @@ function TeamMembersPanel({
824
847
  /* @__PURE__ */ jsx(SelectContent, { children: roles.map((r) => /* @__PURE__ */ jsx(SelectItem, { value: r, children: r }, r)) })
825
848
  ] }) : /* @__PURE__ */ jsx(Badge$1, { variant: "secondary", children: row.original.role })
826
849
  },
827
- {
850
+ ...onRemove ? [{
828
851
  id: "actions",
829
852
  header: "",
830
853
  cell: ({ row }) => /* @__PURE__ */ jsxs(DropdownMenu, { children: [
@@ -832,23 +855,23 @@ function TeamMembersPanel({
832
855
  /* @__PURE__ */ jsx(MoreHorizontal, { className: "size-4" }),
833
856
  /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Row actions" })
834
857
  ] }) }),
835
- /* @__PURE__ */ jsx(DropdownMenuContent, { align: "end", children: /* @__PURE__ */ jsx(DropdownMenuItem, { className: "text-destructive", onClick: () => onRemove?.(row.original.id), children: "Remove member" }) })
858
+ /* @__PURE__ */ jsx(DropdownMenuContent, { align: "end", children: /* @__PURE__ */ jsx(DropdownMenuItem, { className: "text-destructive", onClick: () => onRemove(row.original.id), children: "Remove member" }) })
836
859
  ] }),
837
860
  enableSorting: false
838
- }
861
+ }] : []
839
862
  ], [roles, onRoleChange, onRemove]);
840
863
  return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
841
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-4", children: [
864
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-between gap-4", children: [
842
865
  /* @__PURE__ */ jsxs("div", { children: [
843
866
  /* @__PURE__ */ jsx("h2", { className: "text-base font-semibold", children: title }),
844
867
  /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-muted-foreground", children: description })
845
868
  ] }),
846
- /* @__PURE__ */ jsxs(Dialog, { children: [
869
+ onInvite && /* @__PURE__ */ jsxs(Dialog, { open: inviteOpen, onOpenChange: setInviteOpen, children: [
847
870
  /* @__PURE__ */ jsx(DialogTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(Button, { children: [
848
871
  /* @__PURE__ */ jsx(UserPlus, { className: "size-4" }),
849
872
  " Invite member"
850
873
  ] }) }),
851
- /* @__PURE__ */ jsx(InviteDialog, { roles, onInvite })
874
+ /* @__PURE__ */ jsx(InviteDialog, { roles, onInvite, onClose: () => setInviteOpen(false) }, String(inviteOpen))
852
875
  ] })
853
876
  ] }),
854
877
  /* @__PURE__ */ jsx(DataTable, { columns, data: members, searchKey: "name", searchPlaceholder: "Search members\u2026", pageSize: 0 })
@@ -870,12 +893,13 @@ function BillingPanel({
870
893
  onUpdatePaymentMethod,
871
894
  className
872
895
  }) {
873
- const invoiceColumns = React30.useMemo(() => [
874
- { accessorKey: "date", header: "Date" },
875
- { accessorKey: "amount", header: "Amount" },
896
+ const invoiceColumns = React31.useMemo(() => [
897
+ { accessorKey: "date", header: "Date", enableSorting: false },
898
+ { accessorKey: "amount", header: "Amount", enableSorting: false },
876
899
  {
877
900
  accessorKey: "status",
878
901
  header: "Status",
902
+ enableSorting: false,
879
903
  cell: ({ row }) => /* @__PURE__ */ jsx(Badge$1, { variant: invoiceStatusVariant[row.original.status], className: "capitalize", children: row.original.status })
880
904
  },
881
905
  {
@@ -903,19 +927,23 @@ function BillingPanel({
903
927
  ] }),
904
928
  onManagePlan && /* @__PURE__ */ jsx(Button, { onClick: onManagePlan, children: "Manage plan" })
905
929
  ] }),
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)) })
930
+ usage && usage.length > 0 && /* @__PURE__ */ jsx("div", { className: "mt-4 space-y-4", children: usage.map((metric) => {
931
+ const overLimit = metric.limit > 0 && metric.used > metric.limit;
932
+ const percent = metric.limit > 0 ? Math.min(100, metric.used / metric.limit * 100) : 0;
933
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
934
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between text-sm", children: [
935
+ /* @__PURE__ */ jsx("span", { children: metric.label }),
936
+ /* @__PURE__ */ jsxs("span", { className: overLimit ? "font-medium text-destructive" : "text-muted-foreground", children: [
937
+ metric.used,
938
+ metric.unit,
939
+ " / ",
940
+ metric.limit,
941
+ metric.unit
942
+ ] })
943
+ ] }),
944
+ /* @__PURE__ */ jsx(Progress, { value: percent, className: overLimit ? "[&>div]:bg-destructive" : void 0 })
945
+ ] }, metric.label);
946
+ }) })
919
947
  ] }),
920
948
  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
949
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
@@ -944,9 +972,9 @@ function OnboardingWizard({
944
972
  completeLabel = "Finish",
945
973
  className
946
974
  }) {
947
- const [internalStep, setInternalStep] = React30.useState(0);
948
- const [error, setError] = React30.useState(null);
949
- const [pending, setPending] = React30.useState(false);
975
+ const [internalStep, setInternalStep] = React31.useState(0);
976
+ const [error, setError] = React31.useState(null);
977
+ const [pending, setPending] = React31.useState(false);
950
978
  const index = stepProp ?? internalStep;
951
979
  const current = steps[index];
952
980
  const isLast = index === steps.length - 1;
@@ -959,8 +987,15 @@ function OnboardingWizard({
959
987
  const handleNext = async () => {
960
988
  if (current.onNext) {
961
989
  setPending(true);
962
- const result = await current.onNext();
963
- setPending(false);
990
+ let result;
991
+ try {
992
+ result = await current.onNext();
993
+ } catch (err) {
994
+ setError(err instanceof Error ? err.message : "Something went wrong. Please try again.");
995
+ return;
996
+ } finally {
997
+ setPending(false);
998
+ }
964
999
  if (result === false) {
965
1000
  setError("Please complete this step before continuing.");
966
1001
  return;
@@ -980,30 +1015,47 @@ function OnboardingWizard({
980
1015
  if (index > 0) goTo(index - 1);
981
1016
  };
982
1017
  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"
1018
+ /* @__PURE__ */ jsx("ol", { className: "flex items-center", "aria-label": "Progress", children: steps.map((step, i) => /* @__PURE__ */ jsxs(
1019
+ "li",
1020
+ {
1021
+ "aria-current": i === index ? "step" : void 0,
1022
+ className: cn$1("flex items-center", i !== steps.length - 1 && "flex-1"),
1023
+ children: [
1024
+ /* @__PURE__ */ jsxs(
1025
+ "div",
1026
+ {
1027
+ className: cn$1(
1028
+ "flex size-8 shrink-0 items-center justify-center rounded-full border text-xs font-medium",
1029
+ i < index ? "border-primary bg-primary text-primary-foreground" : i === index ? "border-primary text-primary" : "border-border text-muted-foreground"
1030
+ ),
1031
+ children: [
1032
+ i < index ? /* @__PURE__ */ jsx(Check, { className: "size-4" }) : i + 1,
1033
+ /* @__PURE__ */ jsxs("span", { className: "sr-only", children: [
1034
+ step.title,
1035
+ i < index ? " (completed)" : ""
1036
+ ] })
1037
+ ]
1038
+ }
990
1039
  ),
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)) }),
1040
+ i !== steps.length - 1 && /* @__PURE__ */ jsx("div", { className: cn$1("mx-2 h-px flex-1", i < index ? "bg-primary" : "bg-border") })
1041
+ ]
1042
+ },
1043
+ step.id
1044
+ )) }),
996
1045
  /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
997
1046
  /* @__PURE__ */ jsxs("div", { children: [
998
1047
  /* @__PURE__ */ jsx("h2", { className: "text-xl font-semibold tracking-tight", children: current.title }),
999
1048
  current.description && /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-muted-foreground", children: current.description })
1000
1049
  ] }),
1001
1050
  /* @__PURE__ */ jsx("div", { children: current.content }),
1002
- error && /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-destructive", children: error })
1051
+ error && /* @__PURE__ */ jsx("p", { role: "alert", className: "text-sm font-medium text-destructive", children: error })
1003
1052
  ] }),
1004
1053
  /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
1005
1054
  /* @__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" })
1055
+ /* @__PURE__ */ jsxs(Button, { type: "button", onClick: handleNext, disabled: pending, children: [
1056
+ pending && /* @__PURE__ */ jsx(Loader2, { className: "size-4 animate-spin" }),
1057
+ pending ? "Please wait\u2026" : isLast ? completeLabel : "Continue"
1058
+ ] })
1007
1059
  ] })
1008
1060
  ] });
1009
1061
  }
@@ -1256,9 +1308,9 @@ function FadeIn({
1256
1308
  style,
1257
1309
  ...props
1258
1310
  }) {
1259
- const ref = React30.useRef(null);
1260
- const [visible, setVisible] = React30.useState(false);
1261
- React30.useEffect(() => {
1311
+ const ref = React31.useRef(null);
1312
+ const [visible, setVisible] = React31.useState(false);
1313
+ React31.useEffect(() => {
1262
1314
  const el = ref.current;
1263
1315
  if (!el) return;
1264
1316
  const observer = new IntersectionObserver(
@@ -1365,27 +1417,28 @@ function HeroSection({
1365
1417
  socialProofText,
1366
1418
  renderLink = defaultRenderLink4
1367
1419
  }) {
1420
+ const mode = useUIVariant();
1368
1421
  return /* @__PURE__ */ jsx("section", { className: "overflow-hidden", children: /* @__PURE__ */ jsxs("div", { className: marketingSectionSpacing.hero, children: [
1369
1422
  /* @__PURE__ */ jsxs("div", { className: "mx-auto grid max-w-6xl items-center gap-10 lg:grid-cols-2 lg:gap-16", children: [
1370
1423
  /* @__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 }) }),
1424
+ badge && /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(Badge, { variant: "secondary", children: badge }) }),
1372
1425
  /* @__PURE__ */ jsx("h1", { className: "text-balance text-4xl font-semibold tracking-tight sm:text-5xl lg:text-6xl", children: heading }),
1373
1426
  /* @__PURE__ */ jsx("p", { className: "max-w-lg text-pretty text-lg text-muted-foreground", children: description }),
1374
1427
  /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-3", children: [
1375
1428
  renderLink({
1376
1429
  href: primaryCta.href,
1377
- children: /* @__PURE__ */ jsxs(Button$1, { size: "lg", children: [
1430
+ children: /* @__PURE__ */ jsxs(Button, { size: "lg", children: [
1378
1431
  primaryCta.label,
1379
1432
  /* @__PURE__ */ jsx(ArrowRight, { className: "ml-2 size-4" })
1380
1433
  ] })
1381
1434
  }),
1382
1435
  secondaryCta && renderLink({
1383
1436
  href: secondaryCta.href,
1384
- children: /* @__PURE__ */ jsx(Button$1, { size: "lg", variant: "outline", children: secondaryCta.label })
1437
+ children: /* @__PURE__ */ jsx(Button, { size: "lg", variant: "outline", children: secondaryCta.label })
1385
1438
  })
1386
1439
  ] })
1387
1440
  ] }) }),
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 }) })
1441
+ 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
1442
  ] }),
1390
1443
  (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
1444
  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 +1497,88 @@ function FeatureCard({ icon: Icon, title, description, href, className, ...props
1444
1497
  }
1445
1498
  return content;
1446
1499
  }
1500
+ function Carousel({
1501
+ children,
1502
+ itemClassName = "w-[280px] sm:w-[320px]",
1503
+ controls = "top-right",
1504
+ ariaLabel,
1505
+ className
1506
+ }) {
1507
+ const mode = useUIVariant();
1508
+ const trackRef = React31.useRef(null);
1509
+ const [canPrev, setCanPrev] = React31.useState(false);
1510
+ const [canNext, setCanNext] = React31.useState(true);
1511
+ const updateScrollState = React31.useCallback(() => {
1512
+ const track = trackRef.current;
1513
+ if (!track) return;
1514
+ setCanPrev(track.scrollLeft > 8);
1515
+ setCanNext(track.scrollLeft < track.scrollWidth - track.clientWidth - 8);
1516
+ }, []);
1517
+ React31.useEffect(() => {
1518
+ const track = trackRef.current;
1519
+ if (!track) return;
1520
+ updateScrollState();
1521
+ track.addEventListener("scroll", updateScrollState, { passive: true });
1522
+ const observer = new ResizeObserver(updateScrollState);
1523
+ observer.observe(track);
1524
+ return () => {
1525
+ track.removeEventListener("scroll", updateScrollState);
1526
+ observer.disconnect();
1527
+ };
1528
+ }, [updateScrollState]);
1529
+ const scrollByItem = (direction) => {
1530
+ const track = trackRef.current;
1531
+ if (!track) return;
1532
+ const item = track.querySelector("[data-carousel-item]");
1533
+ const gap = parseFloat(getComputedStyle(track).columnGap) || 24;
1534
+ const step = item ? item.offsetWidth + gap : track.clientWidth * 0.8;
1535
+ track.scrollBy({ left: direction * step, behavior: "smooth" });
1536
+ };
1537
+ const controlClasses = cn$1(
1538
+ "flex size-9 items-center justify-center border bg-background text-foreground transition-colors",
1539
+ "hover:bg-muted disabled:opacity-40 disabled:hover:bg-background",
1540
+ mode === "smooth" ? "rounded-2xl" : "rounded-xl"
1541
+ );
1542
+ return /* @__PURE__ */ jsxs("div", { className, children: [
1543
+ controls === "top-right" && /* @__PURE__ */ jsxs("div", { className: "mb-4 flex items-center justify-end gap-2", children: [
1544
+ /* @__PURE__ */ jsx(
1545
+ "button",
1546
+ {
1547
+ type: "button",
1548
+ "aria-label": "Previous",
1549
+ className: controlClasses,
1550
+ onClick: () => scrollByItem(-1),
1551
+ disabled: !canPrev,
1552
+ children: /* @__PURE__ */ jsx(ChevronLeft, { className: "size-4" })
1553
+ }
1554
+ ),
1555
+ /* @__PURE__ */ jsx(
1556
+ "button",
1557
+ {
1558
+ type: "button",
1559
+ "aria-label": "Next",
1560
+ className: controlClasses,
1561
+ onClick: () => scrollByItem(1),
1562
+ disabled: !canNext,
1563
+ children: /* @__PURE__ */ jsx(ChevronRight, { className: "size-4" })
1564
+ }
1565
+ )
1566
+ ] }),
1567
+ /* @__PURE__ */ jsx(
1568
+ "div",
1569
+ {
1570
+ ref: trackRef,
1571
+ role: "region",
1572
+ "aria-label": ariaLabel,
1573
+ className: "flex snap-x snap-mandatory gap-6 overflow-x-auto pb-4 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden",
1574
+ children: React31.Children.map(
1575
+ children,
1576
+ (child) => child == null ? null : /* @__PURE__ */ jsx("div", { "data-carousel-item": true, className: cn$1("shrink-0 snap-start", itemClassName), children: child })
1577
+ )
1578
+ }
1579
+ )
1580
+ ] });
1581
+ }
1447
1582
  var translateMap2 = {
1448
1583
  up: [0, 20],
1449
1584
  down: [0, -20],
@@ -1461,9 +1596,9 @@ function StaggerChildren({
1461
1596
  className,
1462
1597
  ...props
1463
1598
  }) {
1464
- const ref = React30.useRef(null);
1465
- const [visible, setVisible] = React30.useState(false);
1466
- React30.useEffect(() => {
1599
+ const ref = React31.useRef(null);
1600
+ const [visible, setVisible] = React31.useState(false);
1601
+ React31.useEffect(() => {
1467
1602
  const el = ref.current;
1468
1603
  if (!el) return;
1469
1604
  const observer = new IntersectionObserver(
@@ -1481,7 +1616,7 @@ function StaggerChildren({
1481
1616
  return () => observer.disconnect();
1482
1617
  }, [once]);
1483
1618
  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(
1619
+ return /* @__PURE__ */ jsx("div", { ref, className: cn$1(className), ...props, children: React31.Children.map(children, (child, i) => /* @__PURE__ */ jsx(
1485
1620
  "div",
1486
1621
  {
1487
1622
  className: "h-full",
@@ -1499,7 +1634,8 @@ function FeaturesSection({
1499
1634
  title = "Everything you need to ship fast",
1500
1635
  description = "A complete system of components, blocks, and hooks designed to work together - and get out of your way.",
1501
1636
  badge = "Features",
1502
- features
1637
+ features,
1638
+ layout = "grid"
1503
1639
  }) {
1504
1640
  const mode = useUIVariant();
1505
1641
  const sectionClasses = cn$1(
@@ -1508,18 +1644,19 @@ function FeaturesSection({
1508
1644
  mode === "playful" && "rounded-2xl border-primary/25 border",
1509
1645
  !mode && "rounded-2xl border"
1510
1646
  );
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: [
1647
+ const cards = features.map((feature) => /* @__PURE__ */ jsx(
1648
+ FeatureCard,
1649
+ {
1650
+ icon: feature.icon,
1651
+ title: feature.title,
1652
+ description: feature.description,
1653
+ href: feature.href
1654
+ },
1655
+ feature.title
1656
+ ));
1657
+ 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
1658
  /* @__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
- )) })
1659
+ 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
1660
  ] }) }) });
1524
1661
  }
1525
1662
  function GroupedFeaturesSection({
@@ -1649,106 +1786,6 @@ function TechStackSection({
1649
1786
  }) })
1650
1787
  ] }) }) });
1651
1788
  }
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
1789
  var speedDuration = {
1753
1790
  slow: "280s",
1754
1791
  normal: "180s",
@@ -1829,79 +1866,42 @@ function FeatureMarqueeSection({
1829
1866
  ` })
1830
1867
  ] });
1831
1868
  }
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
- ] }) });
1869
+ function useIntersectionObserver(options) {
1870
+ const ref = React31.useRef(null);
1871
+ const [isIntersecting, setIsIntersecting] = React31.useState(false);
1872
+ React31.useEffect(() => {
1873
+ const el = ref.current;
1874
+ if (!el) return;
1875
+ const observer = new IntersectionObserver(([entry]) => {
1876
+ setIsIntersecting(entry.isIntersecting);
1877
+ }, options);
1878
+ observer.observe(el);
1879
+ return () => observer.disconnect();
1880
+ }, []);
1881
+ return [ref, isIntersecting];
1870
1882
  }
1871
- var defaultRenderLink6 = ({ href, children, className }) => /* @__PURE__ */ jsx("a", { href, className, children });
1872
- function CtaCardSection({
1883
+ var defaultRenderLink5 = ({ href, children, className }) => /* @__PURE__ */ jsx("a", { href, className, children });
1884
+ function ShowcaseCta({
1873
1885
  heading,
1874
1886
  description,
1875
1887
  primaryCta,
1876
1888
  footnote,
1877
1889
  icon,
1878
- renderLink = defaultRenderLink6
1890
+ renderLink = defaultRenderLink5,
1891
+ sectionClasses
1879
1892
  }) {
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
- }, []);
1893
+ const [ref, intersecting] = useIntersectionObserver({ threshold: 0.15 });
1894
+ const [visible, setVisible] = React31.useState(false);
1895
+ React31.useEffect(() => {
1896
+ if (intersecting) setVisible(true);
1897
+ }, [intersecting]);
1897
1898
  return /* @__PURE__ */ jsxs(
1898
1899
  "section",
1899
1900
  {
1900
1901
  ref,
1901
1902
  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",
1903
+ sectionClasses,
1904
+ "relative transition-[opacity,transform] duration-700 ease-out",
1905
1905
  visible ? "opacity-100 translate-y-0" : "opacity-0 translate-y-6"
1906
1906
  ),
1907
1907
  children: [
@@ -1910,11 +1910,11 @@ function CtaCardSection({
1910
1910
  {
1911
1911
  className: "pointer-events-none absolute inset-0 flex items-center justify-center",
1912
1912
  style: {
1913
- opacity: visible ? 0.12 : 0,
1913
+ opacity: visible ? 0.1 : 0,
1914
1914
  transform: visible ? "scale(1) rotate(0deg)" : "scale(0.9) rotate(-5deg)",
1915
1915
  transition: "opacity 700ms ease 200ms, transform 700ms ease 200ms"
1916
1916
  },
1917
- children: icon ?? /* @__PURE__ */ jsx(Rocket, { className: "size-72 text-foreground dark:text-foreground", strokeWidth: 0.75 })
1917
+ children: icon ?? /* @__PURE__ */ jsx(Rocket, { className: "size-72 text-foreground", strokeWidth: 0.75 })
1918
1918
  }
1919
1919
  ),
1920
1920
  /* @__PURE__ */ jsxs("div", { className: "relative z-10 px-6 py-20 text-center sm:px-10 sm:py-28", children: [
@@ -1922,9 +1922,9 @@ function CtaCardSection({
1922
1922
  description && /* @__PURE__ */ jsx("p", { className: "mx-auto mt-4 max-w-lg text-pretty text-muted-foreground", children: description }),
1923
1923
  /* @__PURE__ */ jsx("div", { className: "mt-8", children: renderLink({
1924
1924
  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: [
1925
+ children: /* @__PURE__ */ jsxs(Button, { size: "lg", className: "rounded-full px-6", children: [
1926
1926
  primaryCta.label,
1927
- /* @__PURE__ */ jsx(ArrowRight, { className: "size-4" })
1927
+ /* @__PURE__ */ jsx(ArrowRight, { className: "ml-2 size-4" })
1928
1928
  ] })
1929
1929
  }) }),
1930
1930
  footnote && /* @__PURE__ */ jsx("p", { className: "mt-5 text-sm text-muted-foreground", children: footnote })
@@ -1933,6 +1933,49 @@ function CtaCardSection({
1933
1933
  }
1934
1934
  );
1935
1935
  }
1936
+ function CtaSection(props) {
1937
+ const {
1938
+ heading,
1939
+ description,
1940
+ primaryCta,
1941
+ secondaryCta,
1942
+ footnote,
1943
+ variant = "default",
1944
+ renderLink = defaultRenderLink5
1945
+ } = props;
1946
+ const mode = useUIVariant();
1947
+ const sectionClasses = cn$1(
1948
+ "overflow-hidden bg-card",
1949
+ mode === "smooth" && "rounded-3xl border",
1950
+ mode === "playful" && "rounded-2xl border-primary/25 border",
1951
+ !mode && "rounded-2xl border"
1952
+ );
1953
+ if (variant === "showcase") {
1954
+ return /* @__PURE__ */ jsx(ShowcaseCta, { ...props, sectionClasses });
1955
+ }
1956
+ return /* @__PURE__ */ jsx("section", { className: sectionClasses, children: /* @__PURE__ */ jsxs("div", { className: "relative px-6 py-16 sm:px-10 sm:py-24", children: [
1957
+ /* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-[radial-gradient(ellipse_at_center,hsl(var(--primary)/0.15),transparent_70%)]" }),
1958
+ /* @__PURE__ */ jsx(FadeIn, { direction: "up", children: /* @__PURE__ */ jsxs("div", { className: "relative mx-auto max-w-2xl text-center", children: [
1959
+ /* @__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" }) }),
1960
+ /* @__PURE__ */ jsx("h2", { className: "text-balance text-3xl font-semibold tracking-tight sm:text-4xl", children: heading }),
1961
+ description && /* @__PURE__ */ jsx("p", { className: "mx-auto mt-4 max-w-lg text-pretty text-muted-foreground", children: description }),
1962
+ /* @__PURE__ */ jsxs("div", { className: "mt-8 flex flex-wrap items-center justify-center gap-3", children: [
1963
+ renderLink({
1964
+ href: primaryCta.href,
1965
+ children: /* @__PURE__ */ jsxs(Button, { size: "lg", children: [
1966
+ primaryCta.label,
1967
+ /* @__PURE__ */ jsx(ArrowRight, { className: "ml-2 size-4" })
1968
+ ] })
1969
+ }),
1970
+ secondaryCta && renderLink({
1971
+ href: secondaryCta.href,
1972
+ children: /* @__PURE__ */ jsx(Button, { size: "lg", variant: "outline", children: secondaryCta.label })
1973
+ })
1974
+ ] }),
1975
+ footnote && /* @__PURE__ */ jsx("p", { className: "mt-4 text-sm text-muted-foreground", children: footnote })
1976
+ ] }) })
1977
+ ] }) });
1978
+ }
1936
1979
  function PricingCard({
1937
1980
  name,
1938
1981
  price,
@@ -2026,8 +2069,8 @@ function CountdownTimer({
2026
2069
  className,
2027
2070
  compact = false
2028
2071
  }) {
2029
- const [timeLeft, setTimeLeft] = React30.useState(() => getTimeLeft(deadline));
2030
- React30.useEffect(() => {
2072
+ const [timeLeft, setTimeLeft] = React31.useState(() => getTimeLeft(deadline));
2073
+ React31.useEffect(() => {
2031
2074
  setTimeLeft(getTimeLeft(deadline));
2032
2075
  const id = setInterval(() => setTimeLeft(getTimeLeft(deadline)), 1e3);
2033
2076
  return () => clearInterval(id);
@@ -2053,7 +2096,7 @@ function CountdownTimer({
2053
2096
  ];
2054
2097
  return /* @__PURE__ */ jsxs("div", { className: cn$1("flex items-center justify-center gap-2 text-sm", className), children: [
2055
2098
  /* @__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: [
2099
+ /* @__PURE__ */ jsx("span", { className: "flex items-center gap-1 font-mono font-semibold tabular-nums", children: units.map(({ value, unit }, i) => /* @__PURE__ */ jsxs(React31.Fragment, { children: [
2057
2100
  /* @__PURE__ */ jsxs("span", { children: [
2058
2101
  String(value).padStart(2, "0"),
2059
2102
  /* @__PURE__ */ jsx("span", { className: "font-normal text-muted-foreground", children: unit })
@@ -2062,7 +2105,7 @@ function CountdownTimer({
2062
2105
  ] }, unit)) })
2063
2106
  ] });
2064
2107
  }
2065
- var defaultRenderLink7 = ({ href, children, className }) => /* @__PURE__ */ jsx("a", { href, className, children });
2108
+ var defaultRenderLink6 = ({ href, children, className }) => /* @__PURE__ */ jsx("a", { href, className, children });
2066
2109
  function PricingSection({
2067
2110
  title = "Simple, transparent pricing",
2068
2111
  description = "Start for free. Scale as you grow. No hidden fees.",
@@ -2070,13 +2113,13 @@ function PricingSection({
2070
2113
  plans,
2071
2114
  saveBadge = "Save 34%",
2072
2115
  isAuthenticated,
2073
- renderLink = defaultRenderLink7,
2116
+ renderLink = defaultRenderLink6,
2074
2117
  footnote,
2075
2118
  mode = "subscription",
2076
2119
  foundingDeadline,
2077
2120
  renderPlanFooter
2078
2121
  }) {
2079
- const [annual, setAnnual] = React30.useState(false);
2122
+ const [annual, setAnnual] = React31.useState(false);
2080
2123
  const isOneTime = mode === "one-time";
2081
2124
  const uiMode = useUIVariant();
2082
2125
  const sectionClasses = cn$1(
@@ -2280,9 +2323,16 @@ function TeamSection({
2280
2323
  title = "The people behind Olwiba",
2281
2324
  description = "A small team with deep experience in design systems, open source, and developer tooling."
2282
2325
  }) {
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: [
2326
+ const mode = useUIVariant();
2327
+ const sectionClasses = cn$1(
2328
+ "overflow-hidden bg-card",
2329
+ mode === "smooth" && "rounded-3xl border",
2330
+ mode === "playful" && "rounded-2xl border-primary/25 border",
2331
+ !mode && "rounded-2xl border"
2332
+ );
2333
+ 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
2334
  /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
2285
- /* @__PURE__ */ jsx(Badge$1, { variant: "secondary", className: "mb-4", children: badge }),
2335
+ /* @__PURE__ */ jsx(Badge, { variant: "secondary", className: "mb-4", children: badge }),
2286
2336
  /* @__PURE__ */ jsx("h2", { className: "text-balance text-3xl font-semibold tracking-tight sm:text-4xl", children: title }),
2287
2337
  /* @__PURE__ */ jsx("p", { className: "mx-auto mt-4 max-w-xl text-pretty text-muted-foreground", children: description })
2288
2338
  ] }),
@@ -2338,10 +2388,10 @@ function CountUp({
2338
2388
  className,
2339
2389
  ...props
2340
2390
  }) {
2341
- const ref = React30.useRef(null);
2342
- const [value, setValue] = React30.useState(from);
2343
- const hasStarted = React30.useRef(false);
2344
- React30.useEffect(() => {
2391
+ const ref = React31.useRef(null);
2392
+ const [value, setValue] = React31.useState(from);
2393
+ const hasStarted = React31.useRef(false);
2394
+ React31.useEffect(() => {
2345
2395
  const el = ref.current;
2346
2396
  if (!el) return;
2347
2397
  const observer = new IntersectionObserver(
@@ -2434,20 +2484,27 @@ function NewsletterSection({
2434
2484
  successDescription = defaults.successDescription,
2435
2485
  onSubscribe
2436
2486
  } = {}) {
2437
- const [email, setEmail] = React30.useState("");
2438
- const [submitted, setSubmitted] = React30.useState(false);
2487
+ const [email, setEmail] = React31.useState("");
2488
+ const [submitted, setSubmitted] = React31.useState(false);
2489
+ const mode = useUIVariant();
2490
+ const sectionClasses = cn$1(
2491
+ "overflow-hidden bg-card",
2492
+ mode === "smooth" && "rounded-3xl border",
2493
+ mode === "playful" && "rounded-2xl border-primary/25 border",
2494
+ !mode && "rounded-2xl border"
2495
+ );
2439
2496
  async function handleSubmit(e) {
2440
2497
  e.preventDefault();
2441
2498
  if (!email) return;
2442
2499
  await onSubscribe?.(email);
2443
2500
  setSubmitted(true);
2444
2501
  }
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: [
2502
+ return /* @__PURE__ */ jsx("section", { className: sectionClasses, children: /* @__PURE__ */ jsxs("div", { className: "relative px-6 py-14 sm:px-10 sm:py-20", children: [
2446
2503
  /* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-[radial-gradient(ellipse_at_top_right,hsl(var(--primary)/0.1),transparent_60%)]" }),
2447
2504
  /* @__PURE__ */ jsxs("div", { className: "relative mx-auto max-w-xl text-center", children: [
2448
2505
  /* @__PURE__ */ jsxs("div", { className: "mb-4 flex items-center justify-center gap-2", children: [
2449
2506
  /* @__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 })
2507
+ /* @__PURE__ */ jsx(Badge, { variant: "secondary", children: badge })
2451
2508
  ] }),
2452
2509
  /* @__PURE__ */ jsx("h2", { className: "text-balance text-3xl font-semibold tracking-tight sm:text-4xl", children: title }),
2453
2510
  /* @__PURE__ */ jsx("p", { className: "mx-auto mt-4 max-w-sm text-pretty text-muted-foreground", children: description }),
@@ -2468,7 +2525,7 @@ function NewsletterSection({
2468
2525
  }
2469
2526
  ),
2470
2527
  /* @__PURE__ */ jsx(
2471
- Input$1,
2528
+ Input,
2472
2529
  {
2473
2530
  type: "email",
2474
2531
  placeholder,
@@ -2478,7 +2535,7 @@ function NewsletterSection({
2478
2535
  className: "flex-1"
2479
2536
  }
2480
2537
  ),
2481
- /* @__PURE__ */ jsxs(Button$1, { type: "submit", children: [
2538
+ /* @__PURE__ */ jsxs(Button, { type: "submit", children: [
2482
2539
  submitLabel,
2483
2540
  /* @__PURE__ */ jsx(ArrowRight, { className: "ml-2 size-4" })
2484
2541
  ] })
@@ -2519,15 +2576,22 @@ function ContactSection({
2519
2576
  sendAnotherLabel = defaults2.sendAnotherLabel,
2520
2577
  onSubmit
2521
2578
  } = {}) {
2522
- const [submitted, setSubmitted] = React30.useState(false);
2579
+ const [submitted, setSubmitted] = React31.useState(false);
2580
+ const mode = useUIVariant();
2581
+ const sectionClasses = cn$1(
2582
+ "overflow-hidden bg-card",
2583
+ mode === "smooth" && "rounded-3xl border",
2584
+ mode === "playful" && "rounded-2xl border-primary/25 border",
2585
+ !mode && "rounded-2xl border"
2586
+ );
2523
2587
  async function handleSubmit(e) {
2524
2588
  e.preventDefault();
2525
2589
  await onSubmit?.(e);
2526
2590
  setSubmitted(true);
2527
2591
  }
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: [
2592
+ return /* @__PURE__ */ jsx("section", { className: sectionClasses, children: /* @__PURE__ */ jsxs("div", { className: "px-6 py-14 sm:px-10 sm:py-20", children: [
2529
2593
  /* @__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 }) }),
2594
+ badge && /* @__PURE__ */ jsx("div", { className: "mb-4 flex justify-center", children: /* @__PURE__ */ jsx(Badge, { variant: "secondary", children: badge }) }),
2531
2595
  /* @__PURE__ */ jsx("h2", { className: "text-balance text-4xl font-semibold tracking-tight sm:text-5xl", children: title }),
2532
2596
  /* @__PURE__ */ jsx("p", { className: "mt-4 text-lg text-muted-foreground", children: description }),
2533
2597
  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 +2603,7 @@ function ContactSection({
2539
2603
  /* @__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
2604
  /* @__PURE__ */ jsx("h3", { className: "mt-5 text-lg font-semibold", children: successTitle }),
2541
2605
  /* @__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 })
2606
+ /* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", className: "mt-6", onClick: () => setSubmitted(false), children: sendAnotherLabel })
2543
2607
  ] }) : /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, className: "mx-auto mt-16 max-w-xl sm:mt-20", children: [
2544
2608
  /* @__PURE__ */ jsx(
2545
2609
  "input",
@@ -2556,7 +2620,7 @@ function ContactSection({
2556
2620
  /* @__PURE__ */ jsxs("div", { children: [
2557
2621
  /* @__PURE__ */ jsx(Label, { htmlFor: "contact-first", className: "block text-sm/6 font-semibold", children: "First name" }),
2558
2622
  /* @__PURE__ */ jsx("div", { className: "mt-2.5", children: /* @__PURE__ */ jsx(
2559
- Input$1,
2623
+ Input,
2560
2624
  {
2561
2625
  id: "contact-first",
2562
2626
  name: "firstName",
@@ -2569,7 +2633,7 @@ function ContactSection({
2569
2633
  /* @__PURE__ */ jsxs("div", { children: [
2570
2634
  /* @__PURE__ */ jsx(Label, { htmlFor: "contact-last", className: "block text-sm/6 font-semibold", children: "Last name" }),
2571
2635
  /* @__PURE__ */ jsx("div", { className: "mt-2.5", children: /* @__PURE__ */ jsx(
2572
- Input$1,
2636
+ Input,
2573
2637
  {
2574
2638
  id: "contact-last",
2575
2639
  name: "lastName",
@@ -2586,7 +2650,7 @@ function ContactSection({
2586
2650
  /* @__PURE__ */ jsx("span", { className: "font-normal text-muted-foreground", children: "(optional)" })
2587
2651
  ] }),
2588
2652
  /* @__PURE__ */ jsx("div", { className: "mt-2.5", children: /* @__PURE__ */ jsx(
2589
- Input$1,
2653
+ Input,
2590
2654
  {
2591
2655
  id: "contact-company",
2592
2656
  name: "company",
@@ -2598,7 +2662,7 @@ function ContactSection({
2598
2662
  /* @__PURE__ */ jsxs("div", { className: "sm:col-span-2", children: [
2599
2663
  /* @__PURE__ */ jsx(Label, { htmlFor: "contact-email", className: "block text-sm/6 font-semibold", children: "Email" }),
2600
2664
  /* @__PURE__ */ jsx("div", { className: "mt-2.5", children: /* @__PURE__ */ jsx(
2601
- Input$1,
2665
+ Input,
2602
2666
  {
2603
2667
  id: "contact-email",
2604
2668
  name: "email",
@@ -2644,7 +2708,7 @@ function ContactSection({
2644
2708
  /* @__PURE__ */ jsxs("div", { className: "sm:col-span-2", children: [
2645
2709
  /* @__PURE__ */ jsx(Label, { htmlFor: "contact-message", className: "block text-sm/6 font-semibold", children: "Message" }),
2646
2710
  /* @__PURE__ */ jsx("div", { className: "mt-2.5", children: /* @__PURE__ */ jsx(
2647
- Textarea$1,
2711
+ Textarea,
2648
2712
  {
2649
2713
  id: "contact-message",
2650
2714
  name: "message",
@@ -2692,7 +2756,7 @@ function ContactSection({
2692
2756
  )
2693
2757
  ] })
2694
2758
  ] }),
2695
- /* @__PURE__ */ jsx("div", { className: "mt-10", children: /* @__PURE__ */ jsxs(Button$1, { type: "submit", className: "w-full", children: [
2759
+ /* @__PURE__ */ jsx("div", { className: "mt-10", children: /* @__PURE__ */ jsxs(Button, { type: "submit", className: "w-full", children: [
2696
2760
  "Let's talk",
2697
2761
  /* @__PURE__ */ jsx(Send, { className: "ml-2 size-4" })
2698
2762
  ] }) })
@@ -2802,15 +2866,15 @@ function QualificationSection({
2802
2866
  }) })
2803
2867
  ] }) }) });
2804
2868
  }
2805
- var defaultRenderLink8 = ({ href, children, className }) => /* @__PURE__ */ jsx("a", { href, className, children });
2869
+ var defaultRenderLink7 = ({ href, children, className }) => /* @__PURE__ */ jsx("a", { href, className, children });
2806
2870
  function Navbar({
2807
2871
  brand,
2808
2872
  navLinks,
2809
2873
  cta,
2810
2874
  controls,
2811
- renderLink = defaultRenderLink8
2875
+ renderLink = defaultRenderLink7
2812
2876
  }) {
2813
- const [open, setOpen] = React30.useState(false);
2877
+ const [open, setOpen] = React31.useState(false);
2814
2878
  const brandHref = brand.href ?? "/";
2815
2879
  const hasRightContent = controls?.length || cta?.primary || cta?.secondary;
2816
2880
  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 +2891,7 @@ function Navbar({
2827
2891
  children: link.label
2828
2892
  }) }, link.label)) }),
2829
2893
  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)),
2894
+ controls?.map((control, i) => /* @__PURE__ */ jsx(React31.Fragment, { children: control }, i)),
2831
2895
  (cta?.secondary || cta?.primary) && controls?.length ? /* @__PURE__ */ jsx("div", { className: "mx-1 h-4 w-px bg-border" }) : null,
2832
2896
  cta?.secondary && renderLink({
2833
2897
  href: cta.secondary.href,
@@ -2851,7 +2915,7 @@ function Navbar({
2851
2915
  /* @__PURE__ */ jsx("span", { children: brand.name })
2852
2916
  ] })
2853
2917
  }) }) }),
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,
2918
+ controls?.length ? /* @__PURE__ */ jsx("div", { className: "mb-4 flex w-full items-center justify-between gap-1", children: controls.map((control, i) => /* @__PURE__ */ jsx(React31.Fragment, { children: control }, i)) }) : null,
2855
2919
  /* @__PURE__ */ jsx(Separator, {}),
2856
2920
  /* @__PURE__ */ jsx("nav", { className: "mt-4 flex flex-col gap-1", children: navLinks.map((link) => /* @__PURE__ */ jsx("span", { onClick: () => setOpen(false), children: renderLink({
2857
2921
  href: link.href,
@@ -2872,14 +2936,14 @@ function Navbar({
2872
2936
  ] })
2873
2937
  ] }) });
2874
2938
  }
2875
- var defaultRenderLink9 = ({ href, children, className }) => /* @__PURE__ */ jsx("a", { href, className, children });
2939
+ var defaultRenderLink8 = ({ href, children, className }) => /* @__PURE__ */ jsx("a", { href, className, children });
2876
2940
  function Footer({
2877
2941
  brand,
2878
2942
  navLinks,
2879
2943
  socialLinks,
2880
2944
  legal,
2881
2945
  status,
2882
- renderLink = defaultRenderLink9
2946
+ renderLink = defaultRenderLink8
2883
2947
  }) {
2884
2948
  const brandHref = brand.href ?? "/";
2885
2949
  const copyrightText = legal ?? `\xA9 ${(/* @__PURE__ */ new Date()).getFullYear()} ${brand.name}. All rights reserved.`;
@@ -3000,7 +3064,7 @@ function LogoStrip({
3000
3064
  ] });
3001
3065
  }
3002
3066
  function GridUnderlay({ variant, size = 24, className, ...props }) {
3003
- const id = React30.useId();
3067
+ const id = React31.useId();
3004
3068
  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
3069
  /* @__PURE__ */ jsx("line", { x1: size / 2, y1: 0, x2: size / 2, y2: size, stroke: "currentColor", strokeWidth: 0.5 }),
3006
3070
  /* @__PURE__ */ jsx("line", { x1: 0, y1: size / 2, x2: size, y2: size / 2, stroke: "currentColor", strokeWidth: 0.5 })
@@ -3028,9 +3092,9 @@ function GlowUnderlay({
3028
3092
  style,
3029
3093
  ...props
3030
3094
  }) {
3031
- const [pos, setPos] = React30.useState({ x: "50%", y: "50%" });
3032
- const containerRef = React30.useRef(null);
3033
- React30.useEffect(() => {
3095
+ const [pos, setPos] = React31.useState({ x: "50%", y: "50%" });
3096
+ const containerRef = React31.useRef(null);
3097
+ React31.useEffect(() => {
3034
3098
  if (!followCursor) return;
3035
3099
  const handleMouseMove = (e) => {
3036
3100
  const el = containerRef.current;
@@ -3169,8 +3233,8 @@ function PageTransition({
3169
3233
  style,
3170
3234
  ...props
3171
3235
  }) {
3172
- const [mounted, setMounted] = React30.useState(false);
3173
- React30.useEffect(() => {
3236
+ const [mounted, setMounted] = React31.useState(false);
3237
+ React31.useEffect(() => {
3174
3238
  const id = requestAnimationFrame(() => setMounted(true));
3175
3239
  return () => cancelAnimationFrame(id);
3176
3240
  }, []);
@@ -3197,8 +3261,8 @@ function Spotlight({
3197
3261
  triggerClassName,
3198
3262
  shortcut = "\u2318K"
3199
3263
  }) {
3200
- const [open, setOpen] = React30.useState(false);
3201
- React30.useEffect(() => {
3264
+ const [open, setOpen] = React31.useState(false);
3265
+ React31.useEffect(() => {
3202
3266
  const down = (e) => {
3203
3267
  if (e.key === "k" && (e.metaKey || e.ctrlKey)) {
3204
3268
  e.preventDefault();
@@ -3228,7 +3292,7 @@ function Spotlight({
3228
3292
  /* @__PURE__ */ jsx(CommandInput, { placeholder }),
3229
3293
  /* @__PURE__ */ jsxs(CommandList, { children: [
3230
3294
  /* @__PURE__ */ jsx(CommandEmpty, { children: "No results found." }),
3231
- groups.map((group, i) => /* @__PURE__ */ jsxs(React30.Fragment, { children: [
3295
+ groups.map((group, i) => /* @__PURE__ */ jsxs(React31.Fragment, { children: [
3232
3296
  i > 0 && /* @__PURE__ */ jsx(CommandSeparator, {}),
3233
3297
  /* @__PURE__ */ jsx(CommandGroup, { heading: group.heading, children: group.items.map((item) => /* @__PURE__ */ jsxs(
3234
3298
  CommandItem,
@@ -3253,8 +3317,8 @@ function Spotlight({
3253
3317
  var BASE = 40;
3254
3318
  var MAGNIFY_RADIUS = 80;
3255
3319
  function Dock({ items, iconSize = BASE, magnification = 1.7, className, ...props }) {
3256
- const [mouseX, setMouseX] = React30.useState(null);
3257
- const containerRef = React30.useRef(null);
3320
+ const [mouseX, setMouseX] = React31.useState(null);
3321
+ const containerRef = React31.useRef(null);
3258
3322
  function getScale(index) {
3259
3323
  if (mouseX === null || !containerRef.current) return 1;
3260
3324
  const container = containerRef.current;
@@ -3353,7 +3417,7 @@ function ConfirmDialog({ isOpen, options, handleConfirm, handleCancel, destructi
3353
3417
  ] }) });
3354
3418
  }
3355
3419
  function RegisterHotkeys({ hotkeys }) {
3356
- React30.useEffect(() => {
3420
+ React31.useEffect(() => {
3357
3421
  const handleKeyDown = (event) => {
3358
3422
  for (const hotkey of hotkeys) {
3359
3423
  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 +3434,10 @@ function RegisterHotkeys({ hotkeys }) {
3370
3434
  return null;
3371
3435
  }
3372
3436
  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), []);
3437
+ const [isOpen, setIsOpen] = React31.useState(defaultOpen);
3438
+ const open = React31.useCallback(() => setIsOpen(true), []);
3439
+ const close = React31.useCallback(() => setIsOpen(false), []);
3440
+ const toggle = React31.useCallback(() => setIsOpen((prev) => !prev), []);
3377
3441
  return { isOpen, open, close, toggle, setIsOpen };
3378
3442
  }
3379
3443
  function CommandMenu({
@@ -3386,7 +3450,13 @@ function CommandMenu({
3386
3450
  }) {
3387
3451
  const internal = useControlledOpen(false);
3388
3452
  const isOpen = openProp ?? internal.isOpen;
3389
- const setOpen = onOpenChange ?? internal.setIsOpen;
3453
+ const setOpen = React31.useCallback(
3454
+ (next) => {
3455
+ if (openProp === void 0) internal.setIsOpen(next);
3456
+ onOpenChange?.(next);
3457
+ },
3458
+ [openProp, onOpenChange, internal.setIsOpen]
3459
+ );
3390
3460
  const runItem = (item) => {
3391
3461
  setOpen(false);
3392
3462
  item.onSelect();
@@ -3405,7 +3475,7 @@ function CommandMenu({
3405
3475
  /* @__PURE__ */ jsx(CommandInput, { placeholder }),
3406
3476
  /* @__PURE__ */ jsxs(CommandList, { children: [
3407
3477
  /* @__PURE__ */ jsx(CommandEmpty, { children: emptyMessage }),
3408
- groups.map((group, i) => /* @__PURE__ */ jsxs(React30.Fragment, { children: [
3478
+ groups.map((group, i) => /* @__PURE__ */ jsxs(React31.Fragment, { children: [
3409
3479
  i > 0 && /* @__PURE__ */ jsx(CommandSeparator, {}),
3410
3480
  /* @__PURE__ */ jsx(CommandGroup, { heading: group.heading, children: group.items.map((item) => /* @__PURE__ */ jsxs(
3411
3481
  CommandItem,
@@ -3446,19 +3516,20 @@ function FileUpload({
3446
3516
  hint,
3447
3517
  className
3448
3518
  }) {
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);
3519
+ const [internalFiles, setInternalFiles] = React31.useState(defaultFiles ?? []);
3520
+ const [isDragging, setIsDragging] = React31.useState(false);
3521
+ const [validationError, setValidationError] = React31.useState(null);
3522
+ const inputRef = React31.useRef(null);
3523
+ const dragDepth = React31.useRef(0);
3453
3524
  const files = filesProp ?? internalFiles;
3454
- const setFiles = React30.useCallback(
3525
+ const setFiles = React31.useCallback(
3455
3526
  (next) => {
3456
3527
  if (!filesProp) setInternalFiles(next);
3457
3528
  onFilesChange?.(next);
3458
3529
  },
3459
3530
  [filesProp, onFilesChange]
3460
3531
  );
3461
- const acceptList = React30.useMemo(
3532
+ const acceptList = React31.useMemo(
3462
3533
  () => accept?.split(",").map((a) => a.trim().toLowerCase()).filter(Boolean) ?? [],
3463
3534
  [accept]
3464
3535
  );
@@ -3472,7 +3543,7 @@ function FileUpload({
3472
3543
  const handleFiles = (fileList) => {
3473
3544
  if (!fileList || disabled) return;
3474
3545
  const incoming = Array.from(fileList);
3475
- const room = maxFiles ? Math.max(0, maxFiles - files.length) : Infinity;
3546
+ const room = maxFiles && multiple ? Math.max(0, maxFiles - files.length) : maxFiles || Infinity;
3476
3547
  if (maxFiles && room <= 0) {
3477
3548
  setValidationError(`You can only add up to ${maxFiles} file${maxFiles === 1 ? "" : "s"}.`);
3478
3549
  return;
@@ -3507,15 +3578,25 @@ function FileUpload({
3507
3578
  tabIndex: disabled ? -1 : 0,
3508
3579
  onClick: () => !disabled && inputRef.current?.click(),
3509
3580
  onKeyDown: (e) => {
3510
- if (e.key === "Enter" || e.key === " ") inputRef.current?.click();
3581
+ if (disabled) return;
3582
+ if (e.key === "Enter" || e.key === " ") {
3583
+ e.preventDefault();
3584
+ inputRef.current?.click();
3585
+ }
3511
3586
  },
3512
- onDragOver: (e) => {
3587
+ onDragOver: (e) => e.preventDefault(),
3588
+ onDragEnter: (e) => {
3513
3589
  e.preventDefault();
3590
+ dragDepth.current += 1;
3514
3591
  if (!disabled) setIsDragging(true);
3515
3592
  },
3516
- onDragLeave: () => setIsDragging(false),
3593
+ onDragLeave: () => {
3594
+ dragDepth.current = Math.max(0, dragDepth.current - 1);
3595
+ if (dragDepth.current === 0) setIsDragging(false);
3596
+ },
3517
3597
  onDrop: (e) => {
3518
3598
  e.preventDefault();
3599
+ dragDepth.current = 0;
3519
3600
  setIsDragging(false);
3520
3601
  handleFiles(e.dataTransfer.files);
3521
3602
  },
@@ -3549,7 +3630,7 @@ function FileUpload({
3549
3630
  ]
3550
3631
  }
3551
3632
  ),
3552
- validationError && /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-destructive", children: validationError }),
3633
+ validationError && /* @__PURE__ */ jsx("p", { role: "alert", className: "text-sm font-medium text-destructive", children: validationError }),
3553
3634
  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
3635
  /* @__PURE__ */ jsx(File, { className: "size-5 shrink-0 text-muted-foreground" }),
3555
3636
  /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
@@ -3586,6 +3667,8 @@ function FileUpload({
3586
3667
  var variantIcon = {
3587
3668
  success: /* @__PURE__ */ jsx(CheckCircle2, { className: "size-5 text-primary" }),
3588
3669
  info: /* @__PURE__ */ jsx(Info, { className: "size-5 text-muted-foreground" }),
3670
+ warning: /* @__PURE__ */ jsx(AlertTriangle, { className: "size-5 text-amber-500 dark:text-amber-400" }),
3671
+ error: /* @__PURE__ */ jsx(AlertCircle, { className: "size-5 text-destructive" }),
3589
3672
  message: /* @__PURE__ */ jsx(Inbox, { className: "size-5 text-muted-foreground" })
3590
3673
  };
3591
3674
  function NotificationToast({
@@ -3620,6 +3703,104 @@ function notify(options) {
3620
3703
  const { duration, ...toastProps } = options;
3621
3704
  return toast.custom((id) => /* @__PURE__ */ jsx(NotificationToast, { ...toastProps, onDismiss: () => toast.dismiss(id) }), { duration });
3622
3705
  }
3706
+ function NotificationsPopover({
3707
+ notifications,
3708
+ onNotificationClick,
3709
+ onMarkAllRead,
3710
+ title = "Notifications",
3711
+ emptyMessage = "Nothing new \u2014 you\u2019re all caught up.",
3712
+ align = "end",
3713
+ open,
3714
+ onOpenChange,
3715
+ className
3716
+ }) {
3717
+ const unreadCount = notifications.filter((n) => !n.read).length;
3718
+ return /* @__PURE__ */ jsxs(Popover, { open, onOpenChange, children: [
3719
+ /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(Button, { variant: "ghost", size: "icon", className: cn$1("relative size-8", className), children: [
3720
+ /* @__PURE__ */ jsx(Bell, { className: "size-4" }),
3721
+ 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 }),
3722
+ /* @__PURE__ */ jsxs("span", { className: "sr-only", children: [
3723
+ title,
3724
+ unreadCount > 0 ? ` (${unreadCount} unread)` : ""
3725
+ ] })
3726
+ ] }) }),
3727
+ /* @__PURE__ */ jsxs(PopoverContent, { align, className: "w-80 p-0", children: [
3728
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between border-b px-4 py-3", children: [
3729
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-semibold", children: title }),
3730
+ onMarkAllRead && unreadCount > 0 && /* @__PURE__ */ jsx(
3731
+ "button",
3732
+ {
3733
+ type: "button",
3734
+ onClick: onMarkAllRead,
3735
+ className: "text-xs font-medium text-muted-foreground hover:text-foreground",
3736
+ children: "Mark all read"
3737
+ }
3738
+ )
3739
+ ] }),
3740
+ notifications.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-2 px-4 py-10 text-center", children: [
3741
+ /* @__PURE__ */ jsx(Inbox, { className: "size-6 text-muted-foreground" }),
3742
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: emptyMessage })
3743
+ ] }) : /* @__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(
3744
+ "button",
3745
+ {
3746
+ type: "button",
3747
+ onClick: () => onNotificationClick?.(notification),
3748
+ className: cn$1(
3749
+ "flex w-full items-start gap-3 px-4 py-3 text-left transition-colors hover:bg-muted/60",
3750
+ !notification.read && "bg-muted/40"
3751
+ ),
3752
+ children: [
3753
+ /* @__PURE__ */ jsx(
3754
+ "span",
3755
+ {
3756
+ "aria-hidden": true,
3757
+ className: cn$1(
3758
+ "mt-1.5 size-2 shrink-0 rounded-full",
3759
+ notification.read ? "bg-transparent" : "bg-primary"
3760
+ )
3761
+ }
3762
+ ),
3763
+ notification.avatar ? /* @__PURE__ */ jsxs(Avatar, { className: "size-8 shrink-0", children: [
3764
+ /* @__PURE__ */ jsx(AvatarImage, { src: notification.avatar, alt: "" }),
3765
+ /* @__PURE__ */ jsx(AvatarFallback, { children: notification.title.slice(0, 2).toUpperCase() })
3766
+ ] }) : notification.icon ? /* @__PURE__ */ jsx("span", { className: "mt-0.5 shrink-0 text-muted-foreground", children: notification.icon }) : null,
3767
+ /* @__PURE__ */ jsxs("span", { className: "min-w-0 flex-1", children: [
3768
+ /* @__PURE__ */ jsx("span", { className: cn$1("block truncate text-sm", !notification.read && "font-medium"), children: notification.title }),
3769
+ notification.description && /* @__PURE__ */ jsx("span", { className: "mt-0.5 line-clamp-2 block text-xs text-muted-foreground", children: notification.description }),
3770
+ notification.timestamp && /* @__PURE__ */ jsx("span", { className: "mt-1 block text-xs text-muted-foreground/70", children: notification.timestamp })
3771
+ ] })
3772
+ ]
3773
+ }
3774
+ ) }, notification.id)) })
3775
+ ] })
3776
+ ] });
3777
+ }
3778
+ function ActivityFeed({
3779
+ items,
3780
+ emptyMessage = "No activity yet.",
3781
+ className
3782
+ }) {
3783
+ if (items.length === 0) {
3784
+ return /* @__PURE__ */ jsxs("div", { className: cn$1("flex flex-col items-center gap-2 py-10 text-center", className), children: [
3785
+ /* @__PURE__ */ jsx(Activity, { className: "size-6 text-muted-foreground" }),
3786
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: emptyMessage })
3787
+ ] });
3788
+ }
3789
+ return /* @__PURE__ */ jsx("ol", { className, children: items.map((item, i) => /* @__PURE__ */ jsxs("li", { className: "relative flex gap-3 pb-6 last:pb-0", children: [
3790
+ 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" }),
3791
+ /* @__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: [
3792
+ /* @__PURE__ */ jsx(AvatarImage, { src: item.avatar, alt: "" }),
3793
+ /* @__PURE__ */ jsx(AvatarFallback, { className: "text-xs", children: item.initials ?? "?" })
3794
+ ] }) : /* @__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, {}) }) }),
3795
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1 pt-1", children: [
3796
+ /* @__PURE__ */ jsxs("div", { className: "flex items-baseline justify-between gap-2", children: [
3797
+ /* @__PURE__ */ jsx("p", { className: "text-sm", children: item.title }),
3798
+ item.timestamp && /* @__PURE__ */ jsx("span", { className: "shrink-0 text-xs text-muted-foreground", children: item.timestamp })
3799
+ ] }),
3800
+ item.description && /* @__PURE__ */ jsx("p", { className: "mt-0.5 text-xs text-muted-foreground", children: item.description })
3801
+ ] })
3802
+ ] }, item.id)) });
3803
+ }
3623
3804
  function AppScreenRoot({ children, className }) {
3624
3805
  return /* @__PURE__ */ jsx("div", { className: cn("flex h-full flex-col", className), children });
3625
3806
  }
@@ -4002,20 +4183,20 @@ function FullPageSpinner({ message }) {
4002
4183
  message && /* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-sm", children: message })
4003
4184
  ] });
4004
4185
  }
4005
- var defaultRenderLink10 = ({ href, children, className }) => /* @__PURE__ */ jsx("a", { href, className, children });
4186
+ var defaultRenderLink9 = ({ href, children, className }) => /* @__PURE__ */ jsx("a", { href, className, children });
4006
4187
  function PageHeader({
4007
4188
  title,
4008
4189
  description,
4009
4190
  breadcrumbs,
4010
4191
  backButton,
4011
- renderLink = defaultRenderLink10,
4192
+ renderLink = defaultRenderLink9,
4012
4193
  className,
4013
4194
  children
4014
4195
  }) {
4015
4196
  return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col gap-1 pb-6", className), children: [
4016
4197
  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
4198
  const isLast = i === breadcrumbs.length - 1;
4018
- return /* @__PURE__ */ jsxs(React30.Fragment, { children: [
4199
+ return /* @__PURE__ */ jsxs(React31.Fragment, { children: [
4019
4200
  i > 0 && /* @__PURE__ */ jsx(ChevronRight, { className: "size-3.5 shrink-0" }),
4020
4201
  isLast || !crumb.href ? /* @__PURE__ */ jsx("span", { className: cn(isLast && "text-foreground font-medium"), children: crumb.label }) : renderLink({
4021
4202
  href: crumb.href,
@@ -4051,7 +4232,7 @@ function PageHeader({
4051
4232
  ] });
4052
4233
  }
4053
4234
  function Suspensed({ children, fallback }) {
4054
- return /* @__PURE__ */ jsx(React30.Suspense, { fallback: fallback ?? /* @__PURE__ */ jsx(Spinner, {}), children });
4235
+ return /* @__PURE__ */ jsx(React31.Suspense, { fallback: fallback ?? /* @__PURE__ */ jsx(Spinner, {}), children });
4055
4236
  }
4056
4237
  function getInitialTheme() {
4057
4238
  if (typeof document === "undefined") return "dark";
@@ -4059,7 +4240,7 @@ function getInitialTheme() {
4059
4240
  return stored ?? (document.documentElement.classList.contains("dark") ? "dark" : "light");
4060
4241
  }
4061
4242
  function ThemeSwitchMinimal() {
4062
- const [theme, setThemeState] = React30.useState(getInitialTheme);
4243
+ const [theme, setThemeState] = React31.useState(getInitialTheme);
4063
4244
  const toggle = () => {
4064
4245
  const next = theme === "dark" ? "light" : "dark";
4065
4246
  setThemeState(next);
@@ -4185,9 +4366,9 @@ function getInitialColorName() {
4185
4366
  return localStorage.getItem(STORAGE_KEY) ?? "emerald";
4186
4367
  }
4187
4368
  function BrandColorSwitchMinimal() {
4188
- const [active, setActive] = React30.useState(getInitialColorName);
4189
- const [open, setOpen] = React30.useState(false);
4190
- React30.useEffect(() => {
4369
+ const [active, setActive] = React31.useState(getInitialColorName);
4370
+ const [open, setOpen] = React31.useState(false);
4371
+ React31.useEffect(() => {
4191
4372
  const saved = localStorage.getItem(STORAGE_KEY) ?? "emerald";
4192
4373
  const color = BRAND_COLORS.find((c) => c.name === saved);
4193
4374
  if (color) applyColor(color);
@@ -4248,7 +4429,7 @@ function BrandColorSwitchMinimal() {
4248
4429
  ] });
4249
4430
  }
4250
4431
  function ThemeColorUpdater({ colorTheme }) {
4251
- React30.useEffect(() => {
4432
+ React31.useEffect(() => {
4252
4433
  if (colorTheme) {
4253
4434
  document.documentElement.setAttribute("data-theme", colorTheme);
4254
4435
  } else {
@@ -4258,7 +4439,7 @@ function ThemeColorUpdater({ colorTheme }) {
4258
4439
  return null;
4259
4440
  }
4260
4441
  function VersionBanner({ version, message, className, onDismiss }) {
4261
- const [dismissed, setDismissed] = React30.useState(false);
4442
+ const [dismissed, setDismissed] = React31.useState(false);
4262
4443
  if (dismissed) return null;
4263
4444
  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
4445
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
@@ -4515,29 +4696,29 @@ function useMounted() {
4515
4696
  return mounted;
4516
4697
  }
4517
4698
  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 = {}) => {
4699
+ const [isOpen, setIsOpen] = React31.useState(false);
4700
+ const [options, setOptions] = React31.useState({});
4701
+ const resolveRef = React31.useRef(null);
4702
+ const confirm = React31.useCallback((opts = {}) => {
4522
4703
  setOptions(opts);
4523
4704
  setIsOpen(true);
4524
4705
  return new Promise((resolve) => {
4525
4706
  resolveRef.current = resolve;
4526
4707
  });
4527
4708
  }, []);
4528
- const handleConfirm = React30.useCallback(() => {
4709
+ const handleConfirm = React31.useCallback(() => {
4529
4710
  resolveRef.current?.(true);
4530
4711
  setIsOpen(false);
4531
4712
  }, []);
4532
- const handleCancel = React30.useCallback(() => {
4713
+ const handleCancel = React31.useCallback(() => {
4533
4714
  resolveRef.current?.(false);
4534
4715
  setIsOpen(false);
4535
4716
  }, []);
4536
4717
  return { confirm, isOpen, options, handleConfirm, handleCancel };
4537
4718
  }
4538
4719
  function useScrolledPast(threshold) {
4539
- const [scrolledPast, setScrolledPast] = React30.useState(false);
4540
- React30.useEffect(() => {
4720
+ const [scrolledPast, setScrolledPast] = React31.useState(false);
4721
+ React31.useEffect(() => {
4541
4722
  const handleScroll = () => {
4542
4723
  setScrolledPast(window.scrollY > threshold);
4543
4724
  };
@@ -4548,42 +4729,28 @@ function useScrolledPast(threshold) {
4548
4729
  return scrolledPast;
4549
4730
  }
4550
4731
  function useCopyToClipboard() {
4551
- const [copied, setCopied] = React30.useState(false);
4552
- const timeoutRef = React30.useRef(void 0);
4553
- const copy = React30.useCallback((text) => {
4732
+ const [copied, setCopied] = React31.useState(false);
4733
+ const timeoutRef = React31.useRef(void 0);
4734
+ const copy = React31.useCallback((text) => {
4554
4735
  navigator.clipboard.writeText(text).then(() => {
4555
4736
  setCopied(true);
4556
4737
  clearTimeout(timeoutRef.current);
4557
4738
  timeoutRef.current = setTimeout(() => setCopied(false), 2e3);
4558
4739
  });
4559
4740
  }, []);
4560
- React30.useEffect(() => () => clearTimeout(timeoutRef.current), []);
4741
+ React31.useEffect(() => () => clearTimeout(timeoutRef.current), []);
4561
4742
  return [copied, copy];
4562
4743
  }
4563
4744
  function useDebounce(value, delay) {
4564
- const [debounced, setDebounced] = React30.useState(value);
4565
- React30.useEffect(() => {
4745
+ const [debounced, setDebounced] = React31.useState(value);
4746
+ React31.useEffect(() => {
4566
4747
  const timer = setTimeout(() => setDebounced(value), delay);
4567
4748
  return () => clearTimeout(timer);
4568
4749
  }, [value, delay]);
4569
4750
  return debounced;
4570
4751
  }
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
4752
  function useLocalStorage(key, initialValue) {
4586
- const [storedValue, setStoredValue] = React30.useState(() => {
4753
+ const [storedValue, setStoredValue] = React31.useState(() => {
4587
4754
  if (typeof window === "undefined") return initialValue;
4588
4755
  try {
4589
4756
  const item = window.localStorage.getItem(key);
@@ -4592,7 +4759,7 @@ function useLocalStorage(key, initialValue) {
4592
4759
  return initialValue;
4593
4760
  }
4594
4761
  });
4595
- const setValue = React30.useCallback(
4762
+ const setValue = React31.useCallback(
4596
4763
  (value) => {
4597
4764
  try {
4598
4765
  setStoredValue(value);
@@ -4607,11 +4774,11 @@ function useLocalStorage(key, initialValue) {
4607
4774
  return [storedValue, setValue];
4608
4775
  }
4609
4776
  function useMediaQuery(query) {
4610
- const [matches, setMatches] = React30.useState(() => {
4777
+ const [matches, setMatches] = React31.useState(() => {
4611
4778
  if (typeof window === "undefined") return false;
4612
4779
  return window.matchMedia(query).matches;
4613
4780
  });
4614
- React30.useEffect(() => {
4781
+ React31.useEffect(() => {
4615
4782
  if (typeof window === "undefined") return;
4616
4783
  const mql = window.matchMedia(query);
4617
4784
  setMatches(mql.matches);
@@ -4622,18 +4789,18 @@ function useMediaQuery(query) {
4622
4789
  return matches;
4623
4790
  }
4624
4791
  function usePagination(total, pageSize) {
4625
- const [page, setPage] = React30.useState(1);
4792
+ const [page, setPage] = React31.useState(1);
4626
4793
  const totalPages = Math.max(1, Math.ceil(total / pageSize));
4627
4794
  const offset = (page - 1) * pageSize;
4628
- const goTo = React30.useCallback(
4795
+ const goTo = React31.useCallback(
4629
4796
  (p) => setPage(Math.min(Math.max(1, p), totalPages)),
4630
4797
  [totalPages]
4631
4798
  );
4632
- const next = React30.useCallback(() => goTo(page + 1), [page, goTo]);
4633
- const prev = React30.useCallback(() => goTo(page - 1), [page, goTo]);
4799
+ const next = React31.useCallback(() => goTo(page + 1), [page, goTo]);
4800
+ const prev = React31.useCallback(() => goTo(page - 1), [page, goTo]);
4634
4801
  return { page, pageSize, totalPages, offset, hasPrev: page > 1, hasNext: page < totalPages, goTo, next, prev };
4635
4802
  }
4636
4803
 
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 };
4804
+ export { ActivityFeed, AnimatedPill, AppContent, AppGrid, AppGridCell, AppScreen, AppShell, AuthSection, Badge, BillingPanel, BrandColorSwitchMinimal, Button, Card, Carousel, ChangelogCard, ChangelogList, 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, 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
4805
  //# sourceMappingURL=index.js.map
4639
4806
  //# sourceMappingURL=index.js.map