@olwiba/ui 0.1.1 → 0.1.3

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,29 +1,32 @@
1
1
  import { cn } from './chunk-AMWLSHD6.js';
2
2
  export { MdxContent, cn } from './chunk-AMWLSHD6.js';
3
- import * as React25 from 'react';
3
+ import * as React26 from 'react';
4
4
  import { useState, useEffect } 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, Separator, Accordion, AccordionItem, AccordionTrigger, AccordionContent, Label, Sheet, SheetTrigger, SheetContent, 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, Spinner, SidebarProvider, SidebarInset, CardHeader, CardTitle, CardDescription, CardContent, ContextMenuSeparator, ContextMenuLabel, ContextMenuSub, ContextMenuSubTrigger, ContextMenuSubContent, ContextMenuItem, ContextMenuShortcut, Sidebar, SidebarHeader, SidebarMenu, SidebarMenuItem, SidebarMenuButton, SidebarContent, SidebarGroup, SidebarGroupContent, SidebarFooter, SidebarTrigger, useSidebar, DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuGroup, DropdownMenuItem } 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, useUIVariant, Separator, Accordion, AccordionItem, AccordionTrigger, AccordionContent, Label, Sheet, SheetTrigger, SheetContent, 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, Spinner, SidebarProvider, SidebarInset, CardHeader, CardTitle, CardDescription, CardContent, ContextMenuSeparator, ContextMenuLabel, ContextMenuSub, ContextMenuSubTrigger, ContextMenuSubContent, ContextMenuItem, ContextMenuShortcut, Sidebar, SidebarHeader, SidebarMenu, SidebarMenuItem, SidebarMenuButton, SidebarContent, SidebarGroup, SidebarGroupContent, SidebarFooter, SidebarTrigger, useSidebar, DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuGroup, DropdownMenuItem } from '@olwiba/cn';
6
6
  export { CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@olwiba/cn';
7
7
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
8
- import { Compass, ArrowLeft, X, ArrowRight, Sparkles, Check, Minus, Quote, Star, Twitter, Github, Linkedin, Mail, Phone, MapPin, MessageSquare, Send, Sun, Moon, Menu, Search, TrendingUp, TrendingDown, ChevronRight, ChevronLeft, AlertTriangle, Loader2, ShieldCheck, Building2, MoreVerticalIcon, CreditCardIcon, BellIcon, LogOutIcon } from 'lucide-react';
8
+ import { Compass, ArrowLeft, X, ArrowRight, ChevronLeft, ChevronRight, Sparkles, Check, Minus, Quote, Star, Twitter, Github, Linkedin, Mail, Phone, MapPin, MessageSquare, Send, Menu, Search, TrendingUp, TrendingDown, Sun, Moon, AlertTriangle, Loader2, Layers, LayoutGrid, ShieldCheck, Building2, MoreVerticalIcon, CreditCardIcon, BellIcon, LogOutIcon } from 'lucide-react';
9
9
 
10
- var OlwibaUIContext = React25.createContext({
10
+ var OlwibaUIContext = React26.createContext({
11
11
  isMobile: false,
12
- mode: "default"
12
+ mode: "default",
13
+ setMode: () => {
14
+ }
13
15
  });
14
- function OlwibaUIProvider({ children, isMobile: isMobileProp, mode = "default" }) {
16
+ function OlwibaUIProvider({ children, isMobile: isMobileProp, mode: initialMode = "default" }) {
15
17
  const detectedMobile = useIsMobile();
16
18
  const isMobile = isMobileProp ?? detectedMobile;
19
+ const [mode, setMode] = React26.useState(initialMode);
17
20
  const variant = mode !== "default" ? mode : void 0;
18
- return /* @__PURE__ */ jsx(OlwibaUIContext.Provider, { value: { isMobile, mode }, children: /* @__PURE__ */ jsx(UIVariantProvider, { mode: variant, children }) });
21
+ return /* @__PURE__ */ jsx(OlwibaUIContext.Provider, { value: { isMobile, mode, setMode }, children: /* @__PURE__ */ jsx(UIVariantProvider, { mode: variant, children }) });
19
22
  }
20
23
  function useOlwibaUI() {
21
- return React25.useContext(OlwibaUIContext);
24
+ return React26.useContext(OlwibaUIContext);
22
25
  }
23
26
  function useUIMode() {
24
- return React25.useContext(OlwibaUIContext).mode;
27
+ return React26.useContext(OlwibaUIContext).mode;
25
28
  }
26
- var Button = React25.forwardRef(
29
+ var Button = React26.forwardRef(
27
30
  ({ mode, ...props }, ref) => {
28
31
  const ctxMode = useUIMode();
29
32
  const resolvedMode = mode ?? (ctxMode !== "default" ? ctxMode : void 0);
@@ -31,7 +34,7 @@ var Button = React25.forwardRef(
31
34
  }
32
35
  );
33
36
  Button.displayName = "Button";
34
- var Card = React25.forwardRef(
37
+ var Card = React26.forwardRef(
35
38
  ({ mode, ...props }, ref) => {
36
39
  const ctxMode = useUIMode();
37
40
  const resolvedMode = mode ?? (ctxMode !== "default" ? ctxMode : void 0);
@@ -42,7 +45,7 @@ Card.displayName = "Card";
42
45
  function Badge(props) {
43
46
  return /* @__PURE__ */ jsx(Badge$1, { ...props });
44
47
  }
45
- var Input = React25.forwardRef(
48
+ var Input = React26.forwardRef(
46
49
  ({ mode, ...props }, ref) => {
47
50
  const ctxMode = useUIMode();
48
51
  const resolvedMode = mode ?? (ctxMode !== "default" ? ctxMode : void 0);
@@ -50,7 +53,7 @@ var Input = React25.forwardRef(
50
53
  }
51
54
  );
52
55
  Input.displayName = "Input";
53
- var Textarea = React25.forwardRef(
56
+ var Textarea = React26.forwardRef(
54
57
  ({ mode, ...props }, ref) => {
55
58
  const ctxMode = useUIMode();
56
59
  const resolvedMode = mode ?? (ctxMode !== "default" ? ctxMode : void 0);
@@ -58,13 +61,13 @@ var Textarea = React25.forwardRef(
58
61
  }
59
62
  );
60
63
  Textarea.displayName = "Textarea";
61
- var Checkbox = React25.forwardRef(({ mode, ...props }, ref) => {
64
+ var Checkbox = React26.forwardRef(({ mode, ...props }, ref) => {
62
65
  const ctxMode = useUIMode();
63
66
  const resolvedMode = mode ?? (ctxMode !== "default" ? ctxMode : void 0);
64
67
  return /* @__PURE__ */ jsx(Checkbox$1, { mode: resolvedMode, ...props, ref });
65
68
  });
66
69
  Checkbox.displayName = "Checkbox";
67
- var Switch = React25.forwardRef(({ mode: _mode, ...props }, ref) => {
70
+ var Switch = React26.forwardRef(({ mode: _mode, ...props }, ref) => {
68
71
  return /* @__PURE__ */ jsx(Switch$1, { ...props, ref });
69
72
  });
70
73
  Switch.displayName = "Switch";
@@ -754,9 +757,9 @@ function FadeIn({
754
757
  style,
755
758
  ...props
756
759
  }) {
757
- const ref = React25.useRef(null);
758
- const [visible, setVisible] = React25.useState(false);
759
- React25.useEffect(() => {
760
+ const ref = React26.useRef(null);
761
+ const [visible, setVisible] = React26.useState(false);
762
+ React26.useEffect(() => {
760
763
  const el = ref.current;
761
764
  if (!el) return;
762
765
  const observer = new IntersectionObserver(
@@ -889,17 +892,28 @@ function HeroSection({
889
892
  ] }) });
890
893
  }
891
894
  function FeatureCard({ icon: Icon, title, description, href, className, ...props }) {
892
- const content = /* @__PURE__ */ jsxs(
895
+ const mode = useUIVariant();
896
+ const cardInner = /* @__PURE__ */ jsxs(
893
897
  "div",
894
898
  {
895
899
  className: cn$1(
896
- "group relative flex flex-col gap-4 rounded-2xl border bg-card p-6 transition-all duration-200 hover:-translate-y-0.5 hover:shadow-md",
900
+ "group relative flex h-full flex-col gap-4 border bg-card p-6 transition-all duration-200",
901
+ mode === "playful" ? "rounded-2xl rotate-[0.3deg]" : mode === "smooth" ? "rounded-3xl hover:-translate-y-0.5 hover:shadow-md" : "rounded-2xl hover:-translate-y-0.5 hover:shadow-md",
897
902
  href && "cursor-pointer",
898
- className
903
+ mode !== "playful" && className
899
904
  ),
900
- ...props,
905
+ ...mode !== "playful" ? props : {},
901
906
  children: [
902
- /* @__PURE__ */ jsx("div", { className: "flex h-10 w-10 items-center justify-center rounded-xl bg-primary/10 text-primary transition-colors group-hover:bg-primary group-hover:text-primary-foreground", children: /* @__PURE__ */ jsx(Icon, { className: "size-5" }) }),
907
+ /* @__PURE__ */ jsx(
908
+ "div",
909
+ {
910
+ className: cn$1(
911
+ "flex h-10 w-10 items-center justify-center bg-primary/10 text-primary transition-colors group-hover:bg-primary group-hover:text-primary-foreground",
912
+ mode === "smooth" ? "rounded-2xl" : "rounded-xl"
913
+ ),
914
+ children: /* @__PURE__ */ jsx(Icon, { className: "size-5" })
915
+ }
916
+ ),
903
917
  /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
904
918
  /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
905
919
  /* @__PURE__ */ jsx("h3", { className: "font-semibold leading-tight", children: title }),
@@ -910,8 +924,18 @@ function FeatureCard({ icon: Icon, title, description, href, className, ...props
910
924
  ]
911
925
  }
912
926
  );
927
+ const content = mode === "playful" ? /* @__PURE__ */ jsxs("div", { className: cn$1("group/playful relative h-full", className), ...props, children: [
928
+ /* @__PURE__ */ jsx(
929
+ "div",
930
+ {
931
+ "aria-hidden": "true",
932
+ className: "absolute inset-0 rounded-2xl bg-border transition-transform duration-200 translate-x-[5px] translate-y-[5px] -rotate-[0.5deg] group-hover/playful:-rotate-[1.5deg] group-hover/playful:translate-x-[6px] group-hover/playful:translate-y-[6px]"
933
+ }
934
+ ),
935
+ cardInner
936
+ ] }) : cardInner;
913
937
  if (href) {
914
- return /* @__PURE__ */ jsx("a", { href, className: "block", children: content });
938
+ return /* @__PURE__ */ jsx("a", { href, className: "block h-full", children: content });
915
939
  }
916
940
  return content;
917
941
  }
@@ -932,9 +956,9 @@ function StaggerChildren({
932
956
  className,
933
957
  ...props
934
958
  }) {
935
- const ref = React25.useRef(null);
936
- const [visible, setVisible] = React25.useState(false);
937
- React25.useEffect(() => {
959
+ const ref = React26.useRef(null);
960
+ const [visible, setVisible] = React26.useState(false);
961
+ React26.useEffect(() => {
938
962
  const el = ref.current;
939
963
  if (!el) return;
940
964
  const observer = new IntersectionObserver(
@@ -952,9 +976,10 @@ function StaggerChildren({
952
976
  return () => observer.disconnect();
953
977
  }, [once]);
954
978
  const [tx, ty] = translateMap2[direction];
955
- return /* @__PURE__ */ jsx("div", { ref, className: cn$1(className), ...props, children: React25.Children.map(children, (child, i) => /* @__PURE__ */ jsx(
979
+ return /* @__PURE__ */ jsx("div", { ref, className: cn$1(className), ...props, children: React26.Children.map(children, (child, i) => /* @__PURE__ */ jsx(
956
980
  "div",
957
981
  {
982
+ className: "h-full",
958
983
  style: {
959
984
  transition: `opacity ${duration}ms ease, transform ${duration}ms ease`,
960
985
  transitionDelay: `${delay + i * stagger}ms`,
@@ -971,7 +996,14 @@ function FeaturesSection({
971
996
  badge = "Features",
972
997
  features
973
998
  }) {
974
- 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-4xl", children: [
999
+ const mode = useUIVariant();
1000
+ const sectionClasses = cn$1(
1001
+ "overflow-hidden bg-card",
1002
+ mode === "smooth" && "rounded-3xl border",
1003
+ mode === "playful" && "rounded-2xl border-primary/25 border",
1004
+ !mode && "rounded-2xl border"
1005
+ );
1006
+ 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: [
975
1007
  /* @__PURE__ */ jsx(SectionTitle, { title, description, badge }),
976
1008
  /* @__PURE__ */ jsx(StaggerChildren, { className: "mt-12 grid gap-8 sm:grid-cols-2 lg:grid-cols-3", children: features.map((feature) => /* @__PURE__ */ jsx(
977
1009
  FeatureCard,
@@ -985,6 +1017,106 @@ function FeaturesSection({
985
1017
  )) })
986
1018
  ] }) }) });
987
1019
  }
1020
+ function CarouselSection({
1021
+ title = "Explore the platform",
1022
+ description,
1023
+ badge = "Features",
1024
+ features
1025
+ }) {
1026
+ const mode = useUIVariant();
1027
+ const trackRef = React26.useRef(null);
1028
+ const [canPrev, setCanPrev] = React26.useState(false);
1029
+ const [canNext, setCanNext] = React26.useState(true);
1030
+ const updateScrollState = React26.useCallback(() => {
1031
+ const track = trackRef.current;
1032
+ if (!track) return;
1033
+ setCanPrev(track.scrollLeft > 8);
1034
+ setCanNext(track.scrollLeft < track.scrollWidth - track.clientWidth - 8);
1035
+ }, []);
1036
+ React26.useEffect(() => {
1037
+ const track = trackRef.current;
1038
+ if (!track) return;
1039
+ updateScrollState();
1040
+ track.addEventListener("scroll", updateScrollState, { passive: true });
1041
+ const observer = new ResizeObserver(updateScrollState);
1042
+ observer.observe(track);
1043
+ return () => {
1044
+ track.removeEventListener("scroll", updateScrollState);
1045
+ observer.disconnect();
1046
+ };
1047
+ }, [updateScrollState]);
1048
+ const scrollByCard = (direction) => {
1049
+ const track = trackRef.current;
1050
+ if (!track) return;
1051
+ const card = track.querySelector("[data-carousel-item]");
1052
+ const step = card ? card.offsetWidth + 24 : track.clientWidth * 0.8;
1053
+ track.scrollBy({ left: direction * step, behavior: "smooth" });
1054
+ };
1055
+ const sectionClasses = cn$1(
1056
+ "overflow-hidden bg-card",
1057
+ mode === "smooth" && "rounded-3xl border",
1058
+ mode === "playful" && "rounded-2xl border-primary/25 border",
1059
+ !mode && "rounded-2xl border"
1060
+ );
1061
+ const controlClasses = cn$1(
1062
+ "flex size-9 items-center justify-center border bg-background text-foreground transition-colors",
1063
+ "hover:bg-muted disabled:opacity-40 disabled:hover:bg-background",
1064
+ mode === "smooth" ? "rounded-2xl" : "rounded-xl"
1065
+ );
1066
+ 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: [
1067
+ /* @__PURE__ */ jsx(SectionTitle, { title, description, badge }),
1068
+ /* @__PURE__ */ jsxs(FadeIn, { direction: "up", children: [
1069
+ /* @__PURE__ */ jsxs("div", { className: "mt-10 flex items-center justify-end gap-2", children: [
1070
+ /* @__PURE__ */ jsx(
1071
+ "button",
1072
+ {
1073
+ type: "button",
1074
+ "aria-label": "Previous",
1075
+ className: controlClasses,
1076
+ onClick: () => scrollByCard(-1),
1077
+ disabled: !canPrev,
1078
+ children: /* @__PURE__ */ jsx(ChevronLeft, { className: "size-4" })
1079
+ }
1080
+ ),
1081
+ /* @__PURE__ */ jsx(
1082
+ "button",
1083
+ {
1084
+ type: "button",
1085
+ "aria-label": "Next",
1086
+ className: controlClasses,
1087
+ onClick: () => scrollByCard(1),
1088
+ disabled: !canNext,
1089
+ children: /* @__PURE__ */ jsx(ChevronRight, { className: "size-4" })
1090
+ }
1091
+ )
1092
+ ] }),
1093
+ /* @__PURE__ */ jsx(
1094
+ "div",
1095
+ {
1096
+ ref: trackRef,
1097
+ className: "mt-4 flex snap-x snap-mandatory gap-6 overflow-x-auto pb-4 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden",
1098
+ children: features.map((feature) => /* @__PURE__ */ jsx(
1099
+ "div",
1100
+ {
1101
+ "data-carousel-item": true,
1102
+ className: "w-[280px] shrink-0 snap-start sm:w-[320px]",
1103
+ children: /* @__PURE__ */ jsx(
1104
+ FeatureCard,
1105
+ {
1106
+ icon: feature.icon,
1107
+ title: feature.title,
1108
+ description: feature.description,
1109
+ href: feature.href
1110
+ }
1111
+ )
1112
+ },
1113
+ feature.title
1114
+ ))
1115
+ }
1116
+ )
1117
+ ] })
1118
+ ] }) }) });
1119
+ }
988
1120
  var defaultRenderLink5 = ({ href, children, className }) => /* @__PURE__ */ jsx("a", { href, className, children });
989
1121
  function CtaSection({
990
1122
  heading,
@@ -994,10 +1126,17 @@ function CtaSection({
994
1126
  footnote,
995
1127
  renderLink = defaultRenderLink5
996
1128
  }) {
997
- return /* @__PURE__ */ jsx("section", { className: "overflow-hidden rounded-2xl border bg-card", children: /* @__PURE__ */ jsxs("div", { className: "relative px-6 py-16 sm:px-10 sm:py-24", children: [
1129
+ const mode = useUIVariant();
1130
+ const sectionClasses = cn$1(
1131
+ "overflow-hidden bg-card",
1132
+ mode === "smooth" && "rounded-3xl border",
1133
+ mode === "playful" && "rounded-2xl border-primary/25 border",
1134
+ !mode && "rounded-2xl border"
1135
+ );
1136
+ return /* @__PURE__ */ jsx("section", { className: sectionClasses, children: /* @__PURE__ */ jsxs("div", { className: "relative px-6 py-16 sm:px-10 sm:py-24", children: [
998
1137
  /* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-[radial-gradient(ellipse_at_center,hsl(var(--primary)/0.15),transparent_70%)]" }),
999
1138
  /* @__PURE__ */ jsx(FadeIn, { direction: "up", children: /* @__PURE__ */ jsxs("div", { className: "relative mx-auto max-w-2xl text-center", children: [
1000
- /* @__PURE__ */ jsx("div", { className: "mb-4 inline-flex h-10 w-10 items-center justify-center rounded-2xl bg-primary/10 text-primary", children: /* @__PURE__ */ jsx(Sparkles, { className: "size-5" }) }),
1139
+ /* @__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" }) }),
1001
1140
  /* @__PURE__ */ jsx("h2", { className: "text-balance text-3xl font-semibold tracking-tight sm:text-4xl", children: heading }),
1002
1141
  /* @__PURE__ */ jsx("p", { className: "mx-auto mt-4 max-w-lg text-pretty text-muted-foreground", children: description }),
1003
1142
  /* @__PURE__ */ jsxs("div", { className: "mt-8 flex flex-wrap items-center justify-center gap-3", children: [
@@ -1030,15 +1169,17 @@ function PricingCard({
1030
1169
  className,
1031
1170
  ...props
1032
1171
  }) {
1033
- return /* @__PURE__ */ jsxs(
1172
+ const mode = useUIVariant();
1173
+ const cardInner = /* @__PURE__ */ jsxs(
1034
1174
  "div",
1035
1175
  {
1036
1176
  className: cn$1(
1037
- "relative flex flex-col rounded-2xl border p-6",
1177
+ "relative flex flex-col border p-6",
1178
+ mode === "playful" ? "rounded-2xl rotate-[0.3deg]" : mode === "smooth" ? "rounded-3xl" : "rounded-2xl",
1038
1179
  highlighted ? "border-primary bg-primary/5 shadow-sm" : "bg-card",
1039
- className
1180
+ mode !== "playful" && className
1040
1181
  ),
1041
- ...props,
1182
+ ...mode !== "playful" ? props : {},
1042
1183
  children: [
1043
1184
  badge && /* @__PURE__ */ jsx("div", { className: "absolute -top-3 left-1/2 -translate-x-1/2", children: /* @__PURE__ */ jsx(Badge$1, { children: badge }) }),
1044
1185
  /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
@@ -1069,6 +1210,22 @@ function PricingCard({
1069
1210
  ]
1070
1211
  }
1071
1212
  );
1213
+ if (mode === "playful") {
1214
+ return /* @__PURE__ */ jsxs("div", { className: cn$1("group/playful relative", className), ...props, children: [
1215
+ /* @__PURE__ */ jsx(
1216
+ "div",
1217
+ {
1218
+ "aria-hidden": "true",
1219
+ className: cn$1(
1220
+ "absolute inset-0 rounded-2xl transition-transform duration-200 translate-x-[5px] translate-y-[5px] -rotate-[0.5deg] group-hover/playful:-rotate-[1.5deg] group-hover/playful:translate-x-[6px] group-hover/playful:translate-y-[6px]",
1221
+ highlighted ? "bg-primary/20" : "bg-border"
1222
+ )
1223
+ }
1224
+ ),
1225
+ cardInner
1226
+ ] });
1227
+ }
1228
+ return cardInner;
1072
1229
  }
1073
1230
  var defaultRenderLink6 = ({ href, children, className }) => /* @__PURE__ */ jsx("a", { href, className, children });
1074
1231
  function PricingSection({
@@ -1081,16 +1238,25 @@ function PricingSection({
1081
1238
  renderLink = defaultRenderLink6,
1082
1239
  footnote
1083
1240
  }) {
1084
- const [annual, setAnnual] = React25.useState(false);
1085
- 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: [
1241
+ const [annual, setAnnual] = React26.useState(false);
1242
+ const mode = useUIVariant();
1243
+ const sectionClasses = cn$1(
1244
+ "overflow-hidden bg-card",
1245
+ mode === "smooth" && "rounded-3xl border",
1246
+ mode === "playful" && "rounded-2xl border-primary/25 border",
1247
+ !mode && "rounded-2xl border"
1248
+ );
1249
+ 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: [
1086
1250
  /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
1087
1251
  badge && /* @__PURE__ */ jsx(Badge$1, { variant: "secondary", className: "mb-4", children: badge }),
1088
1252
  /* @__PURE__ */ jsx("h2", { className: "text-balance text-3xl font-semibold tracking-tight sm:text-4xl", children: title }),
1089
1253
  description && /* @__PURE__ */ jsx("p", { className: "mx-auto mt-4 max-w-xl text-pretty text-muted-foreground", children: description }),
1090
1254
  /* @__PURE__ */ jsxs("div", { className: "mt-6 inline-flex items-center gap-3 rounded-full border bg-muted p-1", children: [
1091
1255
  /* @__PURE__ */ jsx(
1092
- "button",
1256
+ Button$1,
1093
1257
  {
1258
+ variant: "ghost",
1259
+ size: "sm",
1094
1260
  onClick: () => setAnnual(false),
1095
1261
  className: cn$1(
1096
1262
  "rounded-full px-4 py-1.5 text-sm font-medium transition-colors",
@@ -1100,8 +1266,10 @@ function PricingSection({
1100
1266
  }
1101
1267
  ),
1102
1268
  /* @__PURE__ */ jsxs(
1103
- "button",
1269
+ Button$1,
1104
1270
  {
1271
+ variant: "ghost",
1272
+ size: "sm",
1105
1273
  onClick: () => setAnnual(true),
1106
1274
  className: cn$1(
1107
1275
  "flex items-center gap-2 rounded-full px-4 py-1.5 text-sm font-medium transition-colors",
@@ -1146,11 +1314,16 @@ function TestimonialCard({
1146
1314
  className,
1147
1315
  ...props
1148
1316
  }) {
1149
- return /* @__PURE__ */ jsxs(
1317
+ const mode = useUIVariant();
1318
+ const cardInner = /* @__PURE__ */ jsxs(
1150
1319
  "div",
1151
1320
  {
1152
- className: cn$1("flex flex-col gap-4 rounded-2xl border bg-card p-6", className),
1153
- ...props,
1321
+ className: cn$1(
1322
+ "flex flex-col gap-4 border bg-card p-6",
1323
+ mode === "playful" ? "rounded-2xl rotate-[0.3deg]" : mode === "smooth" ? "rounded-3xl" : "rounded-2xl",
1324
+ mode !== "playful" && className
1325
+ ),
1326
+ ...mode !== "playful" ? props : {},
1154
1327
  children: [
1155
1328
  /* @__PURE__ */ jsx(Quote, { className: "size-4 text-muted-foreground/40" }),
1156
1329
  rating != null && /* @__PURE__ */ jsx("div", { className: "flex gap-0.5", children: Array.from({ length: 5 }, (_, i) => /* @__PURE__ */ jsx(
@@ -1165,9 +1338,9 @@ function TestimonialCard({
1165
1338
  )) }),
1166
1339
  /* @__PURE__ */ jsx("p", { className: "flex-1 text-sm leading-relaxed", children: quote }),
1167
1340
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
1168
- /* @__PURE__ */ jsxs(Avatar, { className: "size-9 rounded-xl", children: [
1341
+ /* @__PURE__ */ jsxs(Avatar, { className: "size-9", children: [
1169
1342
  /* @__PURE__ */ jsx(AvatarImage, { src: avatar, alt: name }),
1170
- /* @__PURE__ */ jsx(AvatarFallback, { className: "rounded-xl text-xs", children: initials ?? name.slice(0, 2).toUpperCase() })
1343
+ /* @__PURE__ */ jsx(AvatarFallback, { className: "text-xs", children: initials ?? name.slice(0, 2).toUpperCase() })
1171
1344
  ] }),
1172
1345
  /* @__PURE__ */ jsxs("div", { children: [
1173
1346
  /* @__PURE__ */ jsx("div", { className: "text-sm font-medium leading-tight", children: name }),
@@ -1180,6 +1353,19 @@ function TestimonialCard({
1180
1353
  ]
1181
1354
  }
1182
1355
  );
1356
+ if (mode === "playful") {
1357
+ return /* @__PURE__ */ jsxs("div", { className: cn$1("group/playful relative", className), ...props, children: [
1358
+ /* @__PURE__ */ jsx(
1359
+ "div",
1360
+ {
1361
+ "aria-hidden": "true",
1362
+ className: "absolute inset-0 rounded-2xl bg-border transition-transform duration-200 translate-x-[5px] translate-y-[5px] -rotate-[0.5deg] group-hover/playful:-rotate-[1.5deg] group-hover/playful:translate-x-[6px] group-hover/playful:translate-y-[6px]"
1363
+ }
1364
+ ),
1365
+ cardInner
1366
+ ] });
1367
+ }
1368
+ return cardInner;
1183
1369
  }
1184
1370
  function TestimonialsSection({
1185
1371
  title = "Trusted by teams shipping real products",
@@ -1187,7 +1373,14 @@ function TestimonialsSection({
1187
1373
  badge = "Testimonials",
1188
1374
  testimonials
1189
1375
  }) {
1190
- 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: [
1376
+ const mode = useUIVariant();
1377
+ const sectionClasses = cn$1(
1378
+ "overflow-hidden bg-card",
1379
+ mode === "smooth" && "rounded-3xl border",
1380
+ mode === "playful" && "rounded-2xl border-primary/25 border",
1381
+ !mode && "rounded-2xl border"
1382
+ );
1383
+ 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: [
1191
1384
  /* @__PURE__ */ jsx(SectionTitle, { title, description, badge }),
1192
1385
  /* @__PURE__ */ jsx(StaggerChildren, { className: "mt-12 columns-1 gap-4 sm:columns-2 lg:columns-3", children: testimonials.map((t) => /* @__PURE__ */ jsx(
1193
1386
  "div",
@@ -1293,7 +1486,14 @@ function FaqSection({
1293
1486
  badge = "FAQ",
1294
1487
  items
1295
1488
  }) {
1296
- 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-3xl", children: [
1489
+ const mode = useUIVariant();
1490
+ const sectionClasses = cn$1(
1491
+ "overflow-hidden bg-card",
1492
+ mode === "smooth" && "rounded-3xl border",
1493
+ mode === "playful" && "rounded-2xl border-primary/25 border",
1494
+ !mode && "rounded-2xl border"
1495
+ );
1496
+ 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-3xl", children: [
1297
1497
  /* @__PURE__ */ jsx(SectionTitle, { title, description, badge }),
1298
1498
  /* @__PURE__ */ jsx(FadeIn, { direction: "up", children: /* @__PURE__ */ jsx(Accordion, { type: "single", collapsible: true, className: "mt-10 w-full", children: items.map((item, i) => /* @__PURE__ */ jsxs(AccordionItem, { value: `item-${i}`, children: [
1299
1499
  /* @__PURE__ */ jsx(AccordionTrigger, { className: "text-left text-sm font-medium", children: item.question }),
@@ -1312,10 +1512,10 @@ function CountUp({
1312
1512
  className,
1313
1513
  ...props
1314
1514
  }) {
1315
- const ref = React25.useRef(null);
1316
- const [value, setValue] = React25.useState(from);
1317
- const hasStarted = React25.useRef(false);
1318
- React25.useEffect(() => {
1515
+ const ref = React26.useRef(null);
1516
+ const [value, setValue] = React26.useState(from);
1517
+ const hasStarted = React26.useRef(false);
1518
+ React26.useEffect(() => {
1319
1519
  const el = ref.current;
1320
1520
  if (!el) return;
1321
1521
  const observer = new IntersectionObserver(
@@ -1363,7 +1563,14 @@ function StatsSection({
1363
1563
  badge = "By the numbers",
1364
1564
  stats
1365
1565
  }) {
1366
- 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: [
1566
+ const mode = useUIVariant();
1567
+ const sectionClasses = cn$1(
1568
+ "overflow-hidden bg-card",
1569
+ mode === "smooth" && "rounded-3xl border",
1570
+ mode === "playful" && "rounded-2xl border-primary/25 border",
1571
+ !mode && "rounded-2xl border"
1572
+ );
1573
+ return /* @__PURE__ */ jsx("section", { className: sectionClasses, children: /* @__PURE__ */ jsxs("div", { className: "relative px-6 py-14 sm:px-10 sm:py-20", children: [
1367
1574
  /* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-[radial-gradient(ellipse_at_bottom,hsl(var(--primary)/0.08),transparent_60%)]" }),
1368
1575
  /* @__PURE__ */ jsxs("div", { className: "relative mx-auto max-w-4xl", children: [
1369
1576
  /* @__PURE__ */ jsx(SectionTitle, { title, description, badge }),
@@ -1401,8 +1608,8 @@ function NewsletterSection({
1401
1608
  successDescription = defaults.successDescription,
1402
1609
  onSubscribe
1403
1610
  } = {}) {
1404
- const [email, setEmail] = React25.useState("");
1405
- const [submitted, setSubmitted] = React25.useState(false);
1611
+ const [email, setEmail] = React26.useState("");
1612
+ const [submitted, setSubmitted] = React26.useState(false);
1406
1613
  async function handleSubmit(e) {
1407
1614
  e.preventDefault();
1408
1615
  if (!email) return;
@@ -1480,7 +1687,7 @@ function ContactSection({
1480
1687
  sendAnotherLabel = defaults2.sendAnotherLabel,
1481
1688
  onSubmit
1482
1689
  } = {}) {
1483
- const [submitted, setSubmitted] = React25.useState(false);
1690
+ const [submitted, setSubmitted] = React26.useState(false);
1484
1691
  async function handleSubmit(e) {
1485
1692
  e.preventDefault();
1486
1693
  await onSubmit?.(e);
@@ -1569,38 +1776,17 @@ function ContactSection({
1569
1776
  ] }) })
1570
1777
  ] }) });
1571
1778
  }
1572
- function ThemeSwitchMinimal() {
1573
- const [theme, setThemeState] = React25.useState("dark");
1574
- React25.useEffect(() => {
1575
- const stored = localStorage.getItem("theme");
1576
- const resolved = stored ?? (document.documentElement.classList.contains("dark") ? "dark" : "light");
1577
- setThemeState(resolved);
1578
- }, []);
1579
- const toggle = () => {
1580
- const next = theme === "dark" ? "light" : "dark";
1581
- setThemeState(next);
1582
- localStorage.setItem("theme", next);
1583
- document.documentElement.classList.toggle("dark", next === "dark");
1584
- };
1585
- return /* @__PURE__ */ jsx(Button$1, { variant: "ghost", size: "icon", onClick: toggle, className: "size-8", "aria-label": "Toggle theme", children: /* @__PURE__ */ jsx(
1586
- "span",
1587
- {
1588
- className: "inline-flex size-4 items-center justify-center animate-in fade-in zoom-in-75 spin-in-90 duration-200 motion-reduce:animate-none",
1589
- children: theme === "dark" ? /* @__PURE__ */ jsx(Sun, { className: "size-4" }) : /* @__PURE__ */ jsx(Moon, { className: "size-4" })
1590
- },
1591
- theme
1592
- ) });
1593
- }
1594
1779
  var defaultRenderLink7 = ({ href, children, className }) => /* @__PURE__ */ jsx("a", { href, className, children });
1595
1780
  function Navbar({
1596
1781
  brand,
1597
1782
  navLinks,
1598
1783
  cta,
1599
- showThemeToggle = false,
1784
+ controls,
1600
1785
  renderLink = defaultRenderLink7
1601
1786
  }) {
1602
- const [open, setOpen] = React25.useState(false);
1787
+ const [open, setOpen] = React26.useState(false);
1603
1788
  const brandHref = brand.href ?? "/";
1789
+ const hasRightContent = controls?.length || cta?.primary || cta?.secondary;
1604
1790
  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: [
1605
1791
  renderLink({
1606
1792
  href: brandHref,
@@ -1609,12 +1795,14 @@ function Navbar({
1609
1795
  /* @__PURE__ */ jsx("span", { children: brand.name })
1610
1796
  ] })
1611
1797
  }),
1612
- /* @__PURE__ */ jsx("div", { className: "hidden items-center gap-1 md:flex", children: navLinks.map((link) => /* @__PURE__ */ jsx("span", { children: renderLink({
1798
+ /* @__PURE__ */ jsx("div", { className: `hidden items-center gap-1 md:flex ${!hasRightContent ? "col-span-2 justify-end" : ""}`, children: navLinks.map((link) => /* @__PURE__ */ jsx("span", { children: renderLink({
1613
1799
  href: link.href,
1614
1800
  className: "rounded-md px-3 py-1.5 text-sm text-muted-foreground transition-colors hover:bg-accent hover:text-accent-foreground",
1615
1801
  children: link.label
1616
1802
  }) }, link.label)) }),
1617
- /* @__PURE__ */ jsxs("div", { className: "hidden items-center justify-end gap-3 md:flex", children: [
1803
+ hasRightContent && /* @__PURE__ */ jsxs("div", { className: "hidden items-center justify-end gap-2 md:flex", children: [
1804
+ controls?.map((control, i) => /* @__PURE__ */ jsx(React26.Fragment, { children: control }, i)),
1805
+ (cta?.secondary || cta?.primary) && controls?.length ? /* @__PURE__ */ jsx("div", { className: "mx-1 h-4 w-px bg-border" }) : null,
1618
1806
  cta?.secondary && renderLink({
1619
1807
  href: cta.secondary.href,
1620
1808
  children: /* @__PURE__ */ jsx(Button$1, { variant: "ghost", size: "sm", children: cta.secondary.label })
@@ -1622,8 +1810,7 @@ function Navbar({
1622
1810
  cta?.primary && renderLink({
1623
1811
  href: cta.primary.href,
1624
1812
  children: /* @__PURE__ */ jsx(Button$1, { size: "sm", children: cta.primary.label })
1625
- }),
1626
- showThemeToggle && /* @__PURE__ */ jsx(ThemeSwitchMinimal, {})
1813
+ })
1627
1814
  ] }),
1628
1815
  /* @__PURE__ */ jsxs(Sheet, { open, onOpenChange: setOpen, children: [
1629
1816
  /* @__PURE__ */ jsx(SheetTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(Button$1, { variant: "ghost", size: "icon", className: "justify-self-end md:hidden", children: [
@@ -1640,7 +1827,7 @@ function Navbar({
1640
1827
  ] })
1641
1828
  }) }),
1642
1829
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
1643
- showThemeToggle && /* @__PURE__ */ jsx(ThemeSwitchMinimal, {}),
1830
+ controls?.map((control, i) => /* @__PURE__ */ jsx(React26.Fragment, { children: control }, i)),
1644
1831
  /* @__PURE__ */ jsxs(Button$1, { variant: "ghost", size: "icon", onClick: () => setOpen(false), children: [
1645
1832
  /* @__PURE__ */ jsx(X, { className: "size-4" }),
1646
1833
  /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Close menu" })
@@ -1785,7 +1972,7 @@ function LogoStrip({
1785
1972
  ] });
1786
1973
  }
1787
1974
  function GridUnderlay({ variant, size = 24, className, ...props }) {
1788
- const id = React25.useId();
1975
+ const id = React26.useId();
1789
1976
  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: [
1790
1977
  /* @__PURE__ */ jsx("line", { x1: size / 2, y1: 0, x2: size / 2, y2: size, stroke: "currentColor", strokeWidth: 0.5 }),
1791
1978
  /* @__PURE__ */ jsx("line", { x1: 0, y1: size / 2, x2: size, y2: size / 2, stroke: "currentColor", strokeWidth: 0.5 })
@@ -1813,9 +2000,9 @@ function GlowUnderlay({
1813
2000
  style,
1814
2001
  ...props
1815
2002
  }) {
1816
- const [pos, setPos] = React25.useState({ x: "50%", y: "50%" });
1817
- const containerRef = React25.useRef(null);
1818
- React25.useEffect(() => {
2003
+ const [pos, setPos] = React26.useState({ x: "50%", y: "50%" });
2004
+ const containerRef = React26.useRef(null);
2005
+ React26.useEffect(() => {
1819
2006
  if (!followCursor) return;
1820
2007
  const handleMouseMove = (e) => {
1821
2008
  const el = containerRef.current;
@@ -1907,8 +2094,8 @@ function PageTransition({
1907
2094
  style,
1908
2095
  ...props
1909
2096
  }) {
1910
- const [mounted, setMounted] = React25.useState(false);
1911
- React25.useEffect(() => {
2097
+ const [mounted, setMounted] = React26.useState(false);
2098
+ React26.useEffect(() => {
1912
2099
  const id = requestAnimationFrame(() => setMounted(true));
1913
2100
  return () => cancelAnimationFrame(id);
1914
2101
  }, []);
@@ -1935,8 +2122,8 @@ function Spotlight({
1935
2122
  triggerClassName,
1936
2123
  shortcut = "\u2318K"
1937
2124
  }) {
1938
- const [open, setOpen] = React25.useState(false);
1939
- React25.useEffect(() => {
2125
+ const [open, setOpen] = React26.useState(false);
2126
+ React26.useEffect(() => {
1940
2127
  const down = (e) => {
1941
2128
  if (e.key === "k" && (e.metaKey || e.ctrlKey)) {
1942
2129
  e.preventDefault();
@@ -1966,7 +2153,7 @@ function Spotlight({
1966
2153
  /* @__PURE__ */ jsx(CommandInput, { placeholder }),
1967
2154
  /* @__PURE__ */ jsxs(CommandList, { children: [
1968
2155
  /* @__PURE__ */ jsx(CommandEmpty, { children: "No results found." }),
1969
- groups.map((group, i) => /* @__PURE__ */ jsxs(React25.Fragment, { children: [
2156
+ groups.map((group, i) => /* @__PURE__ */ jsxs(React26.Fragment, { children: [
1970
2157
  i > 0 && /* @__PURE__ */ jsx(CommandSeparator, {}),
1971
2158
  /* @__PURE__ */ jsx(CommandGroup, { heading: group.heading, children: group.items.map((item) => /* @__PURE__ */ jsxs(
1972
2159
  CommandItem,
@@ -1991,8 +2178,8 @@ function Spotlight({
1991
2178
  var BASE = 40;
1992
2179
  var MAGNIFY_RADIUS = 80;
1993
2180
  function Dock({ items, iconSize = BASE, magnification = 1.7, className, ...props }) {
1994
- const [mouseX, setMouseX] = React25.useState(null);
1995
- const containerRef = React25.useRef(null);
2181
+ const [mouseX, setMouseX] = React26.useState(null);
2182
+ const containerRef = React26.useRef(null);
1996
2183
  function getScale(index) {
1997
2184
  if (mouseX === null || !containerRef.current) return 1;
1998
2185
  const container = containerRef.current;
@@ -2217,7 +2404,7 @@ function PageHeader({
2217
2404
  return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col gap-1 pb-6", className), children: [
2218
2405
  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) => {
2219
2406
  const isLast = i === breadcrumbs.length - 1;
2220
- return /* @__PURE__ */ jsxs(React25.Fragment, { children: [
2407
+ return /* @__PURE__ */ jsxs(React26.Fragment, { children: [
2221
2408
  i > 0 && /* @__PURE__ */ jsx(ChevronRight, { className: "size-3.5 shrink-0" }),
2222
2409
  isLast || !crumb.href ? /* @__PURE__ */ jsx("span", { className: cn(isLast && "text-foreground font-medium"), children: crumb.label }) : renderLink({
2223
2410
  href: crumb.href,
@@ -2253,10 +2440,58 @@ function PageHeader({
2253
2440
  ] });
2254
2441
  }
2255
2442
  function Suspensed({ children, fallback }) {
2256
- return /* @__PURE__ */ jsx(React25.Suspense, { fallback: fallback ?? /* @__PURE__ */ jsx(Spinner, {}), children });
2443
+ return /* @__PURE__ */ jsx(React26.Suspense, { fallback: fallback ?? /* @__PURE__ */ jsx(Spinner, {}), children });
2444
+ }
2445
+ function ThemeSwitchMinimal() {
2446
+ const [theme, setThemeState] = React26.useState("dark");
2447
+ React26.useEffect(() => {
2448
+ const stored = localStorage.getItem("theme");
2449
+ const resolved = stored ?? (document.documentElement.classList.contains("dark") ? "dark" : "light");
2450
+ setThemeState(resolved);
2451
+ }, []);
2452
+ const toggle = () => {
2453
+ const next = theme === "dark" ? "light" : "dark";
2454
+ setThemeState(next);
2455
+ localStorage.setItem("theme", next);
2456
+ document.documentElement.classList.toggle("dark", next === "dark");
2457
+ };
2458
+ return /* @__PURE__ */ jsx(Button$1, { variant: "ghost", size: "icon", onClick: toggle, className: "size-8", "aria-label": "Toggle theme", children: /* @__PURE__ */ jsx(
2459
+ "span",
2460
+ {
2461
+ className: "inline-flex size-4 items-center justify-center animate-in fade-in zoom-in-75 spin-in-90 duration-200 motion-reduce:animate-none",
2462
+ children: theme === "dark" ? /* @__PURE__ */ jsx(Sun, { className: "size-4" }) : /* @__PURE__ */ jsx(Moon, { className: "size-4" })
2463
+ },
2464
+ theme
2465
+ ) });
2466
+ }
2467
+ var MODES = ["default", "playful", "smooth"];
2468
+ var MODE_ICONS = {
2469
+ default: /* @__PURE__ */ jsx(LayoutGrid, { className: "size-4" }),
2470
+ playful: /* @__PURE__ */ jsx(Sparkles, { className: "size-4" }),
2471
+ smooth: /* @__PURE__ */ jsx(Layers, { className: "size-4" })
2472
+ };
2473
+ var MODE_LABELS = {
2474
+ default: "Default mode",
2475
+ playful: "Playful mode",
2476
+ smooth: "Smooth mode"
2477
+ };
2478
+ function ModeSwitchMinimal() {
2479
+ const { mode, setMode } = useOlwibaUI();
2480
+ const cycle = () => {
2481
+ const idx = MODES.indexOf(mode);
2482
+ setMode(MODES[(idx + 1) % MODES.length]);
2483
+ };
2484
+ return /* @__PURE__ */ jsx(Button$1, { variant: "ghost", size: "icon", onClick: cycle, className: "size-8", "aria-label": MODE_LABELS[mode], children: /* @__PURE__ */ jsx(
2485
+ "span",
2486
+ {
2487
+ className: "inline-flex size-4 items-center justify-center animate-in fade-in zoom-in-75 duration-200 motion-reduce:animate-none",
2488
+ children: MODE_ICONS[mode]
2489
+ },
2490
+ mode
2491
+ ) });
2257
2492
  }
2258
2493
  function ThemeColorUpdater({ colorTheme }) {
2259
- React25.useEffect(() => {
2494
+ React26.useEffect(() => {
2260
2495
  if (colorTheme) {
2261
2496
  document.documentElement.setAttribute("data-theme", colorTheme);
2262
2497
  } else {
@@ -2266,7 +2501,7 @@ function ThemeColorUpdater({ colorTheme }) {
2266
2501
  return null;
2267
2502
  }
2268
2503
  function VersionBanner({ version, message, className, onDismiss }) {
2269
- const [dismissed, setDismissed] = React25.useState(false);
2504
+ const [dismissed, setDismissed] = React26.useState(false);
2270
2505
  if (dismissed) return null;
2271
2506
  return /* @__PURE__ */ jsxs("div", { className: cn("flex items-center justify-between gap-4 bg-primary/10 px-4 py-2 text-sm", className), children: [
2272
2507
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
@@ -2306,7 +2541,7 @@ function DevBanner({ environment = "development", className }) {
2306
2541
  );
2307
2542
  }
2308
2543
  function RegisterHotkeys({ hotkeys }) {
2309
- React25.useEffect(() => {
2544
+ React26.useEffect(() => {
2310
2545
  const handleKeyDown = (event) => {
2311
2546
  for (const hotkey of hotkeys) {
2312
2547
  const matches = event.key.toLowerCase() === hotkey.key.toLowerCase() && !!event.ctrlKey === !!hotkey.ctrl && !!event.metaKey === !!hotkey.meta && !!event.shiftKey === !!hotkey.shift && !!event.altKey === !!hotkey.alt;
@@ -2426,36 +2661,36 @@ function useMounted() {
2426
2661
  return mounted;
2427
2662
  }
2428
2663
  function useConfirm() {
2429
- const [isOpen, setIsOpen] = React25.useState(false);
2430
- const [options, setOptions] = React25.useState({});
2431
- const resolveRef = React25.useRef(null);
2432
- const confirm = React25.useCallback((opts = {}) => {
2664
+ const [isOpen, setIsOpen] = React26.useState(false);
2665
+ const [options, setOptions] = React26.useState({});
2666
+ const resolveRef = React26.useRef(null);
2667
+ const confirm = React26.useCallback((opts = {}) => {
2433
2668
  setOptions(opts);
2434
2669
  setIsOpen(true);
2435
2670
  return new Promise((resolve) => {
2436
2671
  resolveRef.current = resolve;
2437
2672
  });
2438
2673
  }, []);
2439
- const handleConfirm = React25.useCallback(() => {
2674
+ const handleConfirm = React26.useCallback(() => {
2440
2675
  resolveRef.current?.(true);
2441
2676
  setIsOpen(false);
2442
2677
  }, []);
2443
- const handleCancel = React25.useCallback(() => {
2678
+ const handleCancel = React26.useCallback(() => {
2444
2679
  resolveRef.current?.(false);
2445
2680
  setIsOpen(false);
2446
2681
  }, []);
2447
2682
  return { confirm, isOpen, options, handleConfirm, handleCancel };
2448
2683
  }
2449
2684
  function useControlledOpen(defaultOpen = false) {
2450
- const [isOpen, setIsOpen] = React25.useState(defaultOpen);
2451
- const open = React25.useCallback(() => setIsOpen(true), []);
2452
- const close = React25.useCallback(() => setIsOpen(false), []);
2453
- const toggle = React25.useCallback(() => setIsOpen((prev) => !prev), []);
2685
+ const [isOpen, setIsOpen] = React26.useState(defaultOpen);
2686
+ const open = React26.useCallback(() => setIsOpen(true), []);
2687
+ const close = React26.useCallback(() => setIsOpen(false), []);
2688
+ const toggle = React26.useCallback(() => setIsOpen((prev) => !prev), []);
2454
2689
  return { isOpen, open, close, toggle, setIsOpen };
2455
2690
  }
2456
2691
  function useScrolledPast(threshold) {
2457
- const [scrolledPast, setScrolledPast] = React25.useState(false);
2458
- React25.useEffect(() => {
2692
+ const [scrolledPast, setScrolledPast] = React26.useState(false);
2693
+ React26.useEffect(() => {
2459
2694
  const handleScroll = () => {
2460
2695
  setScrolledPast(window.scrollY > threshold);
2461
2696
  };
@@ -2466,30 +2701,30 @@ function useScrolledPast(threshold) {
2466
2701
  return scrolledPast;
2467
2702
  }
2468
2703
  function useCopyToClipboard() {
2469
- const [copied, setCopied] = React25.useState(false);
2470
- const timeoutRef = React25.useRef(void 0);
2471
- const copy = React25.useCallback((text) => {
2704
+ const [copied, setCopied] = React26.useState(false);
2705
+ const timeoutRef = React26.useRef(void 0);
2706
+ const copy = React26.useCallback((text) => {
2472
2707
  navigator.clipboard.writeText(text).then(() => {
2473
2708
  setCopied(true);
2474
2709
  clearTimeout(timeoutRef.current);
2475
2710
  timeoutRef.current = setTimeout(() => setCopied(false), 2e3);
2476
2711
  });
2477
2712
  }, []);
2478
- React25.useEffect(() => () => clearTimeout(timeoutRef.current), []);
2713
+ React26.useEffect(() => () => clearTimeout(timeoutRef.current), []);
2479
2714
  return [copied, copy];
2480
2715
  }
2481
2716
  function useDebounce(value, delay) {
2482
- const [debounced, setDebounced] = React25.useState(value);
2483
- React25.useEffect(() => {
2717
+ const [debounced, setDebounced] = React26.useState(value);
2718
+ React26.useEffect(() => {
2484
2719
  const timer = setTimeout(() => setDebounced(value), delay);
2485
2720
  return () => clearTimeout(timer);
2486
2721
  }, [value, delay]);
2487
2722
  return debounced;
2488
2723
  }
2489
2724
  function useIntersectionObserver(options) {
2490
- const ref = React25.useRef(null);
2491
- const [isIntersecting, setIsIntersecting] = React25.useState(false);
2492
- React25.useEffect(() => {
2725
+ const ref = React26.useRef(null);
2726
+ const [isIntersecting, setIsIntersecting] = React26.useState(false);
2727
+ React26.useEffect(() => {
2493
2728
  const el = ref.current;
2494
2729
  if (!el) return;
2495
2730
  const observer = new IntersectionObserver(([entry]) => {
@@ -2501,7 +2736,7 @@ function useIntersectionObserver(options) {
2501
2736
  return [ref, isIntersecting];
2502
2737
  }
2503
2738
  function useLocalStorage(key, initialValue) {
2504
- const [storedValue, setStoredValue] = React25.useState(() => {
2739
+ const [storedValue, setStoredValue] = React26.useState(() => {
2505
2740
  if (typeof window === "undefined") return initialValue;
2506
2741
  try {
2507
2742
  const item = window.localStorage.getItem(key);
@@ -2510,7 +2745,7 @@ function useLocalStorage(key, initialValue) {
2510
2745
  return initialValue;
2511
2746
  }
2512
2747
  });
2513
- const setValue = React25.useCallback(
2748
+ const setValue = React26.useCallback(
2514
2749
  (value) => {
2515
2750
  try {
2516
2751
  setStoredValue(value);
@@ -2525,11 +2760,11 @@ function useLocalStorage(key, initialValue) {
2525
2760
  return [storedValue, setValue];
2526
2761
  }
2527
2762
  function useMediaQuery(query) {
2528
- const [matches, setMatches] = React25.useState(() => {
2763
+ const [matches, setMatches] = React26.useState(() => {
2529
2764
  if (typeof window === "undefined") return false;
2530
2765
  return window.matchMedia(query).matches;
2531
2766
  });
2532
- React25.useEffect(() => {
2767
+ React26.useEffect(() => {
2533
2768
  if (typeof window === "undefined") return;
2534
2769
  const mql = window.matchMedia(query);
2535
2770
  setMatches(mql.matches);
@@ -2540,18 +2775,18 @@ function useMediaQuery(query) {
2540
2775
  return matches;
2541
2776
  }
2542
2777
  function usePagination(total, pageSize) {
2543
- const [page, setPage] = React25.useState(1);
2778
+ const [page, setPage] = React26.useState(1);
2544
2779
  const totalPages = Math.max(1, Math.ceil(total / pageSize));
2545
2780
  const offset = (page - 1) * pageSize;
2546
- const goTo = React25.useCallback(
2781
+ const goTo = React26.useCallback(
2547
2782
  (p) => setPage(Math.min(Math.max(1, p), totalPages)),
2548
2783
  [totalPages]
2549
2784
  );
2550
- const next = React25.useCallback(() => goTo(page + 1), [page, goTo]);
2551
- const prev = React25.useCallback(() => goTo(page - 1), [page, goTo]);
2785
+ const next = React26.useCallback(() => goTo(page + 1), [page, goTo]);
2786
+ const prev = React26.useCallback(() => goTo(page - 1), [page, goTo]);
2552
2787
  return { page, pageSize, totalPages, offset, hasPrev: page > 1, hasNext: page < totalPages, goTo, next, prev };
2553
2788
  }
2554
2789
 
2555
- export { AppContent, AppGrid, AppGridCell, AppScreen, AppShell, AuthSection, Badge, Button, Card, ChangelogCard, ChangelogList, Checkbox, ConfirmDialog, ContactSection, ContextMenu, CountUp, CtaSection, DevBanner, Dock, EmptyState, ErrorPage, FadeIn, FaqSection, FeatureCard, FeaturesSection, Footer, FullPageSpinner, GlassCard, Grid, GridItem, HeroSection, ImageCard, Input, LogoStrip, Navbar, NewsletterSection, OlwibaUIProvider, Overlay, PageHeader, PageTransition, PhoneFrame, PostCard, PostList, PricingCard, PricingSection, PublicPageFrame, RegisterHotkeys, RootErrorFallback, Section, SectionTitle, Spotlight, Stack, StaggerChildren, StatCard, StatsSection, Suspensed, Switch, TeamSection, TestimonialCard, TestimonialsSection, Textarea, ThemeColorUpdater, ThemeSwitchMinimal, Underlay, UpgradePrompt, VersionBanner, useConfirm, useControlledOpen, useCopyToClipboard, useDebounce, useIntersectionObserver, useLocalStorage, useMediaQuery, useMounted, useOlwibaUI, usePagination, useScrolledPast, useUIMode };
2790
+ export { AppContent, AppGrid, AppGridCell, AppScreen, AppShell, AuthSection, Badge, Button, Card, CarouselSection, ChangelogCard, ChangelogList, Checkbox, ConfirmDialog, ContactSection, ContextMenu, CountUp, CtaSection, DevBanner, Dock, EmptyState, ErrorPage, FadeIn, FaqSection, FeatureCard, FeaturesSection, Footer, FullPageSpinner, GlassCard, Grid, GridItem, HeroSection, ImageCard, Input, LogoStrip, ModeSwitchMinimal, Navbar, NewsletterSection, OlwibaUIProvider, Overlay, PageHeader, PageTransition, PhoneFrame, PostCard, PostList, PricingCard, PricingSection, PublicPageFrame, RegisterHotkeys, RootErrorFallback, Section, SectionTitle, Spotlight, Stack, StaggerChildren, StatCard, StatsSection, Suspensed, Switch, TeamSection, TestimonialCard, TestimonialsSection, Textarea, ThemeColorUpdater, ThemeSwitchMinimal, Underlay, UpgradePrompt, VersionBanner, useConfirm, useControlledOpen, useCopyToClipboard, useDebounce, useIntersectionObserver, useLocalStorage, useMediaQuery, useMounted, useOlwibaUI, usePagination, useScrolledPast, useUIMode };
2556
2791
  //# sourceMappingURL=index.js.map
2557
2792
  //# sourceMappingURL=index.js.map