@bearmenu/ui 0.1.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.
Files changed (207) hide show
  1. package/dist/chunk-2CUFS5LF.js +80 -0
  2. package/dist/chunk-3GWWZKX7.js +33 -0
  3. package/dist/chunk-3VMDCCCX.js +10 -0
  4. package/dist/chunk-4AVS6CV4.js +89 -0
  5. package/dist/chunk-4JOV7FXW.js +17 -0
  6. package/dist/chunk-4KOMHDZD.js +89 -0
  7. package/dist/chunk-4UO73SKC.js +26 -0
  8. package/dist/chunk-7H54PTVH.js +116 -0
  9. package/dist/chunk-CW24PLDW.js +24 -0
  10. package/dist/chunk-EBAMCZDW.js +112 -0
  11. package/dist/chunk-ERMIA7NP.js +106 -0
  12. package/dist/chunk-ESO2EM3L.js +46 -0
  13. package/dist/chunk-EUHEIWHZ.js +32 -0
  14. package/dist/chunk-FEK5XGZW.js +8 -0
  15. package/dist/chunk-NKHH23YK.js +31 -0
  16. package/dist/chunk-R47NEOSI.js +78 -0
  17. package/dist/chunk-UXAYXRFN.js +69 -0
  18. package/dist/chunk-X6GATUI2.js +19 -0
  19. package/dist/components/accordion.d.ts +9 -0
  20. package/dist/components/accordion.js +53 -0
  21. package/dist/components/alert-dialog.d.ts +23 -0
  22. package/dist/components/alert-dialog.js +98 -0
  23. package/dist/components/alert.d.ts +11 -0
  24. package/dist/components/alert.js +52 -0
  25. package/dist/components/aspect-ratio.d.ts +6 -0
  26. package/dist/components/aspect-ratio.js +2 -0
  27. package/dist/components/avatar.d.ts +8 -0
  28. package/dist/components/avatar.js +47 -0
  29. package/dist/components/badge.d.ts +13 -0
  30. package/dist/components/badge.js +3 -0
  31. package/dist/components/breadcrumb.d.ts +14 -0
  32. package/dist/components/breadcrumb.js +96 -0
  33. package/dist/components/button.d.ts +16 -0
  34. package/dist/components/button.js +4 -0
  35. package/dist/components/calendar.d.ts +11 -0
  36. package/dist/components/calendar.js +56 -0
  37. package/dist/components/card.d.ts +13 -0
  38. package/dist/components/card.js +4 -0
  39. package/dist/components/carousel.d.ts +24 -0
  40. package/dist/components/carousel.js +198 -0
  41. package/dist/components/checkbox.d.ts +6 -0
  42. package/dist/components/checkbox.js +25 -0
  43. package/dist/components/collapsible.d.ts +8 -0
  44. package/dist/components/collapsible.js +26 -0
  45. package/dist/components/command.d.ts +84 -0
  46. package/dist/components/command.js +4 -0
  47. package/dist/components/currency-input.d.ts +17 -0
  48. package/dist/components/currency-input.js +121 -0
  49. package/dist/components/dialog.d.ts +22 -0
  50. package/dist/components/dialog.js +3 -0
  51. package/dist/components/drawer.d.ts +27 -0
  52. package/dist/components/drawer.js +85 -0
  53. package/dist/components/dropdown-menu.d.ts +28 -0
  54. package/dist/components/dropdown-menu.js +211 -0
  55. package/dist/components/empty-state.d.ts +22 -0
  56. package/dist/components/empty-state.js +81 -0
  57. package/dist/components/form.d.ts +28 -0
  58. package/dist/components/form.js +97 -0
  59. package/dist/components/haptic-button.d.ts +12 -0
  60. package/dist/components/haptic-button.js +17 -0
  61. package/dist/components/input-otp.d.ts +14 -0
  62. package/dist/components/input-otp.js +62 -0
  63. package/dist/components/input.d.ts +6 -0
  64. package/dist/components/input.js +3 -0
  65. package/dist/components/label.d.ts +8 -0
  66. package/dist/components/label.js +3 -0
  67. package/dist/components/lazy-markdown.d.ts +11 -0
  68. package/dist/components/lazy-markdown.js +13 -0
  69. package/dist/components/link-button.d.ts +30 -0
  70. package/dist/components/link-button.js +17 -0
  71. package/dist/components/markdown-renderer.d.ts +11 -0
  72. package/dist/components/markdown-renderer.js +10 -0
  73. package/dist/components/multi-select-combobox.d.ts +23 -0
  74. package/dist/components/multi-select-combobox.js +114 -0
  75. package/dist/components/pagination.d.ts +18 -0
  76. package/dist/components/pagination.js +101 -0
  77. package/dist/components/popover.d.ts +8 -0
  78. package/dist/components/popover.js +3 -0
  79. package/dist/components/progress.d.ts +6 -0
  80. package/dist/components/progress.js +30 -0
  81. package/dist/components/radio-group.d.ts +7 -0
  82. package/dist/components/radio-group.js +30 -0
  83. package/dist/components/rating.d.ts +15 -0
  84. package/dist/components/rating.js +120 -0
  85. package/dist/components/scroll-area.d.ts +7 -0
  86. package/dist/components/scroll-area.js +44 -0
  87. package/dist/components/search-bar.d.ts +17 -0
  88. package/dist/components/search-bar.js +92 -0
  89. package/dist/components/searchable-select.d.ts +22 -0
  90. package/dist/components/searchable-select.js +104 -0
  91. package/dist/components/select.d.ts +15 -0
  92. package/dist/components/select.js +129 -0
  93. package/dist/components/separator.d.ts +6 -0
  94. package/dist/components/separator.js +25 -0
  95. package/dist/components/sheet.d.ts +29 -0
  96. package/dist/components/sheet.js +96 -0
  97. package/dist/components/skeleton-card.d.ts +13 -0
  98. package/dist/components/skeleton-card.js +154 -0
  99. package/dist/components/skeleton.d.ts +6 -0
  100. package/dist/components/skeleton.js +3 -0
  101. package/dist/components/slider.d.ts +6 -0
  102. package/dist/components/slider.js +24 -0
  103. package/dist/components/sliding-panels.d.ts +19 -0
  104. package/dist/components/sliding-panels.js +75 -0
  105. package/dist/components/sonner.d.ts +6 -0
  106. package/dist/components/sonner.js +23 -0
  107. package/dist/components/sticky-container.d.ts +44 -0
  108. package/dist/components/sticky-container.js +39 -0
  109. package/dist/components/switch.d.ts +6 -0
  110. package/dist/components/switch.js +31 -0
  111. package/dist/components/table.d.ts +12 -0
  112. package/dist/components/table.js +81 -0
  113. package/dist/components/tabs.d.ts +12 -0
  114. package/dist/components/tabs.js +55 -0
  115. package/dist/components/text.d.ts +31 -0
  116. package/dist/components/text.js +3 -0
  117. package/dist/components/textarea.d.ts +6 -0
  118. package/dist/components/textarea.js +23 -0
  119. package/dist/components/toast.d.ts +18 -0
  120. package/dist/components/toast.js +3 -0
  121. package/dist/components/toaster.d.ts +5 -0
  122. package/dist/components/toaster.js +25 -0
  123. package/dist/components/toggle-group.d.ts +12 -0
  124. package/dist/components/toggle-group.js +72 -0
  125. package/dist/components/toggle.d.ts +13 -0
  126. package/dist/components/toggle.js +3 -0
  127. package/dist/components/tooltip.d.ts +9 -0
  128. package/dist/components/tooltip.js +26 -0
  129. package/dist/components/use-mobile.d.ts +3 -0
  130. package/dist/components/use-mobile.js +19 -0
  131. package/dist/components/use-toast.d.ts +42 -0
  132. package/dist/components/use-toast.js +2 -0
  133. package/dist/hooks/use-haptic.d.ts +22 -0
  134. package/dist/hooks/use-haptic.js +2 -0
  135. package/dist/hooks/use-mobile.d.ts +3 -0
  136. package/dist/hooks/use-mobile.js +19 -0
  137. package/dist/hooks/use-mounted-theme.d.ts +6 -0
  138. package/dist/hooks/use-mounted-theme.js +18 -0
  139. package/dist/hooks/use-sticky.d.ts +18 -0
  140. package/dist/hooks/use-sticky.js +2 -0
  141. package/dist/lib/utils.d.ts +5 -0
  142. package/dist/lib/utils.js +2 -0
  143. package/package.json +203 -0
  144. package/src/components/accordion.tsx +57 -0
  145. package/src/components/alert-dialog.tsx +116 -0
  146. package/src/components/alert.tsx +57 -0
  147. package/src/components/aspect-ratio.tsx +9 -0
  148. package/src/components/avatar.tsx +49 -0
  149. package/src/components/badge.tsx +38 -0
  150. package/src/components/breadcrumb.tsx +102 -0
  151. package/src/components/button.tsx +94 -0
  152. package/src/components/calendar.tsx +60 -0
  153. package/src/components/card.tsx +67 -0
  154. package/src/components/carousel.tsx +230 -0
  155. package/src/components/checkbox.tsx +27 -0
  156. package/src/components/collapsible.tsx +28 -0
  157. package/src/components/command.tsx +148 -0
  158. package/src/components/currency-input.tsx +158 -0
  159. package/src/components/dialog.tsx +103 -0
  160. package/src/components/drawer.tsx +99 -0
  161. package/src/components/dropdown-menu.tsx +228 -0
  162. package/src/components/empty-state.tsx +118 -0
  163. package/src/components/form.tsx +152 -0
  164. package/src/components/haptic-button.tsx +19 -0
  165. package/src/components/input-otp.tsx +70 -0
  166. package/src/components/input.tsx +23 -0
  167. package/src/components/label.tsx +20 -0
  168. package/src/components/lazy-markdown.tsx +20 -0
  169. package/src/components/link-button.tsx +40 -0
  170. package/src/components/markdown-renderer.tsx +20 -0
  171. package/src/components/multi-select-combobox.tsx +148 -0
  172. package/src/components/pagination.tsx +106 -0
  173. package/src/components/popover.tsx +30 -0
  174. package/src/components/progress.tsx +27 -0
  175. package/src/components/radio-group.tsx +37 -0
  176. package/src/components/rating.tsx +136 -0
  177. package/src/components/scroll-area.tsx +45 -0
  178. package/src/components/search-bar.tsx +113 -0
  179. package/src/components/searchable-select.tsx +134 -0
  180. package/src/components/select.tsx +152 -0
  181. package/src/components/separator.tsx +25 -0
  182. package/src/components/sheet.tsx +121 -0
  183. package/src/components/skeleton-card.tsx +282 -0
  184. package/src/components/skeleton.tsx +17 -0
  185. package/src/components/slider.tsx +24 -0
  186. package/src/components/sliding-panels.tsx +99 -0
  187. package/src/components/sonner.tsx +25 -0
  188. package/src/components/sticky-container.tsx +88 -0
  189. package/src/components/switch.tsx +28 -0
  190. package/src/components/table.tsx +90 -0
  191. package/src/components/tabs.tsx +72 -0
  192. package/src/components/text.test.tsx +192 -0
  193. package/src/components/text.tsx +114 -0
  194. package/src/components/textarea.tsx +22 -0
  195. package/src/components/toast.tsx +129 -0
  196. package/src/components/toaster.tsx +31 -0
  197. package/src/components/toggle-group.tsx +69 -0
  198. package/src/components/toggle.tsx +46 -0
  199. package/src/components/tooltip.tsx +29 -0
  200. package/src/components/use-mobile.tsx +19 -0
  201. package/src/components/use-toast.ts +180 -0
  202. package/src/globals.css +409 -0
  203. package/src/hooks/use-haptic.ts +118 -0
  204. package/src/hooks/use-mobile.ts +19 -0
  205. package/src/hooks/use-mounted-theme.ts +20 -0
  206. package/src/hooks/use-sticky.ts +47 -0
  207. package/src/lib/utils.ts +6 -0
@@ -0,0 +1,29 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import * as TooltipPrimitive from "@radix-ui/react-tooltip";
5
+ import { cn } from "../lib/utils";
6
+
7
+ const TooltipProvider = TooltipPrimitive.Provider;
8
+
9
+ const Tooltip = TooltipPrimitive.Root;
10
+
11
+ const TooltipTrigger = TooltipPrimitive.Trigger;
12
+
13
+ const TooltipContent = React.forwardRef<
14
+ React.ElementRef<typeof TooltipPrimitive.Content>,
15
+ React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
16
+ >(({ className, sideOffset = 4, ...props }, ref) => (
17
+ <TooltipPrimitive.Content
18
+ ref={ref}
19
+ sideOffset={sideOffset}
20
+ className={cn(
21
+ "z-50 overflow-hidden rounded-md border bg-popover px-3 py-1.5 text-sm text-popover-foreground shadow-md animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
22
+ className
23
+ )}
24
+ {...props}
25
+ />
26
+ ));
27
+ TooltipContent.displayName = TooltipPrimitive.Content.displayName;
28
+
29
+ export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };
@@ -0,0 +1,19 @@
1
+ import * as React from "react";
2
+
3
+ const MOBILE_BREAKPOINT = 768;
4
+
5
+ export function useIsMobile() {
6
+ const [isMobile, setIsMobile] = React.useState<boolean | undefined>(undefined);
7
+
8
+ React.useEffect(() => {
9
+ const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);
10
+ const onChange = () => {
11
+ setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
12
+ };
13
+ mql.addEventListener("change", onChange);
14
+ setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
15
+ return () => mql.removeEventListener("change", onChange);
16
+ }, []);
17
+
18
+ return !!isMobile;
19
+ }
@@ -0,0 +1,180 @@
1
+ "use client";
2
+
3
+ // Inspired by react-hot-toast library
4
+ import * as React from "react";
5
+
6
+ import type { ToastActionElement, ToastProps } from "./toast";
7
+
8
+ const TOAST_LIMIT = 1;
9
+ const TOAST_REMOVE_DELAY = 1000000;
10
+
11
+ type ToasterToast = ToastProps & {
12
+ id: string;
13
+ title?: React.ReactNode;
14
+ description?: React.ReactNode;
15
+ action?: ToastActionElement;
16
+ };
17
+
18
+ let count = 0;
19
+
20
+ function genId() {
21
+ count = (count + 1) % Number.MAX_SAFE_INTEGER;
22
+ return count.toString();
23
+ }
24
+
25
+ type Action =
26
+ | {
27
+ type: "ADD_TOAST";
28
+ toast: ToasterToast;
29
+ }
30
+ | {
31
+ type: "UPDATE_TOAST";
32
+ toast: Partial<ToasterToast>;
33
+ }
34
+ | {
35
+ type: "DISMISS_TOAST";
36
+ toastId?: ToasterToast["id"];
37
+ }
38
+ | {
39
+ type: "REMOVE_TOAST";
40
+ toastId?: ToasterToast["id"];
41
+ };
42
+
43
+ interface State {
44
+ toasts: ToasterToast[];
45
+ }
46
+
47
+ const toastTimeouts = new Map<string, ReturnType<typeof setTimeout>>();
48
+
49
+ const addToRemoveQueue = (toastId: string) => {
50
+ if (toastTimeouts.has(toastId)) {
51
+ return;
52
+ }
53
+
54
+ const timeout = setTimeout(() => {
55
+ toastTimeouts.delete(toastId);
56
+ dispatch({
57
+ type: "REMOVE_TOAST",
58
+ toastId: toastId,
59
+ });
60
+ }, TOAST_REMOVE_DELAY);
61
+
62
+ toastTimeouts.set(toastId, timeout);
63
+ };
64
+
65
+ export const reducer = (state: State, action: Action): State => {
66
+ switch (action.type) {
67
+ case "ADD_TOAST":
68
+ return {
69
+ ...state,
70
+ toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT),
71
+ };
72
+
73
+ case "UPDATE_TOAST":
74
+ return {
75
+ ...state,
76
+ toasts: state.toasts.map((t) => (t.id === action.toast.id ? { ...t, ...action.toast } : t)),
77
+ };
78
+
79
+ case "DISMISS_TOAST": {
80
+ const { toastId } = action;
81
+
82
+ // ! Side effects ! - This could be extracted into a dismissToast() action,
83
+ // but I'll keep it here for simplicity
84
+ if (toastId) {
85
+ addToRemoveQueue(toastId);
86
+ } else {
87
+ state.toasts.forEach((toast) => {
88
+ addToRemoveQueue(toast.id);
89
+ });
90
+ }
91
+
92
+ return {
93
+ ...state,
94
+ toasts: state.toasts.map((t) =>
95
+ t.id === toastId || toastId === undefined
96
+ ? {
97
+ ...t,
98
+ open: false,
99
+ }
100
+ : t
101
+ ),
102
+ };
103
+ }
104
+ case "REMOVE_TOAST":
105
+ if (action.toastId === undefined) {
106
+ return {
107
+ ...state,
108
+ toasts: [],
109
+ };
110
+ }
111
+ return {
112
+ ...state,
113
+ toasts: state.toasts.filter((t) => t.id !== action.toastId),
114
+ };
115
+ }
116
+ };
117
+
118
+ const listeners: Array<(state: State) => void> = [];
119
+
120
+ let memoryState: State = { toasts: [] };
121
+
122
+ function dispatch(action: Action) {
123
+ memoryState = reducer(memoryState, action);
124
+ listeners.forEach((listener) => {
125
+ listener(memoryState);
126
+ });
127
+ }
128
+
129
+ type Toast = Omit<ToasterToast, "id">;
130
+
131
+ function toast({ ...props }: Toast) {
132
+ const id = genId();
133
+
134
+ const update = (props: ToasterToast) =>
135
+ dispatch({
136
+ type: "UPDATE_TOAST",
137
+ toast: { ...props, id },
138
+ });
139
+ const dismiss = () => dispatch({ type: "DISMISS_TOAST", toastId: id });
140
+
141
+ dispatch({
142
+ type: "ADD_TOAST",
143
+ toast: {
144
+ ...props,
145
+ id,
146
+ open: true,
147
+ onOpenChange: (open) => {
148
+ if (!open) dismiss();
149
+ },
150
+ },
151
+ });
152
+
153
+ return {
154
+ id: id,
155
+ dismiss,
156
+ update,
157
+ };
158
+ }
159
+
160
+ function useToast() {
161
+ const [state, setState] = React.useState<State>(memoryState);
162
+
163
+ React.useEffect(() => {
164
+ listeners.push(setState);
165
+ return () => {
166
+ const index = listeners.indexOf(setState);
167
+ if (index > -1) {
168
+ listeners.splice(index, 1);
169
+ }
170
+ };
171
+ }, [state]);
172
+
173
+ return {
174
+ ...state,
175
+ toast,
176
+ dismiss: (toastId?: string) => dispatch({ type: "DISMISS_TOAST", toastId }),
177
+ };
178
+ }
179
+
180
+ export { useToast, toast };
@@ -0,0 +1,409 @@
1
+ @import "tailwindcss";
2
+ @import "tw-animate-css";
3
+
4
+ @custom-variant dark (&:is(.dark *));
5
+
6
+ /* Improve text rendering for headings */
7
+ h1,
8
+ h2,
9
+ h3,
10
+ h4,
11
+ h5,
12
+ h6 {
13
+ text-rendering: optimizeLegibility;
14
+ }
15
+
16
+ /* Improve rendering for backdrop-filter elements */
17
+ .glass,
18
+ [data-radix-popper-content-wrapper] {
19
+ transform: translateZ(0);
20
+ }
21
+
22
+ /* Dynamic tab content - auto-sizes to tallest content */
23
+ .tab-content-container {
24
+ display: grid;
25
+ grid-template-rows: 1fr;
26
+ }
27
+
28
+ .tab-content-container > * {
29
+ grid-row: 1;
30
+ grid-column: 1;
31
+ opacity: 0;
32
+ pointer-events: none;
33
+ transition: opacity 0.2s ease;
34
+ }
35
+
36
+ .tab-content-container > [data-state="active"] {
37
+ opacity: 1;
38
+ pointer-events: auto;
39
+ }
40
+
41
+ :root {
42
+ /* Safe area insets for PWA/iOS notch support */
43
+ --sat: env(safe-area-inset-top);
44
+ --sar: env(safe-area-inset-right);
45
+ --sab: env(safe-area-inset-bottom);
46
+ --sal: env(safe-area-inset-left);
47
+
48
+ --background: oklch(0.97 0.012 80);
49
+ --foreground: oklch(0.22 0.025 45);
50
+ --card: oklch(0.99 0.006 70);
51
+ --card-foreground: oklch(0.22 0.025 45);
52
+ --popover: oklch(0.99 0.006 70);
53
+ --popover-foreground: oklch(0.22 0.025 45);
54
+ --primary: oklch(0.25 0.05 35);
55
+ --primary-foreground: oklch(0.98 0 0);
56
+ --secondary: oklch(0.985 0.008 80);
57
+ --secondary-foreground: oklch(0.22 0.025 45);
58
+ --muted: oklch(0.985 0.008 80);
59
+ --muted-foreground: oklch(0.62 0.015 45);
60
+ --accent: oklch(0.94 0.04 50);
61
+ --accent-foreground: oklch(0.22 0.025 45);
62
+ --destructive: oklch(0.577 0.245 27.325);
63
+ --destructive-foreground: oklch(0.577 0.245 27.325);
64
+ --border: oklch(0.9 0.012 60);
65
+ --input: oklch(0.9 0.012 60);
66
+ --ring: oklch(0.25 0.05 35);
67
+ --chart-1: oklch(0.646 0.222 41.116);
68
+ --chart-2: oklch(0.6 0.118 184.704);
69
+ --chart-3: oklch(0.398 0.07 227.392);
70
+ --chart-4: oklch(0.828 0.189 84.429);
71
+ --chart-5: oklch(0.769 0.188 70.08);
72
+ --surface: oklch(0.99 0.006 70);
73
+ --surface-foreground: var(--foreground);
74
+ --surface-2: oklch(0.985 0.008 80);
75
+ --line-soft: oklch(0.94 0.01 60);
76
+ --radius: 0.625rem;
77
+ --sidebar: oklch(0.985 0 0);
78
+ --sidebar-foreground: oklch(0.145 0 0);
79
+ --sidebar-primary: oklch(0.205 0 0);
80
+ --sidebar-primary-foreground: oklch(0.985 0 0);
81
+ --sidebar-accent: oklch(0.97 0 0);
82
+ --sidebar-accent-foreground: oklch(0.205 0 0);
83
+ --sidebar-border: oklch(0.922 0 0);
84
+ --sidebar-ring: oklch(0.708 0 0);
85
+ }
86
+
87
+ .dark {
88
+ --background: oklch(0.17 0.014 45);
89
+ --foreground: oklch(0.97 0.008 60);
90
+ --card: oklch(0.21 0.016 45);
91
+ --card-foreground: oklch(0.97 0.008 60);
92
+ --popover: oklch(0.21 0.016 45);
93
+ --popover-foreground: oklch(0.97 0.008 60);
94
+ --primary: oklch(0.98 0 0);
95
+ --primary-foreground: oklch(0.25 0.05 35);
96
+ --secondary: oklch(0.24 0.018 45);
97
+ --secondary-foreground: oklch(0.97 0.008 60);
98
+ --muted: oklch(0.24 0.018 45);
99
+ --muted-foreground: oklch(0.6 0.012 60);
100
+ --accent: oklch(0.3 0.04 50);
101
+ --accent-foreground: oklch(0.97 0.008 60);
102
+ --destructive: oklch(0.396 0.141 25.723);
103
+ --destructive-foreground: oklch(0.637 0.237 25.331);
104
+ --border: oklch(0.3 0.015 45);
105
+ --input: oklch(0.3 0.015 45);
106
+ --ring: oklch(0.98 0 0);
107
+ --chart-1: oklch(0.488 0.243 264.376);
108
+ --chart-2: oklch(0.696 0.17 162.48);
109
+ --chart-3: oklch(0.769 0.188 70.08);
110
+ --chart-4: oklch(0.627 0.265 303.9);
111
+ --chart-5: oklch(0.645 0.246 16.439);
112
+ --surface: oklch(0.21 0.016 45);
113
+ --surface-foreground: var(--foreground);
114
+ --surface-2: oklch(0.24 0.018 45);
115
+ --line-soft: oklch(0.26 0.014 45);
116
+ --sidebar: oklch(0.205 0 0);
117
+ --sidebar-foreground: oklch(0.985 0 0);
118
+ --sidebar-primary: oklch(0.488 0.243 264.376);
119
+ --sidebar-primary-foreground: oklch(0.985 0 0);
120
+ --sidebar-accent: oklch(0.269 0 0);
121
+ --sidebar-accent-foreground: oklch(0.985 0 0);
122
+ --sidebar-border: oklch(0.269 0 0);
123
+ --sidebar-ring: oklch(0.439 0 0);
124
+ }
125
+
126
+ @theme inline {
127
+ /* optional: --font-sans, --font-serif, --font-mono if they are applied in the layout.tsx */
128
+ --font-sans: var(--font-inter);
129
+ --color-background: var(--background);
130
+ --color-foreground: var(--foreground);
131
+ --color-card: var(--card);
132
+ --color-card-foreground: var(--card-foreground);
133
+ --color-popover: var(--popover);
134
+ --color-popover-foreground: var(--popover-foreground);
135
+ --color-primary: var(--primary);
136
+ --color-primary-foreground: var(--primary-foreground);
137
+ --color-secondary: var(--secondary);
138
+ --color-secondary-foreground: var(--secondary-foreground);
139
+ --color-muted: var(--muted);
140
+ --color-muted-foreground: var(--muted-foreground);
141
+ --color-accent: var(--accent);
142
+ --color-accent-foreground: var(--accent-foreground);
143
+ --color-destructive: var(--destructive);
144
+ --color-destructive-foreground: var(--destructive-foreground);
145
+ --color-border: var(--border);
146
+ --color-input: var(--input);
147
+ --color-ring: var(--ring);
148
+ --color-chart-1: var(--chart-1);
149
+ --color-chart-2: var(--chart-2);
150
+ --color-chart-3: var(--chart-3);
151
+ --color-chart-4: var(--chart-4);
152
+ --color-chart-5: var(--chart-5);
153
+ --radius-sm: calc(var(--radius) - 4px);
154
+ --radius-md: calc(var(--radius) - 2px);
155
+ --radius-lg: var(--radius);
156
+ --radius-xl: calc(var(--radius) + 4px);
157
+ --color-sidebar: var(--sidebar);
158
+ --color-sidebar-foreground: var(--sidebar-foreground);
159
+ --color-sidebar-primary: var(--sidebar-primary);
160
+ --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
161
+ --color-sidebar-accent: var(--sidebar-accent);
162
+ --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
163
+ --color-sidebar-border: var(--sidebar-border);
164
+ --color-sidebar-ring: var(--sidebar-ring);
165
+ --color-surface: var(--surface);
166
+ --color-surface-foreground: var(--surface-foreground);
167
+ --color-surface-2: var(--surface-2);
168
+ --color-line-soft: var(--line-soft);
169
+ --animate-highlight-ring: highlight-ring 5s ease-out forwards;
170
+ }
171
+
172
+ @layer base {
173
+ * {
174
+ @apply border-border outline-ring/50;
175
+ }
176
+
177
+ html {
178
+ overflow-y: scroll; /* Always show vertical scrollbar to prevent layout shift */
179
+ }
180
+
181
+ body {
182
+ @apply bg-background text-foreground;
183
+ }
184
+
185
+ /* Prevent Radix UI from adding padding when locking scroll */
186
+ body[data-scroll-locked] {
187
+ margin-right: 0px !important;
188
+ }
189
+ }
190
+
191
+ /* Global glass effects — dark mode default */
192
+ .glass {
193
+ background: rgba(255, 255, 255, 0.04);
194
+ backdrop-filter: blur(16px);
195
+ border: 1px solid rgba(255, 255, 255, 0.15);
196
+ box-shadow:
197
+ 0 2px 8px rgba(0, 0, 0, 0.12),
198
+ 0 1px 2px rgba(0, 0, 0, 0.06);
199
+ }
200
+
201
+ /* Light-mode: opaque background for modal surfaces (Dialog, Sheet, Drawer) */
202
+ :root .glass[role="dialog"] {
203
+ background: rgba(255, 255, 255, 0.97);
204
+ border-color: rgba(0, 0, 0, 0.08);
205
+ backdrop-filter: blur(20px);
206
+ }
207
+
208
+ .dark .glass[role="dialog"] {
209
+ background: rgba(255, 255, 255, 0.04);
210
+ border-color: rgba(255, 255, 255, 0.15);
211
+ }
212
+
213
+ .glass-hover {
214
+ transition:
215
+ background-color 0.2s ease,
216
+ border-color 0.2s ease;
217
+ }
218
+
219
+ .glass-hover:hover {
220
+ background: rgba(255, 255, 255, 0.08);
221
+ border-color: rgba(255, 255, 255, 0.25);
222
+ }
223
+
224
+ .glass-glow {
225
+ box-shadow: 0 0 12px rgba(212, 175, 55, 0.15);
226
+ }
227
+
228
+ /* Scroll-to highlight pulse */
229
+ @keyframes highlight-pulse {
230
+ 0% {
231
+ outline-color: var(--primary);
232
+ outline-offset: 3px;
233
+ }
234
+ 50% {
235
+ outline-color: var(--primary);
236
+ outline-offset: 6px;
237
+ }
238
+ 100% {
239
+ outline-color: var(--primary);
240
+ outline-offset: 3px;
241
+ }
242
+ }
243
+
244
+ .scroll-highlight {
245
+ outline: 2px solid var(--primary);
246
+ outline-offset: 3px;
247
+ animation: highlight-pulse 1s ease-in-out 3;
248
+ }
249
+
250
+ /* Scrollbar styling */
251
+ ::-webkit-scrollbar {
252
+ width: 8px;
253
+ }
254
+
255
+ ::-webkit-scrollbar-track {
256
+ background: transparent;
257
+ }
258
+
259
+ ::-webkit-scrollbar-thumb {
260
+ background: oklch(0.7 0 0 / 0.3);
261
+ border-radius: 4px;
262
+ }
263
+
264
+ ::-webkit-scrollbar-thumb:hover {
265
+ background: oklch(0.7 0 0 / 0.5);
266
+ }
267
+
268
+ .dark ::-webkit-scrollbar-thumb {
269
+ background: oklch(1 0 0 / 0.1);
270
+ }
271
+
272
+ .dark ::-webkit-scrollbar-thumb:hover {
273
+ background: oklch(1 0 0 / 0.2);
274
+ }
275
+
276
+ /* Hide scrollbar for horizontal carousels */
277
+ .scrollbar-hide {
278
+ -ms-overflow-style: none !important;
279
+ scrollbar-width: none !important;
280
+ }
281
+
282
+ .scrollbar-hide::-webkit-scrollbar {
283
+ display: none !important;
284
+ }
285
+
286
+ /* Selection styling */
287
+ ::selection {
288
+ background: rgba(212, 175, 55, 0.3);
289
+ color: #ffffff;
290
+ }
291
+
292
+ /* Focus ring styling */
293
+ .focus-ring {
294
+ @apply focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background;
295
+ }
296
+
297
+ /* App Background — solid color, no gradient */
298
+ .bg-cave-depth {
299
+ background: var(--background);
300
+ }
301
+
302
+ /* Highlight ring animation — used when scrolling to a specific card */
303
+ @keyframes highlight-ring {
304
+ 0% {
305
+ box-shadow: 0 0 0 0 var(--primary);
306
+ }
307
+ 15% {
308
+ box-shadow: 0 0 0 3px var(--primary);
309
+ }
310
+ 100% {
311
+ box-shadow: 0 0 0 0 transparent;
312
+ }
313
+ }
314
+
315
+ /* Collapsible animations */
316
+ @keyframes collapsible-down {
317
+ from {
318
+ height: 0;
319
+ opacity: 0;
320
+ }
321
+ to {
322
+ height: var(--radix-collapsible-content-height);
323
+ opacity: 1;
324
+ }
325
+ }
326
+
327
+ @keyframes collapsible-up {
328
+ from {
329
+ height: var(--radix-collapsible-content-height);
330
+ opacity: 1;
331
+ }
332
+ to {
333
+ height: 0;
334
+ opacity: 0;
335
+ }
336
+ }
337
+
338
+ .animate-collapsible-down {
339
+ animation: collapsible-down 0.3s ease-out;
340
+ }
341
+
342
+ .animate-collapsible-up {
343
+ animation: collapsible-up 0.2s ease-in;
344
+ }
345
+
346
+ /* Swiper pagination styling for experience cards */
347
+ .swiper-pagination-bullet {
348
+ background: rgba(255, 255, 255, 0.3) !important;
349
+ opacity: 1 !important;
350
+ width: 6px !important;
351
+ height: 6px !important;
352
+ transition: all 0.3s ease !important;
353
+ }
354
+
355
+ .swiper-pagination-bullet-active {
356
+ background: var(--color-primary) !important;
357
+ width: 18px !important;
358
+ border-radius: 4px !important;
359
+ }
360
+
361
+ .dark .swiper-pagination-bullet {
362
+ background: rgba(255, 255, 255, 0.3) !important;
363
+ }
364
+
365
+ .dark .swiper-pagination-bullet-active {
366
+ background: var(--color-primary) !important;
367
+ }
368
+
369
+ /* Safe Area Utilities for PWA */
370
+ .safe-top {
371
+ padding-top: max(3rem, env(safe-area-inset-top));
372
+ }
373
+
374
+ .safe-bottom {
375
+ padding-bottom: max(1rem, env(safe-area-inset-bottom));
376
+ }
377
+
378
+ .safe-left {
379
+ padding-left: max(0px, env(safe-area-inset-left));
380
+ }
381
+
382
+ .safe-right {
383
+ padding-right: max(0px, env(safe-area-inset-right));
384
+ }
385
+
386
+ /* Specific for mobile header (3rem = 48px = 12 in Tailwind) */
387
+ /* Only apply safe area padding when app is in PWA/standalone mode */
388
+ /* @media (max-width: 768px) and (display-mode: standalone) {
389
+ .mobile-header-safe {
390
+ padding-top: calc(3rem + env(safe-area-inset-top, 0px));
391
+ }
392
+ } */
393
+
394
+ /* In browser mode, just use normal padding */
395
+ /* @media (max-width: 768px) and (display-mode: browser) {
396
+ .mobile-header-safe {
397
+ padding-top: 3rem;
398
+ }
399
+ } */
400
+
401
+ /* For sticky header */
402
+ .sticky-header-safe {
403
+ top: env(safe-area-inset-top, 0);
404
+ }
405
+
406
+ /* For mobile bottom nav */
407
+ .mobile-nav-safe {
408
+ bottom: calc(1rem + env(safe-area-inset-bottom, 0px));
409
+ }