@fayz-ai/ui 0.14.0 → 0.16.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 (115) hide show
  1. package/dist/{ChartWidgetInner-GDE5PBTR.js → ChartWidgetInner-IOLIUYTB.js} +21 -8
  2. package/dist/ChartWidgetInner-IOLIUYTB.js.map +1 -0
  3. package/dist/analytics/AnalyticsCard.d.ts.map +1 -1
  4. package/dist/analytics/AnalyticsControls.d.ts.map +1 -1
  5. package/dist/analytics/context.d.ts +15 -1
  6. package/dist/analytics/context.d.ts.map +1 -1
  7. package/dist/analytics/index.d.ts +1 -1
  8. package/dist/analytics/index.d.ts.map +1 -1
  9. package/dist/chunk-3OVZ5JJQ.js +540 -0
  10. package/dist/chunk-3OVZ5JJQ.js.map +1 -0
  11. package/dist/{chunk-UZYXFEQS.js → chunk-4GK6IHOI.js} +16 -3
  12. package/dist/chunk-4GK6IHOI.js.map +1 -0
  13. package/dist/{chunk-46UZAKFJ.js → chunk-5VF45NLE.js} +7 -6
  14. package/dist/chunk-5VF45NLE.js.map +1 -0
  15. package/dist/{chunk-5KAGNW45.js → chunk-6BJTH3L6.js} +5 -5
  16. package/dist/chunk-6BJTH3L6.js.map +1 -0
  17. package/dist/chunk-AASANYXN.js +601 -0
  18. package/dist/chunk-AASANYXN.js.map +1 -0
  19. package/dist/{chunk-2EFRFYGF.js → chunk-E44WOPCR.js} +198 -39
  20. package/dist/chunk-E44WOPCR.js.map +1 -0
  21. package/dist/chunk-HKGTF2YA.js +117 -0
  22. package/dist/chunk-HKGTF2YA.js.map +1 -0
  23. package/dist/chunk-OKZ6WQB3.js +57 -0
  24. package/dist/chunk-OKZ6WQB3.js.map +1 -0
  25. package/dist/{chunk-MVH64JFG.js → chunk-PZU7XC2M.js} +780 -719
  26. package/dist/chunk-PZU7XC2M.js.map +1 -0
  27. package/dist/{chunk-77CUIMOI.js → chunk-SDPYDWWV.js} +459 -226
  28. package/dist/chunk-SDPYDWWV.js.map +1 -0
  29. package/dist/dashboard/ChartWidgetInner.d.ts.map +1 -1
  30. package/dist/dashboard/index.js +5 -4
  31. package/dist/hooks/useIsWide.d.ts +16 -0
  32. package/dist/hooks/useIsWide.d.ts.map +1 -0
  33. package/dist/icons.d.ts.map +1 -1
  34. package/dist/icons.js +1 -1
  35. package/dist/index.d.ts +5 -3
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/index.js +51 -17
  38. package/dist/index.js.map +1 -1
  39. package/dist/layout/AppShell.d.ts +31 -3
  40. package/dist/layout/AppShell.d.ts.map +1 -1
  41. package/dist/layout/ModulePage.d.ts +4 -0
  42. package/dist/layout/ModulePage.d.ts.map +1 -1
  43. package/dist/layout/SaveBar.d.ts.map +1 -1
  44. package/dist/layout/Sidebar.d.ts +27 -4
  45. package/dist/layout/Sidebar.d.ts.map +1 -1
  46. package/dist/layout/Topbar.d.ts +14 -3
  47. package/dist/layout/Topbar.d.ts.map +1 -1
  48. package/dist/layout/index.d.ts +1 -1
  49. package/dist/layout/index.d.ts.map +1 -1
  50. package/dist/layout/index.js +5 -4
  51. package/dist/primitives/SearchCombobox.d.ts +9 -1
  52. package/dist/primitives/SearchCombobox.d.ts.map +1 -1
  53. package/dist/primitives/checkbox.d.ts +4 -0
  54. package/dist/primitives/checkbox.d.ts.map +1 -1
  55. package/dist/primitives/color-palette.d.ts +24 -0
  56. package/dist/primitives/color-palette.d.ts.map +1 -0
  57. package/dist/primitives/header-tabs.d.ts +11 -1
  58. package/dist/primitives/header-tabs.d.ts.map +1 -1
  59. package/dist/primitives/image-drop.d.ts +28 -0
  60. package/dist/primitives/image-drop.d.ts.map +1 -0
  61. package/dist/primitives/index.d.ts +11 -3
  62. package/dist/primitives/index.d.ts.map +1 -1
  63. package/dist/primitives/index.js +7 -5
  64. package/dist/primitives/list-view.d.ts +11 -1
  65. package/dist/primitives/list-view.d.ts.map +1 -1
  66. package/dist/primitives/modal.d.ts.map +1 -1
  67. package/dist/primitives/option-select.d.ts +46 -0
  68. package/dist/primitives/option-select.d.ts.map +1 -0
  69. package/dist/primitives/page-actions.d.ts +55 -0
  70. package/dist/primitives/page-actions.d.ts.map +1 -0
  71. package/dist/primitives/page-header.d.ts +5 -2
  72. package/dist/primitives/page-header.d.ts.map +1 -1
  73. package/dist/primitives/page-toolbar.d.ts +20 -1
  74. package/dist/primitives/page-toolbar.d.ts.map +1 -1
  75. package/dist/primitives/refresh-button.d.ts.map +1 -1
  76. package/dist/primitives/row-actions.d.ts +11 -0
  77. package/dist/primitives/row-actions.d.ts.map +1 -0
  78. package/dist/primitives/sheet.d.ts.map +1 -1
  79. package/dist/primitives/step-progress.d.ts +20 -0
  80. package/dist/primitives/step-progress.d.ts.map +1 -0
  81. package/dist/primitives/use-row-selection.d.ts +50 -0
  82. package/dist/primitives/use-row-selection.d.ts.map +1 -0
  83. package/dist/primitives/whatsapp-icon.d.ts +7 -0
  84. package/dist/primitives/whatsapp-icon.d.ts.map +1 -0
  85. package/dist/stores/index.d.ts +1 -1
  86. package/dist/stores/index.d.ts.map +1 -1
  87. package/dist/stores/layout.store.d.ts +1 -0
  88. package/dist/stores/layout.store.d.ts.map +1 -1
  89. package/dist/stores/page-header.store.d.ts +39 -5
  90. package/dist/stores/page-header.store.d.ts.map +1 -1
  91. package/dist/styles.css +186 -3
  92. package/dist/wizard/Wizard.d.ts +38 -0
  93. package/dist/wizard/Wizard.d.ts.map +1 -0
  94. package/dist/wizard/WizardField.d.ts +22 -0
  95. package/dist/wizard/WizardField.d.ts.map +1 -0
  96. package/dist/wizard/index.d.ts +6 -0
  97. package/dist/wizard/index.d.ts.map +1 -0
  98. package/dist/wizard/index.js +5 -0
  99. package/dist/wizard/index.js.map +1 -0
  100. package/dist/wizard/logic.d.ts +39 -0
  101. package/dist/wizard/logic.d.ts.map +1 -0
  102. package/dist/wizard/types.d.ts +95 -0
  103. package/dist/wizard/types.d.ts.map +1 -0
  104. package/dist/wizard/useWizard.d.ts +47 -0
  105. package/dist/wizard/useWizard.d.ts.map +1 -0
  106. package/package.json +12 -6
  107. package/dist/ChartWidgetInner-GDE5PBTR.js.map +0 -1
  108. package/dist/chunk-2EFRFYGF.js.map +0 -1
  109. package/dist/chunk-46UZAKFJ.js.map +0 -1
  110. package/dist/chunk-5KAGNW45.js.map +0 -1
  111. package/dist/chunk-77CUIMOI.js.map +0 -1
  112. package/dist/chunk-LFZFBN3P.js +0 -142
  113. package/dist/chunk-LFZFBN3P.js.map +0 -1
  114. package/dist/chunk-MVH64JFG.js.map +0 -1
  115. package/dist/chunk-UZYXFEQS.js.map +0 -1
@@ -1,8 +1,9 @@
1
- import { cn, useHasPageHeaderHost, usePageHeader, useLayoutStore, useRegisterPageActions, useHasPageHeaderBack, usePageHeaderSlot, useHasPageHeaderOutlet, useHasPageActions, usePageHeaderHost } from './chunk-LFZFBN3P.js';
2
- import { ICON_MAP } from './chunk-UZYXFEQS.js';
3
- import * as React7 from 'react';
1
+ import { useHasPageHeaderHost, usePageHeader, PageTitlePortal, useLayoutStore, useHasPageHeaderBack, useRegisterPageActions, usePageHeaderClaimed, usePageTitleSlotHost, useHasPageHeaderOutlet, useHasPageActions, usePageHeaderHost } from './chunk-HKGTF2YA.js';
2
+ import { cn } from './chunk-OKZ6WQB3.js';
3
+ import { ICON_MAP } from './chunk-4GK6IHOI.js';
4
+ import * as React2 from 'react';
4
5
  import * as DialogPrimitive from '@radix-ui/react-dialog';
5
- import { X, Loader2, ArrowLeft, Menu, Search, Crown, PanelLeftClose, PanelLeft, Bell, Repeat, GraduationCap, Newspaper, Clapperboard, Zap, Star, UserCheck, Inbox, Clock3, Ban, Map, ListPlus, CalendarX, CalendarCheck2, Mic, UsersRound, Disc3, ListMusic, Eye, Music, Layers, Banknote, BadgeDollarSign, CalendarDays, Radio, PartyPopper, FileCheck2, Box, Boxes, TreePalm, UserX, UserPlus, ArrowDownRight, ArrowUpRight, Ruler, Warehouse, Wallet, Sparkles, AlertTriangle, Clock, CircleDollarSign, TrendingUp, Receipt, Landmark, TableProperties, SlidersHorizontal, Plug, ArrowLeftRight, ArrowUpCircle, ArrowDownCircle, Wheat, Egg, Apple, LeafyGreen, LayoutTemplate, Heart, PawPrint, Cat, Dog, FolderOpen, ListChecks, List, Shield, Plus, Filter, ChevronDown, Building2, Contact, Handshake, MapPin, UtensilsCrossed, Camera, Tags, Tag, Percent, BookOpenCheck, Globe, MessageCircle, BookOpen, UserCog, Briefcase, ClipboardCheck, ClipboardList, Wrench, Target, ShoppingBag, ShoppingCart, Megaphone, DollarSign, Mail, FileText, BarChart3, Activity, Package, CalendarClock, Calendar, CreditCard, Settings, Users, Home, ChevronRight, ChevronLeft, ExternalLink, PiggyBank, LogOut } from 'lucide-react';
6
+ import { X, Loader2, ArrowLeft, Search, Bell, PanelLeft, PanelLeftClose, Menu, Crown, ChevronDown, ChevronRight, ChevronLeft, ExternalLink, CreditCard, Sparkles, Home, Plus, UserCog, Settings, LogOut } from 'lucide-react';
6
7
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
7
8
  import { useTranslation, useNavReferrer, routeModule, navHistoryBack } from '@fayz-ai/core';
8
9
  import * as Popover from '@radix-ui/react-popover';
@@ -28,26 +29,40 @@ var Sheet = DialogPrimitive.Root;
28
29
  var SheetTrigger = DialogPrimitive.Trigger;
29
30
  var SheetClose = DialogPrimitive.Close;
30
31
  var SheetPortal = DialogPrimitive.Portal;
31
- var SheetOverlay = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
32
+ var SheetOverlay = React2.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
32
33
  DialogPrimitive.Overlay,
33
34
  {
34
35
  ref,
35
36
  className: cn(
36
- "saas-sheet-overlay fixed inset-0 z-50 bg-black/40 backdrop-blur-[2px]",
37
+ "saas-sheet-overlay fixed inset-0 z-[100] bg-black/40 backdrop-blur-[2px]",
37
38
  className
38
39
  ),
39
40
  ...props
40
41
  }
41
42
  ));
42
43
  SheetOverlay.displayName = "SheetOverlay";
43
- var SheetContent = React7.forwardRef(({ className, children, width, hideClose, overlay = "blur", ...props }, ref) => /* @__PURE__ */ jsxs(SheetPortal, { children: [
44
+ var SheetContent = React2.forwardRef(({ className, children, width, hideClose, overlay = "blur", onPointerDownOutside, onInteractOutside, ...props }, ref) => /* @__PURE__ */ jsxs(SheetPortal, { children: [
44
45
  /* @__PURE__ */ jsx(SheetOverlay, { className: overlay === "none" ? "bg-transparent backdrop-blur-0" : overlay === "dim" ? "backdrop-blur-0" : void 0 }),
45
46
  /* @__PURE__ */ jsxs(
46
47
  DialogPrimitive.Content,
47
48
  {
48
49
  ref,
50
+ onPointerDownOutside: (e) => {
51
+ const target = e.target;
52
+ if (target.closest("[data-modal-passthrough]") || target.closest("[data-radix-popper-content-wrapper]")) {
53
+ e.preventDefault();
54
+ }
55
+ onPointerDownOutside?.(e);
56
+ },
57
+ onInteractOutside: (e) => {
58
+ const target = e.target;
59
+ if (target.closest("[data-modal-passthrough]") || target.closest("[data-radix-popper-content-wrapper]")) {
60
+ e.preventDefault();
61
+ }
62
+ onInteractOutside?.(e);
63
+ },
49
64
  className: cn(
50
- "fayz-glass-surface saas-sheet-content fixed inset-y-0 right-0 z-50 flex flex-col border-l bg-card shadow-2xl outline-none",
65
+ "fayz-glass-surface saas-sheet-content fixed inset-y-0 right-0 z-[100] flex flex-col border-l bg-card shadow-2xl outline-none",
51
66
  "w-full",
52
67
  width ?? "max-w-md",
53
68
  className
@@ -84,7 +99,7 @@ function SheetFooter({ className, ...props }) {
84
99
  );
85
100
  }
86
101
  SheetFooter.displayName = "SheetFooter";
87
- var SheetTitle = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
102
+ var SheetTitle = React2.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
88
103
  DialogPrimitive.Title,
89
104
  {
90
105
  ref,
@@ -93,7 +108,7 @@ var SheetTitle = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE_
93
108
  }
94
109
  ));
95
110
  SheetTitle.displayName = "SheetTitle";
96
- var SheetDescription = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
111
+ var SheetDescription = React2.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
97
112
  DialogPrimitive.Description,
98
113
  {
99
114
  ref,
@@ -112,8 +127,8 @@ function SheetBody({ className, ...props }) {
112
127
  );
113
128
  }
114
129
  SheetBody.displayName = "SheetBody";
115
- var SaveBarApiContext = React7.createContext(null);
116
- var SaveBarStateContext = React7.createContext({ visible: false, saving: false });
130
+ var SaveBarApiContext = React2.createContext(null);
131
+ var SaveBarStateContext = React2.createContext({ visible: false, saving: false });
117
132
  var _saveBarVisible = false;
118
133
  var _saveBarSubs = /* @__PURE__ */ new Set();
119
134
  function setSaveBarVisibleSignal(v) {
@@ -122,7 +137,7 @@ function setSaveBarVisibleSignal(v) {
122
137
  _saveBarSubs.forEach((fn) => fn());
123
138
  }
124
139
  function useSaveBarVisible() {
125
- return React7.useSyncExternalStore(
140
+ return React2.useSyncExternalStore(
126
141
  (cb) => {
127
142
  _saveBarSubs.add(cb);
128
143
  return () => {
@@ -134,15 +149,15 @@ function useSaveBarVisible() {
134
149
  );
135
150
  }
136
151
  function SaveBarProvider({ children }) {
137
- const [state, setState] = React7.useState({ visible: false, saving: false });
138
- const handlers = React7.useRef({});
139
- const api = React7.useMemo(() => ({ setState, handlers }), []);
140
- const backStack = React7.useRef([]);
141
- const backApi = React7.useMemo(() => ({ stack: backStack }), []);
142
- React7.useEffect(() => {
152
+ const [state, setState] = React2.useState({ visible: false, saving: false });
153
+ const handlers = React2.useRef({});
154
+ const api = React2.useMemo(() => ({ setState, handlers }), []);
155
+ const backStack = React2.useRef([]);
156
+ const backApi = React2.useMemo(() => ({ stack: backStack }), []);
157
+ React2.useEffect(() => {
143
158
  setSaveBarVisibleSignal(state.visible);
144
159
  }, [state.visible]);
145
- React7.useEffect(() => {
160
+ React2.useEffect(() => {
146
161
  function onKey(e) {
147
162
  if (state.visible && !state.saving) {
148
163
  if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
@@ -170,7 +185,7 @@ function SaveBarProvider({ children }) {
170
185
  }, [state.visible, state.saving]);
171
186
  return /* @__PURE__ */ jsx(SaveBarApiContext.Provider, { value: api, children: /* @__PURE__ */ jsx(BackNavApiContext.Provider, { value: backApi, children: /* @__PURE__ */ jsx(SaveBarStateContext.Provider, { value: state, children }) }) });
172
187
  }
173
- var BackNavApiContext = React7.createContext(null);
188
+ var BackNavApiContext = React2.createContext(null);
174
189
  function isEditableTarget(target) {
175
190
  const el = target;
176
191
  if (!el || !el.tagName) return false;
@@ -178,12 +193,12 @@ function isEditableTarget(target) {
178
193
  return tag === "input" || tag === "textarea" || tag === "select" || el.isContentEditable;
179
194
  }
180
195
  function useBackHandler(onBack) {
181
- const api = React7.useContext(BackNavApiContext);
182
- const ref = React7.useRef(onBack);
183
- React7.useEffect(() => {
196
+ const api = React2.useContext(BackNavApiContext);
197
+ const ref = React2.useRef(onBack);
198
+ React2.useEffect(() => {
184
199
  ref.current = onBack;
185
200
  });
186
- React7.useEffect(() => {
201
+ React2.useEffect(() => {
187
202
  if (!api) return;
188
203
  const stack = api.stack.current;
189
204
  stack.push(ref);
@@ -194,23 +209,23 @@ function useBackHandler(onBack) {
194
209
  }, [api]);
195
210
  }
196
211
  function useSaveBar({ dirty, saving = false, onSave, onDiscard, message, saveLabel, discardLabel }) {
197
- const api = React7.useContext(SaveBarApiContext);
198
- React7.useEffect(() => {
212
+ const api = React2.useContext(SaveBarApiContext);
213
+ React2.useEffect(() => {
199
214
  if (api) api.handlers.current = { onSave, onDiscard };
200
215
  });
201
- React7.useEffect(() => {
216
+ React2.useEffect(() => {
202
217
  if (!api) return;
203
218
  api.setState({ visible: dirty, saving, message, saveLabel, discardLabel });
204
219
  return () => api.setState((s) => ({ ...s, visible: false }));
205
220
  }, [api, dirty, saving, message, saveLabel, discardLabel]);
206
221
  }
207
222
  function SaveBar({ className }) {
208
- const state = React7.useContext(SaveBarStateContext);
209
- const api = React7.useContext(SaveBarApiContext);
223
+ const state = React2.useContext(SaveBarStateContext);
224
+ const api = React2.useContext(SaveBarApiContext);
210
225
  const t = useTranslation();
211
- const [rendered, setRendered] = React7.useState(state.visible);
212
- const [closing, setClosing] = React7.useState(false);
213
- React7.useEffect(() => {
226
+ const [rendered, setRendered] = React2.useState(state.visible);
227
+ const [closing, setClosing] = React2.useState(false);
228
+ React2.useEffect(() => {
214
229
  if (state.visible) {
215
230
  setRendered(true);
216
231
  setClosing(false);
@@ -226,40 +241,52 @@ function SaveBar({ className }) {
226
241
  }, [state.visible, rendered]);
227
242
  if (!api || !rendered) return null;
228
243
  const { saving, message, saveLabel, discardLabel } = state;
229
- return /* @__PURE__ */ jsx("div", { className: cn("pointer-events-none fixed inset-x-0 bottom-5 z-40 flex justify-center px-4", className), children: /* @__PURE__ */ jsxs("div", { className: cn(
230
- "pointer-events-auto flex items-center gap-5 rounded-full bg-foreground py-2 pl-5 pr-2 text-background shadow-xl ring-1 ring-black/5 duration-200",
231
- closing ? "animate-out fade-out slide-out-to-bottom-2" : "animate-in fade-in slide-in-from-bottom-2"
232
- ), children: [
233
- /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2 text-sm font-medium", children: [
234
- /* @__PURE__ */ jsx("span", { className: "h-2 w-2 shrink-0 rounded-full bg-amber-400" }),
235
- message ?? t("saveBar.unsaved")
236
- ] }),
237
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
238
- /* @__PURE__ */ jsx(
239
- "button",
240
- {
241
- type: "button",
242
- disabled: saving,
243
- onClick: () => api.handlers.current.onDiscard?.(),
244
- className: "rounded-full px-3 py-1.5 text-sm font-medium text-background/70 transition-colors hover:bg-background/10 hover:text-background disabled:opacity-50",
245
- children: discardLabel ?? t("common.discard")
246
- }
244
+ return /* @__PURE__ */ jsx(
245
+ "div",
246
+ {
247
+ className: cn(
248
+ // Above the tab bar (z-50), below a full-screen sheet (z-60), and lifted
249
+ // clear of the bar's height so its actions are not sitting under it.
250
+ "pointer-events-none fixed inset-x-0 z-[55] flex justify-center px-4",
251
+ "bottom-[calc(var(--fz-bottom-bar,0px)+1.25rem)]",
252
+ className
247
253
  ),
248
- /* @__PURE__ */ jsxs(
249
- "button",
250
- {
251
- type: "button",
252
- disabled: saving,
253
- onClick: () => api.handlers.current.onSave?.(),
254
- className: "inline-flex items-center rounded-full bg-background px-4 py-1.5 text-sm font-semibold text-foreground transition-colors hover:bg-background/90 disabled:opacity-60",
255
- children: [
256
- saving && /* @__PURE__ */ jsx(Loader2, { className: "mr-1.5 h-3.5 w-3.5 animate-spin" }),
257
- saveLabel ?? t("common.save")
258
- ]
259
- }
260
- )
261
- ] })
262
- ] }) });
254
+ children: /* @__PURE__ */ jsxs("div", { className: cn(
255
+ "pointer-events-auto flex items-center gap-5 rounded-full bg-foreground py-2 pl-5 pr-2 text-background shadow-xl ring-1 ring-black/5 duration-200",
256
+ closing ? "animate-out fade-out slide-out-to-bottom-2" : "animate-in fade-in slide-in-from-bottom-2"
257
+ ), children: [
258
+ /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2 text-sm font-medium", children: [
259
+ /* @__PURE__ */ jsx("span", { className: "h-2 w-2 shrink-0 rounded-full bg-amber-400" }),
260
+ message ?? t("saveBar.unsaved")
261
+ ] }),
262
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
263
+ /* @__PURE__ */ jsx(
264
+ "button",
265
+ {
266
+ type: "button",
267
+ disabled: saving,
268
+ onClick: () => api.handlers.current.onDiscard?.(),
269
+ className: "rounded-full px-3 py-1.5 text-sm font-medium text-background/70 transition-colors hover:bg-background/10 hover:text-background disabled:opacity-50",
270
+ children: discardLabel ?? t("common.discard")
271
+ }
272
+ ),
273
+ /* @__PURE__ */ jsxs(
274
+ "button",
275
+ {
276
+ type: "button",
277
+ disabled: saving,
278
+ onClick: () => api.handlers.current.onSave?.(),
279
+ className: "inline-flex items-center rounded-full bg-background px-4 py-1.5 text-sm font-semibold text-foreground transition-colors hover:bg-background/90 disabled:opacity-60",
280
+ children: [
281
+ saving && /* @__PURE__ */ jsx(Loader2, { className: "mr-1.5 h-3.5 w-3.5 animate-spin" }),
282
+ saveLabel ?? t("common.save")
283
+ ]
284
+ }
285
+ )
286
+ ] })
287
+ ] })
288
+ }
289
+ );
263
290
  }
264
291
  function useCrossModuleBack(onBack, parentLabel) {
265
292
  const currentPath = typeof window !== "undefined" ? (window.location.hash ? window.location.hash.slice(1) : window.location.pathname) || "/" : "/";
@@ -274,10 +301,14 @@ function useCrossModuleBack(onBack, parentLabel) {
274
301
  function PageHeaderCrumb({
275
302
  title,
276
303
  parentLabel,
277
- onBack
304
+ onBack,
305
+ headingLevel = "h2"
278
306
  }) {
307
+ const Heading = headingLevel;
308
+ const asHeading = typeof title === "string" && title !== "";
279
309
  if (!onBack) {
280
- return title ? /* @__PURE__ */ jsx("h2", { className: "min-w-0 truncate text-lg font-semibold", children: title }) : null;
310
+ if (!title) return null;
311
+ return asHeading ? /* @__PURE__ */ jsx(Heading, { className: "min-w-0 truncate text-lg font-semibold", children: title }) : /* @__PURE__ */ jsx("span", { className: "min-w-0 truncate text-lg font-semibold", children: title });
281
312
  }
282
313
  return /* @__PURE__ */ jsxs("nav", { className: "flex min-w-0 items-center gap-1.5 text-sm text-muted-foreground", children: [
283
314
  /* @__PURE__ */ jsxs(
@@ -298,6 +329,11 @@ function PageHeaderCrumb({
298
329
  // Title still waiting on data: a pulsing bar, otherwise the line
299
330
  // appears from nowhere and shoves the rest when the record lands.
300
331
  /* @__PURE__ */ jsx("span", { className: "inline-block h-4 w-24 animate-pulse rounded bg-muted align-middle" })
332
+ ) : asHeading ? (
333
+ // A heading, not a span, when it IS the screen's name: a subpage
334
+ // that only ever renders a breadcrumb was leaving the page with no
335
+ // heading for a screen reader to land on.
336
+ /* @__PURE__ */ jsx(Heading, { className: "min-w-0 truncate font-medium text-foreground", children: title })
301
337
  ) : /* @__PURE__ */ jsx("span", { className: "min-w-0 truncate font-medium text-foreground", children: title })
302
338
  ] })
303
339
  ] });
@@ -306,7 +342,7 @@ function PageHeader({ title, onBack, parentLabel, backLabel }) {
306
342
  const hasHost = useHasPageHeaderHost();
307
343
  const { onBack: effectiveOnBack, parentLabel: effectiveParent } = useCrossModuleBack(onBack, parentLabel);
308
344
  useBackHandler(effectiveOnBack);
309
- const node = React7.useMemo(() => /* @__PURE__ */ jsx(
345
+ const node = React2.useMemo(() => /* @__PURE__ */ jsx(
310
346
  PageHeaderCrumb,
311
347
  {
312
348
  title,
@@ -314,385 +350,21 @@ function PageHeader({ title, onBack, parentLabel, backLabel }) {
314
350
  onBack: effectiveOnBack
315
351
  }
316
352
  ), [effectiveOnBack, effectiveParent, backLabel, title]);
317
- usePageHeader(hasHost ? node : null, { back: !!effectiveOnBack });
318
- if (hasHost) return null;
353
+ usePageHeader(hasHost, { back: !!effectiveOnBack });
354
+ if (hasHost) return /* @__PURE__ */ jsx(PageTitlePortal, { children: node });
319
355
  return /* @__PURE__ */ jsx("div", { className: "mb-4", children: node });
320
356
  }
321
- function TopbarUserMenu({
322
- user,
323
- userPlan,
324
- billingLabel = "Subscription",
325
- onSignOut,
326
- onProfile,
327
- onSettings,
328
- onBilling,
329
- userMenuExtras
330
- }) {
331
- const [open, setOpen] = React7.useState(false);
332
- if (!user) return null;
333
- const initial = (user.fullName?.trim()?.[0] ?? user.email?.[0] ?? "?").toUpperCase();
334
- const item = (label, onClick, Icon) => onClick ? /* @__PURE__ */ jsxs(
335
- "button",
336
- {
337
- onClick: () => {
338
- setOpen(false);
339
- onClick();
340
- },
341
- className: "flex w-full items-center gap-2.5 rounded-lg px-3 py-2 text-sm text-popover-foreground transition-colors hover:bg-muted/70",
342
- children: [
343
- Icon && /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4 text-muted-foreground" }),
344
- /* @__PURE__ */ jsx("span", { children: label })
345
- ]
346
- },
347
- label
348
- ) : null;
349
- return /* @__PURE__ */ jsxs(Popover.Root, { open, onOpenChange: setOpen, children: [
350
- /* @__PURE__ */ jsx(Popover.Trigger, { asChild: true, children: /* @__PURE__ */ jsx(
351
- "button",
352
- {
353
- "aria-label": "User menu",
354
- className: "ml-1 flex h-9 w-9 items-center justify-center overflow-hidden rounded-full bg-primary text-xs font-semibold text-primary-foreground ring-2 ring-transparent transition hover:ring-sidebar-border",
355
- children: user.avatarUrl ? (
356
- // eslint-disable-next-line @next/next/no-img-element
357
- /* @__PURE__ */ jsx("img", { src: user.avatarUrl, alt: "", className: "h-full w-full object-cover" })
358
- ) : initial
359
- }
360
- ) }),
361
- /* @__PURE__ */ jsx(Popover.Portal, { children: /* @__PURE__ */ jsxs(
362
- Popover.Content,
363
- {
364
- align: "end",
365
- sideOffset: 8,
366
- className: "fayz-glass-surface z-50 min-w-[220px] rounded-xl border border-border bg-popover p-1.5 shadow-lg animate-in fade-in-0 zoom-in-95",
367
- children: [
368
- /* @__PURE__ */ jsxs("div", { className: "border-b border-border px-3 py-2", children: [
369
- /* @__PURE__ */ jsx("p", { className: "truncate text-sm font-medium text-foreground", children: user.fullName || user.email }),
370
- /* @__PURE__ */ jsx("p", { className: "truncate text-xs text-muted-foreground", children: user.email })
371
- ] }),
372
- /* @__PURE__ */ jsxs("div", { className: "mt-1 space-y-0.5", children: [
373
- item("Perfil", onProfile, UserCog),
374
- item("Configura\xE7\xF5es", onSettings, Settings),
375
- onBilling && /* @__PURE__ */ jsxs(
376
- "button",
377
- {
378
- onClick: () => {
379
- setOpen(false);
380
- onBilling();
381
- },
382
- className: "flex w-full items-center gap-2.5 rounded-lg px-3 py-2 text-sm text-popover-foreground transition-colors hover:bg-muted/70",
383
- children: [
384
- /* @__PURE__ */ jsx(CreditCard, { className: "h-4 w-4 text-muted-foreground" }),
385
- /* @__PURE__ */ jsx("span", { className: "flex-1 text-left", children: billingLabel }),
386
- userPlan && /* @__PURE__ */ jsxs(
387
- "span",
388
- {
389
- className: userPlan.paid ? "inline-flex items-center gap-1 rounded-full border border-amber-400/40 bg-amber-400/15 px-2 py-0.5 text-[10px] font-semibold text-amber-600 dark:text-amber-300" : "inline-flex items-center gap-1 rounded-full border border-border bg-muted px-2 py-0.5 text-[10px] font-medium text-muted-foreground",
390
- children: [
391
- userPlan.paid ? /* @__PURE__ */ jsx(Crown, { className: "h-3 w-3" }) : /* @__PURE__ */ jsx(Sparkles, { className: "h-3 w-3" }),
392
- userPlan.label
393
- ]
394
- }
395
- )
396
- ]
397
- },
398
- "billing"
399
- ),
400
- userMenuExtras?.map((e) => item(e.label, e.onClick)),
401
- onSignOut && /* @__PURE__ */ jsxs(Fragment, { children: [
402
- /* @__PURE__ */ jsx("div", { className: "my-1 border-t border-border" }),
403
- item("Sair", onSignOut, LogOut)
404
- ] })
405
- ] })
406
- ]
407
- }
408
- ) })
409
- ] });
410
- }
411
- var ICON_MAP2 = {
412
- Home,
413
- Users,
414
- Settings,
415
- CreditCard,
416
- Bell,
417
- Calendar,
418
- CalendarClock,
419
- Package,
420
- Activity,
421
- BarChart3,
422
- FileText,
423
- Mail,
424
- DollarSign,
425
- Megaphone,
426
- ShoppingCart,
427
- ShoppingBag,
428
- Target,
429
- Wrench,
430
- ClipboardList,
431
- ClipboardCheck,
432
- Briefcase,
433
- UserCog,
434
- BookOpen,
435
- MessageCircle,
436
- Globe,
437
- BookOpenCheck,
438
- Percent,
439
- Tag,
440
- Tags,
441
- Camera,
442
- UtensilsCrossed,
443
- Search,
444
- MapPin,
445
- Handshake,
446
- Contact,
447
- Building2,
448
- ChevronDown,
449
- Filter,
450
- Plus,
451
- Shield,
452
- List,
453
- ListChecks,
454
- FolderOpen,
455
- Dog,
456
- Cat,
457
- PawPrint,
458
- Heart,
459
- LayoutTemplate,
460
- LeafyGreen,
461
- Apple,
462
- Egg,
463
- Wheat,
464
- ArrowDownCircle,
465
- ArrowUpCircle,
466
- ArrowLeftRight,
467
- Plug,
468
- SlidersHorizontal,
469
- TableProperties,
470
- Landmark,
471
- Receipt,
472
- TrendingUp,
473
- CircleDollarSign,
474
- Clock,
475
- AlertTriangle,
476
- Sparkles,
477
- Wallet,
478
- Warehouse,
479
- Ruler,
480
- ArrowUpRight,
481
- ArrowDownRight,
482
- UserPlus,
483
- UserX,
484
- TreePalm,
485
- Boxes,
486
- Box,
487
- FileCheck2,
488
- PartyPopper,
489
- Radio,
490
- CalendarDays,
491
- BadgeDollarSign,
492
- Banknote,
493
- Layers,
494
- Music,
495
- Eye,
496
- ListMusic,
497
- Disc3,
498
- UsersRound,
499
- Mic,
500
- CalendarCheck2,
501
- CalendarX,
502
- ListPlus,
503
- Map,
504
- Ban,
505
- Clock3,
506
- Inbox,
507
- UserCheck,
508
- Star,
509
- Zap,
510
- Clapperboard,
511
- Newspaper,
512
- GraduationCap,
513
- Repeat
514
- };
515
- function getIcon(name) {
516
- return ICON_MAP2[name] ?? Home;
517
- }
518
- function NavDropdown({ item, currentPath, onNavigate }) {
519
- const [open, setOpen] = React7.useState(false);
520
- const Icon = getIcon(item.icon);
521
- const isChildActive = item.children?.some(
522
- (child) => currentPath === child.route || currentPath.startsWith(child.route + "/")
523
- );
524
- return /* @__PURE__ */ jsxs(Popover.Root, { open, onOpenChange: setOpen, children: [
525
- /* @__PURE__ */ jsx(Popover.Trigger, { asChild: true, children: /* @__PURE__ */ jsxs(
526
- "button",
527
- {
528
- "data-nav-active": isChildActive || void 0,
529
- className: cn(
530
- "flex items-center gap-1.5 whitespace-nowrap border-b-2 px-3 py-2 text-sm transition-colors",
531
- isChildActive ? "border-sidebar-accent-foreground font-semibold text-sidebar-foreground" : "border-transparent text-sidebar-muted hover:text-sidebar-foreground"
532
- ),
533
- children: [
534
- /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4" }),
535
- /* @__PURE__ */ jsx("span", { children: item.label }),
536
- /* @__PURE__ */ jsx(ChevronDown, { className: cn("h-3 w-3 transition-transform", open && "rotate-180") })
537
- ]
538
- }
539
- ) }),
540
- /* @__PURE__ */ jsx(Popover.Portal, { children: /* @__PURE__ */ jsx(
541
- Popover.Content,
542
- {
543
- align: "start",
544
- sideOffset: 4,
545
- className: "fayz-glass-surface z-50 min-w-[200px] rounded-xl border border-border bg-popover p-1.5 shadow-lg animate-in fade-in-0 zoom-in-95",
546
- children: item.children?.map((child) => {
547
- const ChildIcon = getIcon(child.icon);
548
- const isActive = currentPath === child.route || currentPath.startsWith(child.route + "/");
549
- return /* @__PURE__ */ jsxs(
550
- "button",
551
- {
552
- onClick: () => {
553
- onNavigate(child.route);
554
- setOpen(false);
555
- },
556
- className: cn(
557
- "flex w-full items-center gap-2.5 rounded-lg px-3 py-2 text-sm transition-colors",
558
- isActive ? "bg-muted font-medium text-foreground" : "text-popover-foreground hover:bg-muted/70"
559
- ),
560
- children: [
561
- /* @__PURE__ */ jsx(ChildIcon, { className: "h-4 w-4 text-muted-foreground" }),
562
- /* @__PURE__ */ jsx("span", { children: child.label })
563
- ]
564
- },
565
- child.id
566
- );
567
- })
568
- }
569
- ) })
570
- ] });
571
- }
572
- function Topbar({
573
- navigation,
574
- logo,
575
- user,
576
- onMenuClick,
577
- orgSwitcher,
578
- leftContent,
579
- rightContent,
580
- currentPath = "/",
581
- onNavigate,
582
- searchPlaceholder = "Search...",
583
- onSignOut,
584
- onProfile,
585
- onSettings,
586
- onBilling,
587
- userPlan,
588
- billingLabel,
589
- userMenuExtras
590
- }) {
591
- const setCommandPaletteOpen = useLayoutStore((s) => s.setCommandPaletteOpen);
592
- const mainNav = navigation.filter((item) => item.section === "main");
593
- const secondaryNav = navigation.filter((item) => item.section === "secondary");
594
- const allNav = [...mainNav, ...secondaryNav];
595
- const handleNavigate = (route) => {
596
- onNavigate?.(route);
597
- };
598
- return /* @__PURE__ */ jsxs("header", { "data-print": "hide", className: "sticky top-0 z-50 w-full shrink-0", children: [
599
- /* @__PURE__ */ jsx("div", { className: "border-b border-sidebar-border bg-sidebar text-sidebar-foreground", children: /* @__PURE__ */ jsxs("div", { className: "flex h-14 w-full items-center justify-between px-4 md:px-6", children: [
600
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
601
- onMenuClick && /* @__PURE__ */ jsx(
602
- "button",
603
- {
604
- onClick: onMenuClick,
605
- className: "inline-flex items-center justify-center rounded-md p-2 text-sidebar-foreground/70 hover:bg-sidebar/30 hover:text-sidebar-foreground md:hidden",
606
- "aria-label": "Open menu",
607
- children: /* @__PURE__ */ jsx(Menu, { className: "h-5 w-5" })
608
- }
609
- ),
610
- /* @__PURE__ */ jsx("div", { className: "hidden shrink-0 items-center md:flex", children: logo ?? /* @__PURE__ */ jsx("span", { className: "text-lg font-bold", children: "App" }) })
611
- ] }),
612
- /* @__PURE__ */ jsx("div", { className: "mx-8 hidden max-w-sm flex-1 md:flex", children: /* @__PURE__ */ jsxs(
613
- "button",
614
- {
615
- onClick: () => setCommandPaletteOpen(true),
616
- className: "relative flex h-8 w-full items-center rounded-md border border-sidebar-border bg-sidebar-accent px-3 pl-8 text-sm text-sidebar-muted shadow-sm transition-colors hover:border-sidebar-foreground/25 hover:bg-sidebar-foreground/10 hover:text-sidebar-foreground",
617
- children: [
618
- /* @__PURE__ */ jsx(Search, { className: "absolute left-2.5 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-sidebar-muted" }),
619
- /* @__PURE__ */ jsx("span", { className: "text-xs", children: searchPlaceholder }),
620
- /* @__PURE__ */ jsx("kbd", { className: "ml-auto hidden rounded border border-sidebar-border bg-sidebar-accent px-1.5 py-0.5 text-[10px] font-medium text-sidebar-muted sm:inline-block", children: "\u2318K" })
621
- ]
622
- }
623
- ) }),
624
- /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-center gap-1", children: [
625
- orgSwitcher && /* @__PURE__ */ jsx("div", { className: "hidden min-w-0 items-center md:flex", children: orgSwitcher }),
626
- leftContent,
627
- rightContent,
628
- /* @__PURE__ */ jsx(
629
- TopbarUserMenu,
630
- {
631
- user,
632
- userPlan,
633
- billingLabel,
634
- onSignOut,
635
- onProfile,
636
- onSettings,
637
- onBilling,
638
- userMenuExtras
639
- }
640
- )
641
- ] })
642
- ] }) }),
643
- /* @__PURE__ */ jsx("div", { className: "hidden md:block bg-sidebar", children: /* @__PURE__ */ jsx("div", { className: "flex h-11 w-full items-center px-4 md:px-6", children: /* @__PURE__ */ jsx("nav", { ref: (el) => {
644
- if (!el) return;
645
- const active = el.querySelector('[data-nav-active="true"]');
646
- if (active) {
647
- const left = active.offsetLeft - el.offsetWidth / 2 + active.offsetWidth / 2;
648
- el.scrollTo({ left: Math.max(0, left), behavior: "smooth" });
649
- }
650
- }, className: "flex items-center gap-0.5 overflow-x-auto scrollbar-none", children: allNav.map((item) => {
651
- if (item.children && item.children.length > 0) {
652
- return /* @__PURE__ */ jsx(
653
- NavDropdown,
654
- {
655
- item,
656
- currentPath,
657
- onNavigate: handleNavigate
658
- },
659
- item.id
660
- );
661
- }
662
- const Icon = getIcon(item.icon);
663
- const isActive = item.route === "/" ? currentPath === "/" : currentPath === item.route || currentPath.startsWith(item.route + "/");
664
- return /* @__PURE__ */ jsxs(
665
- "button",
666
- {
667
- "data-nav-active": isActive || void 0,
668
- onClick: () => handleNavigate(item.route),
669
- className: cn(
670
- "flex items-center gap-1.5 whitespace-nowrap border-b-2 px-3 py-2 text-sm transition-colors",
671
- isActive ? "border-primary font-semibold text-sidebar-foreground" : "border-transparent text-sidebar-muted hover:text-sidebar-foreground"
672
- ),
673
- children: [
674
- /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4" }),
675
- /* @__PURE__ */ jsx("span", { children: item.label }),
676
- item.premium && /* @__PURE__ */ jsx(Crown, { className: "h-3 w-3 text-amber-500", "aria-hidden": true }),
677
- item.badge !== void 0 && /* @__PURE__ */ jsx("span", { className: "rounded-full bg-primary px-1.5 py-0.5 text-[10px] font-semibold text-primary-foreground", children: item.badge })
678
- ]
679
- },
680
- item.id
681
- );
682
- }) }) }) })
683
- ] });
684
- }
685
357
  var GUTTER_PX = 8;
686
358
  var CLOSE_DELAY_MS = 140;
687
- var useIsomorphicLayoutEffect = typeof window !== "undefined" ? React7.useLayoutEffect : React7.useEffect;
359
+ var useIsomorphicLayoutEffect = typeof window !== "undefined" ? React2.useLayoutEffect : React2.useEffect;
688
360
  function iconOf(name) {
689
361
  if (!name) return null;
690
362
  return ICON_MAP[name] ?? null;
691
363
  }
692
- var RailFlyoutContext = React7.createContext(null);
364
+ var RailFlyoutContext = React2.createContext(null);
693
365
  var RailFlyoutProvider = RailFlyoutContext.Provider;
694
366
  function useRailRow(content) {
695
- const flyout = React7.useContext(RailFlyoutContext);
367
+ const flyout = React2.useContext(RailFlyoutContext);
696
368
  if (!flyout || !content) return {};
697
369
  return flyout.rowProps(content);
698
370
  }
@@ -702,19 +374,19 @@ function useRailFlyout({
702
374
  activeRoute,
703
375
  onNavigate
704
376
  }) {
705
- const [state, setState] = React7.useState(null);
706
- const [pos, setPos] = React7.useState({ top: 0, arrow: 20 });
707
- const [animate, setAnimate] = React7.useState(false);
708
- const panelRef = React7.useRef(null);
709
- const closeTimer = React7.useRef(null);
710
- const lastContent = React7.useRef(null);
711
- const cancelClose = React7.useCallback(() => {
377
+ const [state, setState] = React2.useState(null);
378
+ const [pos, setPos] = React2.useState({ top: 0, arrow: 20 });
379
+ const [animate, setAnimate] = React2.useState(false);
380
+ const panelRef = React2.useRef(null);
381
+ const closeTimer = React2.useRef(null);
382
+ const lastContent = React2.useRef(null);
383
+ const cancelClose = React2.useCallback(() => {
712
384
  if (closeTimer.current) {
713
385
  clearTimeout(closeTimer.current);
714
386
  closeTimer.current = null;
715
387
  }
716
388
  }, []);
717
- const show = React7.useCallback(
389
+ const show = React2.useCallback(
718
390
  (anchor, content2) => {
719
391
  if (!enabled) return;
720
392
  cancelClose();
@@ -722,19 +394,19 @@ function useRailFlyout({
722
394
  },
723
395
  [enabled, cancelClose]
724
396
  );
725
- const hide = React7.useCallback(() => {
397
+ const hide = React2.useCallback(() => {
726
398
  cancelClose();
727
399
  closeTimer.current = setTimeout(() => setState(null), CLOSE_DELAY_MS);
728
400
  }, [cancelClose]);
729
- const dismiss = React7.useCallback(() => {
401
+ const dismiss = React2.useCallback(() => {
730
402
  cancelClose();
731
403
  setState(null);
732
404
  }, [cancelClose]);
733
- React7.useEffect(() => () => cancelClose(), [cancelClose]);
734
- React7.useEffect(() => {
405
+ React2.useEffect(() => () => cancelClose(), [cancelClose]);
406
+ React2.useEffect(() => {
735
407
  if (!enabled) dismiss();
736
408
  }, [enabled, dismiss]);
737
- const reposition = React7.useCallback(() => {
409
+ const reposition = React2.useCallback(() => {
738
410
  const rail = railRef.current;
739
411
  const anchor = state?.anchor;
740
412
  if (!rail || !anchor) return;
@@ -749,7 +421,7 @@ function useRailFlyout({
749
421
  setPos({ top, arrow });
750
422
  }, [railRef, state]);
751
423
  useIsomorphicLayoutEffect(reposition, [reposition, state?.content.key]);
752
- React7.useEffect(() => {
424
+ React2.useEffect(() => {
753
425
  if (!state) return;
754
426
  const rail = railRef.current;
755
427
  const onScroll = () => reposition();
@@ -761,7 +433,7 @@ function useRailFlyout({
761
433
  };
762
434
  }, [state, railRef, reposition]);
763
435
  const visible = !!state;
764
- React7.useEffect(() => {
436
+ React2.useEffect(() => {
765
437
  if (!visible) {
766
438
  setAnimate(false);
767
439
  return;
@@ -769,7 +441,7 @@ function useRailFlyout({
769
441
  const id = requestAnimationFrame(() => setAnimate(true));
770
442
  return () => cancelAnimationFrame(id);
771
443
  }, [visible]);
772
- React7.useEffect(() => {
444
+ React2.useEffect(() => {
773
445
  if (!visible) return;
774
446
  const onKey = (event) => {
775
447
  if (event.key === "Escape") dismiss();
@@ -777,7 +449,7 @@ function useRailFlyout({
777
449
  window.addEventListener("keydown", onKey);
778
450
  return () => window.removeEventListener("keydown", onKey);
779
451
  }, [visible, dismiss]);
780
- const rowProps = React7.useCallback(
452
+ const rowProps = React2.useCallback(
781
453
  (content2) => ({
782
454
  onMouseEnter: (event) => show(event.currentTarget, content2),
783
455
  onFocus: (event) => show(event.currentTarget, content2),
@@ -894,8 +566,8 @@ function FlyoutBody({
894
566
  ] });
895
567
  }
896
568
  var RAIL_PUSH_MS = 260;
897
- var useIsomorphicLayoutEffect2 = typeof window !== "undefined" ? React7.useLayoutEffect : React7.useEffect;
898
- function getIcon2(name) {
569
+ var useIsomorphicLayoutEffect2 = typeof window !== "undefined" ? React2.useLayoutEffect : React2.useEffect;
570
+ function getIcon(name) {
899
571
  return ICON_MAP[name] ?? Home;
900
572
  }
901
573
  function shortcutKey() {
@@ -928,7 +600,7 @@ function NavItem({
928
600
  onNavigate,
929
601
  flyout
930
602
  }) {
931
- const Icon = getIcon2(item.icon);
603
+ const Icon = getIcon(item.icon);
932
604
  const isExternal = !!item.href;
933
605
  const hoverProps = collapsed && flyout ? flyout.rowProps(flyoutContentOf(item)) : void 0;
934
606
  const className = cn(
@@ -938,7 +610,7 @@ function NavItem({
938
610
  // as cramped rather than as dense.
939
611
  "flex w-full items-center gap-3 rounded-button px-2.5 py-2 text-[13px] font-medium transition-colors",
940
612
  "hover:bg-sidebar-accent/60 hover:text-sidebar-accent-foreground",
941
- isActive ? "bg-sidebar-accent font-semibold text-sidebar-accent-foreground shadow-sm" : "text-sidebar-foreground/80",
613
+ isActive ? "bg-sidebar-accent font-semibold text-sidebar-accent-foreground" : "text-sidebar-foreground/80",
942
614
  collapsed && "justify-center px-2"
943
615
  );
944
616
  const inner = /* @__PURE__ */ jsxs(Fragment, { children: [
@@ -994,8 +666,8 @@ function NavGroup({
994
666
  flyout
995
667
  }) {
996
668
  const isChildActive = item.children?.some((c) => c.route === activeRoute);
997
- const [open, setOpen] = React7.useState(() => isChildActive ?? false);
998
- const Icon = getIcon2(item.icon);
669
+ const [open, setOpen] = React2.useState(() => isChildActive ?? false);
670
+ const Icon = getIcon(item.icon);
999
671
  if (collapsed) {
1000
672
  const hoverProps = flyout?.rowProps(flyoutContentOf(item));
1001
673
  return /* @__PURE__ */ jsx(
@@ -1050,7 +722,7 @@ function NavGroup({
1050
722
  // along with the labels and land the arrowhead on top of the text.
1051
723
  childConnector ? "relative" : "ml-4 space-y-0.5 border-l border-sidebar-border/50 pl-3"
1052
724
  ), children: item.children?.map((child, childIndex) => {
1053
- const ChildIcon = getIcon2(child.icon);
725
+ const ChildIcon = getIcon(child.icon);
1054
726
  const isActive = child.route === activeRoute;
1055
727
  const childExternal = !!child.href;
1056
728
  const childClass = cn(
@@ -1068,7 +740,7 @@ function NavGroup({
1068
740
  child.premium && /* @__PURE__ */ jsx(Crown, { className: "h-3 w-3 shrink-0 text-amber-500", "aria-hidden": true })
1069
741
  ] });
1070
742
  const row = childExternal ? /* @__PURE__ */ jsx("a", { href: child.href, target: "_blank", rel: "noopener noreferrer", className: childClass, children: childInner }) : /* @__PURE__ */ jsx("button", { onClick: () => onNavigate(child.route), className: childClass, children: childInner });
1071
- if (!childConnector) return /* @__PURE__ */ jsx(React7.Fragment, { children: row }, child.id);
743
+ if (!childConnector) return /* @__PURE__ */ jsx(React2.Fragment, { children: row }, child.id);
1072
744
  const activeIndex = item.children.findIndex((c) => c.route === activeRoute);
1073
745
  const onPath = activeIndex >= 0 && childIndex < activeIndex;
1074
746
  return (
@@ -1130,9 +802,9 @@ function NavGroup({
1130
802
  ] });
1131
803
  }
1132
804
  function withCollapsed(slot, collapsed) {
1133
- if (!React7.isValidElement(slot)) return slot;
805
+ if (!React2.isValidElement(slot)) return slot;
1134
806
  if (typeof slot.type === "string") return slot;
1135
- return React7.cloneElement(slot, { collapsed });
807
+ return React2.cloneElement(slot, { collapsed });
1136
808
  }
1137
809
  function Sidebar({
1138
810
  navigation,
@@ -1144,6 +816,7 @@ function Sidebar({
1144
816
  user: _user,
1145
817
  orgSwitcher,
1146
818
  topContent,
819
+ identity,
1147
820
  navTop,
1148
821
  footerContent,
1149
822
  borderless,
@@ -1154,12 +827,13 @@ function Sidebar({
1154
827
  sectionLabels,
1155
828
  navKey,
1156
829
  navDirection = "forward",
1157
- navChildConnector
830
+ navChildConnector,
831
+ drawer
1158
832
  }) {
1159
833
  const currentPath = currentPathProp;
1160
834
  const setCommandPaletteOpen = useLayoutStore((s) => s.setCommandPaletteOpen);
1161
835
  const activeRoute = resolveActiveRoute(navigation, currentPath);
1162
- const railRef = React7.useRef(null);
836
+ const railRef = React2.useRef(null);
1163
837
  const flyout = useRailFlyout({
1164
838
  enabled: collapsed,
1165
839
  railRef,
@@ -1205,14 +879,13 @@ function Sidebar({
1205
879
  item.id
1206
880
  );
1207
881
  const navBody = /* @__PURE__ */ jsxs(Fragment, { children: [
1208
- navTop && /* @__PURE__ */ jsx("div", { className: "mb-2", children: withCollapsed(navTop, collapsed) }),
1209
882
  mainItems.length > 0 && renderSection(mainItems, sectionLabels?.main),
1210
883
  secondaryItems.length > 0 && /* @__PURE__ */ jsx("div", { className: "mt-6", children: renderSection(secondaryItems, sectionLabels?.secondary) })
1211
884
  ] });
1212
- const [outgoing, setOutgoing] = React7.useState(null);
1213
- const lastBodyRef = React7.useRef(null);
1214
- const lastKeyRef = React7.useRef(navKey);
1215
- const timerRef = React7.useRef(null);
885
+ const [outgoing, setOutgoing] = React2.useState(null);
886
+ const lastBodyRef = React2.useRef(null);
887
+ const lastKeyRef = React2.useRef(navKey);
888
+ const timerRef = React2.useRef(null);
1216
889
  useIsomorphicLayoutEffect2(() => {
1217
890
  if (lastKeyRef.current !== navKey) {
1218
891
  const previousBody = lastBodyRef.current;
@@ -1225,7 +898,7 @@ function Sidebar({
1225
898
  }
1226
899
  lastBodyRef.current = navBody;
1227
900
  });
1228
- React7.useEffect(() => () => {
901
+ React2.useEffect(() => () => {
1229
902
  if (timerRef.current) clearTimeout(timerRef.current);
1230
903
  }, []);
1231
904
  return (
@@ -1253,162 +926,446 @@ function Sidebar({
1253
926
  collapsed ? "w-16" : "w-56"
1254
927
  ),
1255
928
  children: [
1256
- /* @__PURE__ */ jsxs(
1257
- "div",
1258
- {
1259
- className: cn(
1260
- "flex h-14 items-center justify-between px-4",
1261
- !borderless && "border-b border-sidebar-border",
1262
- collapsed && "justify-center px-2"
1263
- ),
1264
- children: [
1265
- !collapsed && /* @__PURE__ */ jsxs(Fragment, { children: [
1266
- logo ?? /* @__PURE__ */ jsx("span", { className: "text-lg font-bold", children: "App" }),
1267
- /* @__PURE__ */ jsx(
929
+ /* @__PURE__ */ jsxs("div", { className: cn("shrink-0", !borderless && "border-b border-sidebar-border"), children: [
930
+ /* @__PURE__ */ jsxs(
931
+ "div",
932
+ {
933
+ className: cn(
934
+ "flex items-center gap-1 px-2",
935
+ // h-12 com a conta embaixo (48 + 44 + 8 = 100px, na régua de 4/8),
936
+ // h-14 quando a marca é a única coisa aqui e a linha tem de bater
937
+ // com o cabeçalho da página.
938
+ identity ? "h-12" : "h-14",
939
+ collapsed && "justify-center"
940
+ ),
941
+ children: [
942
+ topContent ? /* @__PURE__ */ jsx("div", { className: cn("min-w-0", collapsed ? "flex justify-center" : "flex-1"), children: withCollapsed(topContent, collapsed) }) : !collapsed && /* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1 px-1.5", children: logo ?? /* @__PURE__ */ jsx("span", { className: "text-lg font-bold", children: "App" }) }),
943
+ drawer && /* @__PURE__ */ jsx(
1268
944
  "button",
1269
945
  {
1270
946
  onClick: onToggle,
1271
- className: "ml-auto rounded-button p-1.5 text-sidebar-muted transition-colors hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",
1272
- "aria-label": "Collapse sidebar",
1273
- children: /* @__PURE__ */ jsx(PanelLeftClose, { className: "h-4 w-4" })
947
+ className: "fz-tap shrink-0 rounded-button p-1.5 text-sidebar-muted transition-colors hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",
948
+ "aria-label": "Close menu",
949
+ children: /* @__PURE__ */ jsx(X, { className: "h-4 w-4" })
1274
950
  }
1275
951
  )
1276
- ] }),
1277
- collapsed && /* @__PURE__ */ jsx(
952
+ ]
953
+ }
954
+ ),
955
+ identity && /* @__PURE__ */ jsx("div", { className: cn("px-2 pb-2", collapsed && "flex justify-center"), children: withCollapsed(identity, collapsed) })
956
+ ] }),
957
+ navTop ? /* @__PURE__ */ jsx("div", { className: "px-2 pb-2 pt-2", children: withCollapsed(navTop, collapsed) }) : (
958
+ /* Search — above the first destination. It used to be a 16px magnifier
959
+ tucked beside the avatar at the very bottom, which is where you put
960
+ something nobody is meant to find: the fastest way through an admin
961
+ with thirty screens was the one control shaped like an afterthought.
962
+ Here it reads as what it is — a field — and says which key opens it.
963
+ Clicking it opens the same palette ⌘K does; it is a button, not an
964
+ input, because typing happens there. */
965
+ /* @__PURE__ */ jsx("div", { className: "px-2 pb-2 pt-2", children: /* @__PURE__ */ jsxs(
966
+ "button",
967
+ {
968
+ onClick: () => setCommandPaletteOpen(true),
969
+ "aria-keyshortcuts": "Meta+K Control+K",
970
+ className: cn(
971
+ // Tonal surface, no outline. On a dark rail a 1px border is a hard
972
+ // edge competing with every other hard edge; a lighter fill of the
973
+ // same hue reads as "a field" without drawing a box around it — and
974
+ // leaves the outline treatment free to mean focus, which it must.
975
+ "fz-tap group flex items-center rounded-lg bg-sidebar-accent/40 text-sidebar-muted transition-colors",
976
+ "hover:bg-sidebar-accent/70 hover:text-sidebar-foreground",
977
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-sidebar-accent-foreground/50",
978
+ collapsed ? "h-9 w-9 justify-center" : "h-9 w-full gap-2.5 px-2.5"
979
+ ),
980
+ "aria-label": searchLabel,
981
+ title: collapsed ? searchLabel : void 0,
982
+ children: [
983
+ /* @__PURE__ */ jsx(Search, { className: "h-4 w-4 shrink-0" }),
984
+ !collapsed && /* @__PURE__ */ jsxs(Fragment, { children: [
985
+ /* @__PURE__ */ jsx("span", { className: "flex-1 truncate text-left text-[13px]", children: searchLabel }),
986
+ /* @__PURE__ */ jsxs("span", { className: "shrink-0 text-[11px] font-medium tabular-nums text-sidebar-muted/60 transition-opacity group-hover:opacity-100 md:opacity-70", children: [
987
+ shortcutKey(),
988
+ "K"
989
+ ] })
990
+ ] })
991
+ ]
992
+ }
993
+ ) })
994
+ ),
995
+ /* @__PURE__ */ jsxs(
996
+ "nav",
997
+ {
998
+ className: cn(
999
+ "relative flex-1 p-2",
1000
+ // scrollbar-none: the rail still scrolls, but without the native
1001
+ // scrollbar gutter eating into the nav items.
1002
+ outgoing ? "overflow-hidden" : "overflow-y-auto scrollbar-none"
1003
+ ),
1004
+ children: [
1005
+ outgoing && /* @__PURE__ */ jsx(
1006
+ "div",
1007
+ {
1008
+ "aria-hidden": true,
1009
+ className: cn(
1010
+ "pointer-events-none absolute inset-x-2 top-2 space-y-1",
1011
+ outgoing.direction === "back" ? "saas-rail-out-back" : "saas-rail-out-forward"
1012
+ ),
1013
+ children: outgoing.body
1014
+ }
1015
+ ),
1016
+ /* @__PURE__ */ jsx(
1017
+ "div",
1018
+ {
1019
+ className: cn(
1020
+ "space-y-1",
1021
+ outgoing && (outgoing.direction === "back" ? "saas-rail-in-back" : "saas-rail-in-forward")
1022
+ ),
1023
+ children: navBody
1024
+ },
1025
+ navKey
1026
+ )
1027
+ ]
1028
+ }
1029
+ ),
1030
+ settingsItems.length > 0 && /* @__PURE__ */ jsx("div", { className: cn("p-2 space-y-1", !borderless && "border-t border-sidebar-border"), children: settingsItems.map((item) => /* @__PURE__ */ jsx(
1031
+ NavItem,
1032
+ {
1033
+ item,
1034
+ collapsed,
1035
+ isActive: item.route === activeRoute,
1036
+ onNavigate: handleNavigate,
1037
+ flyout
1038
+ },
1039
+ item.id
1040
+ )) }),
1041
+ footerContent && // `empty:hidden` — slot children may all render null (e.g. the
1042
+ // SeuSiteWidget self-hides when the tenant has no project); without
1043
+ // it the padded/bordered wrapper shows as a blank block.
1044
+ /* @__PURE__ */ jsx("div", { className: cn("space-y-2 p-2 empty:hidden", !borderless && "border-t border-sidebar-border"), children: footerContent }),
1045
+ withCollapsed(orgSwitcher, collapsed),
1046
+ /* @__PURE__ */ jsx(
1047
+ "div",
1048
+ {
1049
+ className: cn("py-2 pr-2", !borderless && "border-t border-sidebar-border"),
1050
+ style: drawer ? { paddingBottom: "calc(0.5rem + env(safe-area-inset-bottom))" } : void 0,
1051
+ children: /* @__PURE__ */ jsxs("div", { className: cn(
1052
+ "flex items-center rounded-button",
1053
+ collapsed ? "flex-col gap-1" : "gap-1"
1054
+ ), children: [
1055
+ userMenuSlot,
1056
+ !collapsed && /* @__PURE__ */ jsx("div", { className: "flex-1" }),
1057
+ notificationSlot ?? /* @__PURE__ */ jsxs(
1058
+ "button",
1059
+ {
1060
+ className: "fz-tap relative inline-flex shrink-0 items-center justify-center rounded-button p-2 text-sidebar-muted transition-colors hover:bg-sidebar-accent/50 hover:text-sidebar-accent-foreground",
1061
+ "aria-label": unreadCount > 0 ? `${unreadCount} unread notifications` : "Notifications",
1062
+ children: [
1063
+ /* @__PURE__ */ jsx(Bell, { className: "h-4 w-4" }),
1064
+ unreadCount > 0 && /* @__PURE__ */ jsx("span", { className: "absolute right-1 top-1 h-2 w-2 rounded-full bg-destructive" })
1065
+ ]
1066
+ }
1067
+ ),
1068
+ !drawer && /* @__PURE__ */ jsx(
1278
1069
  "button",
1279
1070
  {
1280
1071
  onClick: onToggle,
1281
- className: "rounded-button p-1.5 text-sidebar-muted transition-colors hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",
1282
- "aria-label": "Expand sidebar",
1283
- children: /* @__PURE__ */ jsx(PanelLeft, { className: "h-4 w-4" })
1072
+ className: "fz-tap inline-flex shrink-0 items-center justify-center rounded-button p-2 text-sidebar-muted transition-colors hover:bg-sidebar-accent/50 hover:text-sidebar-accent-foreground",
1073
+ "aria-label": collapsed ? "Expand sidebar" : "Collapse sidebar",
1074
+ title: collapsed ? "Expand sidebar" : "Collapse sidebar",
1075
+ children: collapsed ? /* @__PURE__ */ jsx(PanelLeft, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx(PanelLeftClose, { className: "h-4 w-4" })
1284
1076
  }
1285
1077
  )
1286
- ]
1078
+ ] })
1287
1079
  }
1288
1080
  ),
1289
- topContent && /* @__PURE__ */ jsx("div", { className: "px-2 pt-2", children: withCollapsed(topContent, collapsed) }),
1290
- /* @__PURE__ */ jsx("div", { className: "px-2 pb-2 pt-2", children: /* @__PURE__ */ jsxs(
1081
+ flyout.panel
1082
+ ]
1083
+ }
1084
+ ) })
1085
+ );
1086
+ }
1087
+ function TopbarUserMenu({
1088
+ user,
1089
+ userPlan,
1090
+ billingLabel = "Subscription",
1091
+ onSignOut,
1092
+ onProfile,
1093
+ onSettings,
1094
+ onBilling,
1095
+ userMenuExtras
1096
+ }) {
1097
+ const [open, setOpen] = React2.useState(false);
1098
+ if (!user) return null;
1099
+ const initial = (user.fullName?.trim()?.[0] ?? user.email?.[0] ?? "?").toUpperCase();
1100
+ const item = (label, onClick, Icon) => onClick ? /* @__PURE__ */ jsxs(
1101
+ "button",
1102
+ {
1103
+ onClick: () => {
1104
+ setOpen(false);
1105
+ onClick();
1106
+ },
1107
+ className: "flex w-full items-center gap-2.5 rounded-lg px-3 py-2 text-sm text-popover-foreground transition-colors hover:bg-muted/70",
1108
+ children: [
1109
+ Icon && /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4 text-muted-foreground" }),
1110
+ /* @__PURE__ */ jsx("span", { children: label })
1111
+ ]
1112
+ },
1113
+ label
1114
+ ) : null;
1115
+ return /* @__PURE__ */ jsxs(Popover.Root, { open, onOpenChange: setOpen, children: [
1116
+ /* @__PURE__ */ jsx(Popover.Trigger, { asChild: true, children: /* @__PURE__ */ jsx(
1117
+ "button",
1118
+ {
1119
+ "aria-label": "User menu",
1120
+ className: "ml-1 flex h-9 w-9 items-center justify-center overflow-hidden rounded-full bg-primary text-xs font-semibold text-primary-foreground ring-2 ring-transparent transition hover:ring-sidebar-border",
1121
+ children: user.avatarUrl ? (
1122
+ // eslint-disable-next-line @next/next/no-img-element
1123
+ /* @__PURE__ */ jsx("img", { src: user.avatarUrl, alt: "", className: "h-full w-full object-cover" })
1124
+ ) : initial
1125
+ }
1126
+ ) }),
1127
+ /* @__PURE__ */ jsx(Popover.Portal, { children: /* @__PURE__ */ jsxs(
1128
+ Popover.Content,
1129
+ {
1130
+ align: "end",
1131
+ sideOffset: 8,
1132
+ className: "fayz-glass-surface z-50 min-w-[220px] rounded-xl border border-border bg-popover p-1.5 shadow-lg animate-in fade-in-0 zoom-in-95",
1133
+ children: [
1134
+ /* @__PURE__ */ jsxs("div", { className: "border-b border-border px-3 py-2", children: [
1135
+ /* @__PURE__ */ jsx("p", { className: "truncate text-sm font-medium text-foreground", children: user.fullName || user.email }),
1136
+ /* @__PURE__ */ jsx("p", { className: "truncate text-xs text-muted-foreground", children: user.email })
1137
+ ] }),
1138
+ /* @__PURE__ */ jsxs("div", { className: "mt-1 space-y-0.5", children: [
1139
+ item("Perfil", onProfile, UserCog),
1140
+ item("Configura\xE7\xF5es", onSettings, Settings),
1141
+ onBilling && /* @__PURE__ */ jsxs(
1142
+ "button",
1143
+ {
1144
+ onClick: () => {
1145
+ setOpen(false);
1146
+ onBilling();
1147
+ },
1148
+ className: "flex w-full items-center gap-2.5 rounded-lg px-3 py-2 text-sm text-popover-foreground transition-colors hover:bg-muted/70",
1149
+ children: [
1150
+ /* @__PURE__ */ jsx(CreditCard, { className: "h-4 w-4 text-muted-foreground" }),
1151
+ /* @__PURE__ */ jsx("span", { className: "flex-1 text-left", children: billingLabel }),
1152
+ userPlan && /* @__PURE__ */ jsxs(
1153
+ "span",
1154
+ {
1155
+ className: userPlan.paid ? "inline-flex items-center gap-1 rounded-full border border-amber-400/40 bg-amber-400/15 px-2 py-0.5 text-[10px] font-semibold text-amber-600 dark:text-amber-300" : "inline-flex items-center gap-1 rounded-full border border-border bg-muted px-2 py-0.5 text-[10px] font-medium text-muted-foreground",
1156
+ children: [
1157
+ userPlan.paid ? /* @__PURE__ */ jsx(Crown, { className: "h-3 w-3" }) : /* @__PURE__ */ jsx(Sparkles, { className: "h-3 w-3" }),
1158
+ userPlan.label
1159
+ ]
1160
+ }
1161
+ )
1162
+ ]
1163
+ },
1164
+ "billing"
1165
+ ),
1166
+ userMenuExtras?.map((e) => item(e.label, e.onClick)),
1167
+ onSignOut && /* @__PURE__ */ jsxs(Fragment, { children: [
1168
+ /* @__PURE__ */ jsx("div", { className: "my-1 border-t border-border" }),
1169
+ item("Sair", onSignOut, LogOut)
1170
+ ] })
1171
+ ] })
1172
+ ]
1173
+ }
1174
+ ) })
1175
+ ] });
1176
+ }
1177
+ function getIcon2(name) {
1178
+ return ICON_MAP[name] ?? Home;
1179
+ }
1180
+ function NavDropdown({ item, currentPath, onNavigate }) {
1181
+ const [open, setOpen] = React2.useState(false);
1182
+ const Icon = getIcon2(item.icon);
1183
+ const isChildActive = item.children?.some(
1184
+ (child) => currentPath === child.route || currentPath.startsWith(child.route + "/")
1185
+ );
1186
+ return /* @__PURE__ */ jsxs(Popover.Root, { open, onOpenChange: setOpen, children: [
1187
+ /* @__PURE__ */ jsx(Popover.Trigger, { asChild: true, children: /* @__PURE__ */ jsxs(
1188
+ "button",
1189
+ {
1190
+ "data-nav-active": isChildActive || void 0,
1191
+ className: cn(
1192
+ "flex items-center gap-1.5 whitespace-nowrap border-b-2 px-3 py-2 text-sm transition-colors",
1193
+ isChildActive ? "border-sidebar-accent-foreground font-semibold text-sidebar-foreground" : "border-transparent text-sidebar-muted hover:text-sidebar-foreground"
1194
+ ),
1195
+ children: [
1196
+ /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4" }),
1197
+ /* @__PURE__ */ jsx("span", { children: item.label }),
1198
+ /* @__PURE__ */ jsx(ChevronDown, { className: cn("h-3 w-3 transition-transform", open && "rotate-180") })
1199
+ ]
1200
+ }
1201
+ ) }),
1202
+ /* @__PURE__ */ jsx(Popover.Portal, { children: /* @__PURE__ */ jsx(
1203
+ Popover.Content,
1204
+ {
1205
+ align: "start",
1206
+ sideOffset: 4,
1207
+ className: "fayz-glass-surface z-50 min-w-[200px] rounded-xl border border-border bg-popover p-1.5 shadow-lg animate-in fade-in-0 zoom-in-95",
1208
+ children: item.children?.map((child) => {
1209
+ const ChildIcon = getIcon2(child.icon);
1210
+ const isActive = currentPath === child.route || currentPath.startsWith(child.route + "/");
1211
+ return /* @__PURE__ */ jsxs(
1291
1212
  "button",
1292
1213
  {
1293
- onClick: () => setCommandPaletteOpen(true),
1294
- "aria-keyshortcuts": "Meta+K Control+K",
1295
- className: cn(
1296
- // Tonal surface, no outline. On a dark rail a 1px border is a hard
1297
- // edge competing with every other hard edge; a lighter fill of the
1298
- // same hue reads as "a field" without drawing a box around it — and
1299
- // leaves the outline treatment free to mean focus, which it must.
1300
- "fz-tap group flex items-center rounded-lg bg-sidebar-accent/40 text-sidebar-muted transition-colors",
1301
- "hover:bg-sidebar-accent/70 hover:text-sidebar-foreground",
1302
- "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-sidebar-accent-foreground/50",
1303
- collapsed ? "h-9 w-9 justify-center" : "h-9 w-full gap-2.5 px-2.5"
1304
- ),
1305
- "aria-label": searchLabel,
1306
- title: collapsed ? searchLabel : void 0,
1307
- children: [
1308
- /* @__PURE__ */ jsx(Search, { className: "h-4 w-4 shrink-0" }),
1309
- !collapsed && /* @__PURE__ */ jsxs(Fragment, { children: [
1310
- /* @__PURE__ */ jsx("span", { className: "flex-1 truncate text-left text-[13px]", children: searchLabel }),
1311
- /* @__PURE__ */ jsxs("span", { className: "shrink-0 text-[11px] font-medium tabular-nums text-sidebar-muted/60 transition-opacity group-hover:opacity-100 md:opacity-70", children: [
1312
- shortcutKey(),
1313
- "K"
1314
- ] })
1315
- ] })
1316
- ]
1317
- }
1318
- ) }),
1319
- /* @__PURE__ */ jsxs(
1320
- "nav",
1321
- {
1214
+ onClick: () => {
1215
+ onNavigate(child.route);
1216
+ setOpen(false);
1217
+ },
1322
1218
  className: cn(
1323
- "relative flex-1 p-2",
1324
- // scrollbar-none: the rail still scrolls, but without the native
1325
- // scrollbar gutter eating into the nav items.
1326
- outgoing ? "overflow-hidden" : "overflow-y-auto scrollbar-none"
1219
+ "flex w-full items-center gap-2.5 rounded-lg px-3 py-2 text-sm transition-colors",
1220
+ isActive ? "bg-muted font-medium text-foreground" : "text-popover-foreground hover:bg-muted/70"
1327
1221
  ),
1328
1222
  children: [
1329
- outgoing && /* @__PURE__ */ jsx(
1330
- "div",
1331
- {
1332
- "aria-hidden": true,
1333
- className: cn(
1334
- "pointer-events-none absolute inset-x-2 top-2 space-y-1",
1335
- outgoing.direction === "back" ? "saas-rail-out-back" : "saas-rail-out-forward"
1336
- ),
1337
- children: outgoing.body
1338
- }
1339
- ),
1340
- /* @__PURE__ */ jsx(
1341
- "div",
1342
- {
1343
- className: cn(
1344
- "space-y-1",
1345
- outgoing && (outgoing.direction === "back" ? "saas-rail-in-back" : "saas-rail-in-forward")
1346
- ),
1347
- children: navBody
1348
- },
1349
- navKey
1350
- )
1223
+ /* @__PURE__ */ jsx(ChildIcon, { className: "h-4 w-4 text-muted-foreground" }),
1224
+ /* @__PURE__ */ jsx("span", { children: child.label })
1351
1225
  ]
1352
- }
1353
- ),
1354
- settingsItems.length > 0 && /* @__PURE__ */ jsx("div", { className: cn("p-2 space-y-1", !borderless && "border-t border-sidebar-border"), children: settingsItems.map((item) => /* @__PURE__ */ jsx(
1355
- NavItem,
1356
- {
1357
- item,
1358
- collapsed,
1359
- isActive: item.route === activeRoute,
1360
- onNavigate: handleNavigate,
1361
- flyout
1362
1226
  },
1363
- item.id
1364
- )) }),
1365
- footerContent && // `empty:hidden` — slot children may all render null (e.g. the
1366
- // SeuSiteWidget self-hides when the tenant has no project); without
1367
- // it the padded/bordered wrapper shows as a blank block.
1368
- /* @__PURE__ */ jsx("div", { className: cn("space-y-2 p-2 empty:hidden", !borderless && "border-t border-sidebar-border"), children: footerContent }),
1369
- withCollapsed(orgSwitcher, collapsed),
1370
- /* @__PURE__ */ jsx("div", { className: cn("p-2", !borderless && "border-t border-sidebar-border"), children: /* @__PURE__ */ jsxs("div", { className: cn(
1371
- "flex items-center rounded-button",
1372
- collapsed ? "flex-col gap-1" : "gap-1 px-1"
1373
- ), children: [
1374
- userMenuSlot,
1375
- !collapsed && /* @__PURE__ */ jsx("div", { className: "flex-1" }),
1376
- notificationSlot ?? /* @__PURE__ */ jsxs(
1377
- "button",
1378
- {
1379
- className: "fz-tap relative inline-flex shrink-0 items-center justify-center rounded-button p-2 text-sidebar-muted transition-colors hover:bg-sidebar-accent/50 hover:text-sidebar-accent-foreground",
1380
- "aria-label": unreadCount > 0 ? `${unreadCount} unread notifications` : "Notifications",
1381
- children: [
1382
- /* @__PURE__ */ jsx(Bell, { className: "h-4 w-4" }),
1383
- unreadCount > 0 && /* @__PURE__ */ jsx("span", { className: "absolute right-1 top-1 h-2 w-2 rounded-full bg-destructive" })
1384
- ]
1385
- }
1386
- )
1387
- ] }) }),
1388
- flyout.panel
1389
- ]
1227
+ child.id
1228
+ );
1229
+ })
1390
1230
  }
1391
1231
  ) })
1392
- );
1232
+ ] });
1233
+ }
1234
+ function Topbar({
1235
+ navigation,
1236
+ logo,
1237
+ user,
1238
+ onMenuClick,
1239
+ hideMobileChrome,
1240
+ orgSwitcher,
1241
+ leftContent,
1242
+ rightContent,
1243
+ currentPath = "/",
1244
+ onNavigate,
1245
+ searchPlaceholder = "Search...",
1246
+ onSignOut,
1247
+ onProfile,
1248
+ onSettings,
1249
+ onBilling,
1250
+ userPlan,
1251
+ billingLabel,
1252
+ userMenuExtras,
1253
+ userMenuSlot
1254
+ }) {
1255
+ const setCommandPaletteOpen = useLayoutStore((s) => s.setCommandPaletteOpen);
1256
+ const mainNav = navigation.filter((item) => item.section === "main");
1257
+ const secondaryNav = navigation.filter((item) => item.section === "secondary");
1258
+ const allNav = [...mainNav, ...secondaryNav];
1259
+ const handleNavigate = (route) => {
1260
+ onNavigate?.(route);
1261
+ };
1262
+ return /* @__PURE__ */ jsxs("header", { "data-print": "hide", className: "sticky top-0 z-50 w-full shrink-0", children: [
1263
+ /* @__PURE__ */ jsx("div", { className: cn("border-b border-sidebar-border bg-sidebar text-sidebar-foreground", hideMobileChrome && "hidden md:block"), children: /* @__PURE__ */ jsxs("div", { className: "flex h-14 w-full items-center justify-between px-4 md:px-6", children: [
1264
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-center gap-3", children: [
1265
+ onMenuClick && /* @__PURE__ */ jsx(
1266
+ "button",
1267
+ {
1268
+ onClick: onMenuClick,
1269
+ className: "inline-flex items-center justify-center rounded-md p-2 text-sidebar-foreground/70 hover:bg-sidebar/30 hover:text-sidebar-foreground md:hidden",
1270
+ "aria-label": "Open menu",
1271
+ children: /* @__PURE__ */ jsx(Menu, { className: "h-5 w-5" })
1272
+ }
1273
+ ),
1274
+ /* @__PURE__ */ jsx("div", { className: "hidden min-w-0 shrink-0 items-center md:flex", children: orgSwitcher ?? logo ?? /* @__PURE__ */ jsx("span", { className: "text-lg font-bold", children: "App" }) })
1275
+ ] }),
1276
+ /* @__PURE__ */ jsx("div", { className: "mx-8 hidden max-w-sm flex-1 md:flex", children: /* @__PURE__ */ jsxs(
1277
+ "button",
1278
+ {
1279
+ onClick: () => setCommandPaletteOpen(true),
1280
+ className: "relative flex h-8 w-full items-center rounded-md border border-sidebar-border bg-sidebar-accent px-3 pl-8 text-sm text-sidebar-muted shadow-sm transition-colors hover:border-sidebar-foreground/25 hover:bg-sidebar-foreground/10 hover:text-sidebar-foreground",
1281
+ children: [
1282
+ /* @__PURE__ */ jsx(Search, { className: "absolute left-2.5 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-sidebar-muted" }),
1283
+ /* @__PURE__ */ jsx("span", { className: "text-xs", children: searchPlaceholder }),
1284
+ /* @__PURE__ */ jsx("kbd", { className: "ml-auto hidden rounded border border-sidebar-border bg-sidebar-accent px-1.5 py-0.5 text-[10px] font-medium text-sidebar-muted sm:inline-block", children: "\u2318K" })
1285
+ ]
1286
+ }
1287
+ ) }),
1288
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-center gap-1", children: [
1289
+ leftContent,
1290
+ rightContent,
1291
+ userMenuSlot ?? /* @__PURE__ */ jsx(
1292
+ TopbarUserMenu,
1293
+ {
1294
+ user,
1295
+ userPlan,
1296
+ billingLabel,
1297
+ onSignOut,
1298
+ onProfile,
1299
+ onSettings,
1300
+ onBilling,
1301
+ userMenuExtras
1302
+ }
1303
+ )
1304
+ ] })
1305
+ ] }) }),
1306
+ /* @__PURE__ */ jsx("div", { className: "hidden md:block bg-sidebar", children: /* @__PURE__ */ jsx("div", { className: "flex h-11 w-full items-center px-4 md:px-6", children: /* @__PURE__ */ jsx("nav", { ref: (el) => {
1307
+ if (!el) return;
1308
+ const active = el.querySelector('[data-nav-active="true"]');
1309
+ if (active) {
1310
+ const left = active.offsetLeft - el.offsetWidth / 2 + active.offsetWidth / 2;
1311
+ el.scrollTo({ left: Math.max(0, left), behavior: "smooth" });
1312
+ }
1313
+ }, className: "flex items-center gap-0.5 overflow-x-auto scrollbar-none", children: allNav.map((item) => {
1314
+ if (item.children && item.children.length > 0) {
1315
+ return /* @__PURE__ */ jsx(
1316
+ NavDropdown,
1317
+ {
1318
+ item,
1319
+ currentPath,
1320
+ onNavigate: handleNavigate
1321
+ },
1322
+ item.id
1323
+ );
1324
+ }
1325
+ const Icon = getIcon2(item.icon);
1326
+ const isActive = item.route === "/" ? currentPath === "/" : currentPath === item.route || currentPath.startsWith(item.route + "/");
1327
+ return /* @__PURE__ */ jsxs(
1328
+ "button",
1329
+ {
1330
+ "data-nav-active": isActive || void 0,
1331
+ onClick: () => handleNavigate(item.route),
1332
+ className: cn(
1333
+ "flex items-center gap-1.5 whitespace-nowrap border-b-2 px-3 py-2 text-sm transition-colors",
1334
+ isActive ? "border-primary font-semibold text-sidebar-foreground" : "border-transparent text-sidebar-muted hover:text-sidebar-foreground"
1335
+ ),
1336
+ children: [
1337
+ /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4" }),
1338
+ /* @__PURE__ */ jsx("span", { children: item.label }),
1339
+ item.premium && /* @__PURE__ */ jsx(Crown, { className: "h-3 w-3 text-amber-500", "aria-hidden": true }),
1340
+ item.badge !== void 0 && /* @__PURE__ */ jsx("span", { className: "rounded-full bg-primary px-1.5 py-0.5 text-[10px] font-semibold text-primary-foreground", children: item.badge })
1341
+ ]
1342
+ },
1343
+ item.id
1344
+ );
1345
+ }) }) }) })
1346
+ ] });
1393
1347
  }
1394
- var ModuleHeaderSlotContext = React7.createContext(null);
1348
+ var ModuleHeaderSlotContext = React2.createContext(null);
1395
1349
  function useModuleHeaderSlot() {
1396
- return React7.useContext(ModuleHeaderSlotContext);
1350
+ return React2.useContext(ModuleHeaderSlotContext);
1397
1351
  }
1398
- var ModuleHeaderActionsSlotContext = React7.createContext(null);
1352
+ var ModuleHeaderActionsSlotContext = React2.createContext(null);
1399
1353
  function useModuleHeaderActionsSlot() {
1400
- return React7.useContext(ModuleHeaderActionsSlotContext);
1354
+ return React2.useContext(ModuleHeaderActionsSlotContext);
1401
1355
  }
1402
- var PageRailSlotContext = React7.createContext(null);
1356
+ var PageRailSlotContext = React2.createContext(null);
1403
1357
  function usePageRailSlot() {
1404
- return React7.useContext(PageRailSlotContext);
1358
+ return React2.useContext(PageRailSlotContext);
1359
+ }
1360
+ function bottomNavReachesAll(items) {
1361
+ return !!items?.some((i) => "kind" in i && i.kind === "more");
1405
1362
  }
1406
1363
  function ShellPageTitle({
1407
1364
  title,
1408
1365
  back
1409
1366
  }) {
1410
1367
  if (!title && !back) return null;
1411
- return /* @__PURE__ */ jsx(PageHeaderCrumb, { title, parentLabel: back?.label, onBack: back?.onBack });
1368
+ return /* @__PURE__ */ jsx(PageHeaderCrumb, { title, parentLabel: back?.label, onBack: back?.onBack, headingLevel: "h1" });
1412
1369
  }
1413
1370
  function SidebarLayout({
1414
1371
  navigation = [],
@@ -1423,6 +1380,7 @@ function SidebarLayout({
1423
1380
  userMenuExtras: _userMenuExtras,
1424
1381
  orgSwitcher,
1425
1382
  sidebarTopContent,
1383
+ sidebarIdentity,
1426
1384
  sidebarNavTop,
1427
1385
  sidebarFooterContent,
1428
1386
  frame,
@@ -1436,23 +1394,28 @@ function SidebarLayout({
1436
1394
  unreadCount = 0,
1437
1395
  searchLabel,
1438
1396
  hasBottomNav = false,
1397
+ barOwnsNav = false,
1439
1398
  mobileHeader = "minimal",
1440
1399
  sidebarSectionLabels,
1441
1400
  sidebarNavKey,
1442
1401
  sidebarNavDirection,
1443
- sidebarNavChildConnector
1402
+ sidebarNavChildConnector,
1403
+ railOpen
1444
1404
  }) {
1445
1405
  const sidebarCollapsed = useLayoutStore((s) => s.sidebarCollapsed);
1446
- const pageHeaderSlot = usePageHeaderSlot();
1406
+ const pageHeaderClaimed = usePageHeaderClaimed();
1407
+ const setTitleSlot = usePageTitleSlotHost();
1447
1408
  usePageHeaderHost();
1448
1409
  const setSidebarCollapsed = useLayoutStore((s) => s.setSidebarCollapsed);
1449
- const [mobileMenuOpen, setMobileMenuOpen] = React7.useState(false);
1450
- const [headerSlot, setHeaderSlot] = React7.useState(null);
1451
- const [actionsSlot, setActionsSlot] = React7.useState(null);
1452
- const [pageRailSlot, setPageRailSlot] = React7.useState(null);
1410
+ const mobileMenuOpen = useLayoutStore((st) => st.mobileDrawerOpen);
1411
+ const setMobileMenuOpen = useLayoutStore((st) => st.setMobileDrawerOpen);
1412
+ const [headerSlot, setHeaderSlot] = React2.useState(null);
1413
+ const [actionsSlot, setActionsSlot] = React2.useState(null);
1414
+ const [pageRailSlot, setPageRailSlot] = React2.useState(null);
1453
1415
  const hasPageActions = useHasPageActions();
1454
1416
  return /* @__PURE__ */ jsx(SaveBarProvider, { children: /* @__PURE__ */ jsx(ModuleHeaderSlotContext.Provider, { value: headerSlot, children: /* @__PURE__ */ jsx(ModuleHeaderActionsSlotContext.Provider, { value: actionsSlot, children: /* @__PURE__ */ jsx(PageRailSlotContext.Provider, { value: pageRailSlot, children: /* @__PURE__ */ jsxs("div", { className: cn(
1455
- "shell-enter flex h-screen overflow-hidden bg-background",
1417
+ "shell-enter fz-viewport-h flex overflow-hidden bg-background",
1418
+ hasBottomNav && "fz-has-bottom-bar",
1456
1419
  // Frame mode: the inset gap around the framed card takes the sidebar (menu)
1457
1420
  // color, so the white card reads as a panel floating on the same tone as the
1458
1421
  // left menu. The card's own md:m-2 margin reveals this behind it.
@@ -1474,6 +1437,7 @@ function SidebarLayout({
1474
1437
  onBilling,
1475
1438
  orgSwitcher,
1476
1439
  topContent: sidebarTopContent,
1440
+ identity: sidebarIdentity,
1477
1441
  navTop: sidebarNavTop,
1478
1442
  footerContent: sidebarFooterContent,
1479
1443
  borderless: frame,
@@ -1492,7 +1456,13 @@ function SidebarLayout({
1492
1456
  // `content`, not `card`: this is the page's own surface, and cards have
1493
1457
  // to sit ON it. Painted card, the frame was the same tone as everything
1494
1458
  // inside it and the page read as one flat sheet.
1495
- frame && "md:m-2 md:rounded-xl md:border md:border-border md:bg-content"
1459
+ frame && "md:my-2 md:ml-2 md:rounded-xl md:border md:border-border md:bg-content",
1460
+ // With a companion rail open, the page and the rail are ONE card: the
1461
+ // right edge opens and the rail closes it. Two cards with a strip of
1462
+ // chrome between them made the assistant read as a separate window
1463
+ // parked next to the app rather than as a column of it — the rail's own
1464
+ // left border is then the single seam between the two.
1465
+ frame && (railOpen ? "md:rounded-r-none md:border-r-0" : "md:mr-2")
1496
1466
  ), children: [
1497
1467
  /* @__PURE__ */ jsx("div", { ref: setPageRailSlot, className: "flex shrink-0 empty:hidden" }),
1498
1468
  /* @__PURE__ */ jsxs("div", { className: "relative flex min-w-0 flex-1 flex-col overflow-hidden", children: [
@@ -1503,7 +1473,7 @@ function SidebarLayout({
1503
1473
  mobileHeader !== "minimal" && !hasPageActions && "hidden md:flex"
1504
1474
  ), children: [
1505
1475
  /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-3", children: [
1506
- !hasBottomNav && /* @__PURE__ */ jsx(
1476
+ !barOwnsNav && /* @__PURE__ */ jsx(
1507
1477
  "button",
1508
1478
  {
1509
1479
  onClick: () => setMobileMenuOpen(true),
@@ -1512,7 +1482,8 @@ function SidebarLayout({
1512
1482
  children: /* @__PURE__ */ jsx(Menu, { className: "h-5 w-5" })
1513
1483
  }
1514
1484
  ),
1515
- pageHeaderSlot ?? /* @__PURE__ */ jsx(ShellPageTitle, { title: pageTitle, back: pageBack }),
1485
+ !pageHeaderClaimed && /* @__PURE__ */ jsx(ShellPageTitle, { title: pageTitle, back: pageBack }),
1486
+ /* @__PURE__ */ jsx("div", { ref: setTitleSlot, className: "flex min-w-0 items-center empty:hidden" }),
1516
1487
  topbarStart,
1517
1488
  /* @__PURE__ */ jsx("div", { ref: setHeaderSlot, className: "flex min-w-0 flex-1 items-center" })
1518
1489
  ] }),
@@ -1539,6 +1510,7 @@ function SidebarLayout({
1539
1510
  onBilling,
1540
1511
  orgSwitcher,
1541
1512
  topContent: sidebarTopContent,
1513
+ identity: sidebarIdentity,
1542
1514
  navTop: sidebarNavTop,
1543
1515
  footerContent: sidebarFooterContent,
1544
1516
  userMenuSlot,
@@ -1563,6 +1535,8 @@ function TopbarLayout({
1563
1535
  userPlan,
1564
1536
  billingLabel,
1565
1537
  userMenuExtras,
1538
+ userMenuSlot,
1539
+ drawerUserMenuSlot,
1566
1540
  orgSwitcher,
1567
1541
  frame,
1568
1542
  currentPath,
@@ -1571,19 +1545,23 @@ function TopbarLayout({
1571
1545
  pageTitle,
1572
1546
  pageBack,
1573
1547
  notificationSlot,
1574
- hasBottomNav = false
1548
+ unreadCount = 0,
1549
+ hasBottomNav = false,
1550
+ barOwnsNav = false
1575
1551
  }) {
1576
- const [mobileMenuOpen, setMobileMenuOpen] = React7.useState(false);
1577
- const [headerSlot, setHeaderSlot] = React7.useState(null);
1578
- const [actionsSlot, setActionsSlot] = React7.useState(null);
1579
- const [pageRailSlot, setPageRailSlot] = React7.useState(null);
1580
- const rawSlot = usePageHeaderSlot();
1552
+ const mobileMenuOpen = useLayoutStore((st) => st.mobileDrawerOpen);
1553
+ const setMobileMenuOpen = useLayoutStore((st) => st.setMobileDrawerOpen);
1554
+ const [headerSlot, setHeaderSlot] = React2.useState(null);
1555
+ const [actionsSlot, setActionsSlot] = React2.useState(null);
1556
+ const [pageRailSlot, setPageRailSlot] = React2.useState(null);
1557
+ const rawClaimed = usePageHeaderClaimed();
1558
+ const setTitleSlot = usePageTitleSlotHost();
1581
1559
  const hasOutlet = useHasPageHeaderOutlet();
1582
1560
  const hasPageActions = useHasPageActions();
1583
1561
  usePageHeaderHost();
1584
- const pageHeaderSlot = hasOutlet ? null : rawSlot;
1562
+ const pageHeaderClaimed = hasOutlet ? false : rawClaimed;
1585
1563
  const showTitle = !hasOutlet && !!pageTitle;
1586
- return /* @__PURE__ */ jsx(SaveBarProvider, { children: /* @__PURE__ */ jsx(ModuleHeaderSlotContext.Provider, { value: headerSlot, children: /* @__PURE__ */ jsx(ModuleHeaderActionsSlotContext.Provider, { value: actionsSlot, children: /* @__PURE__ */ jsx(PageRailSlotContext.Provider, { value: pageRailSlot, children: /* @__PURE__ */ jsxs("div", { className: "shell-enter relative flex h-screen flex-col overflow-hidden bg-background", children: [
1564
+ return /* @__PURE__ */ jsx(SaveBarProvider, { children: /* @__PURE__ */ jsx(ModuleHeaderSlotContext.Provider, { value: headerSlot, children: /* @__PURE__ */ jsx(ModuleHeaderActionsSlotContext.Provider, { value: actionsSlot, children: /* @__PURE__ */ jsx(PageRailSlotContext.Provider, { value: pageRailSlot, children: /* @__PURE__ */ jsxs("div", { className: cn("shell-enter fz-viewport-h relative flex flex-col overflow-hidden bg-background", hasBottomNav && "fz-has-bottom-bar"), children: [
1587
1565
  /* @__PURE__ */ jsx(
1588
1566
  Topbar,
1589
1567
  {
@@ -1599,25 +1577,37 @@ function TopbarLayout({
1599
1577
  userPlan,
1600
1578
  billingLabel,
1601
1579
  userMenuExtras,
1580
+ userMenuSlot,
1602
1581
  orgSwitcher,
1603
1582
  leftContent: topbarStart,
1604
1583
  rightContent: topbarEnd || notificationSlot ? /* @__PURE__ */ jsxs(Fragment, { children: [
1605
1584
  topbarEnd,
1606
1585
  notificationSlot
1607
1586
  ] }) : void 0,
1608
- onMenuClick: hasBottomNav ? void 0 : () => setMobileMenuOpen(true)
1587
+ onMenuClick: barOwnsNav ? void 0 : () => setMobileMenuOpen(true),
1588
+ hideMobileChrome: barOwnsNav
1609
1589
  }
1610
1590
  ),
1611
1591
  /* @__PURE__ */ jsx("main", { className: cn("flex min-w-0 min-h-0 flex-1 flex-col overflow-hidden", frame && "md:bg-sidebar md:p-2"), children: /* @__PURE__ */ jsxs("div", { className: cn("flex min-w-0 min-h-0 flex-1 overflow-hidden", frame && "md:rounded-xl md:border md:border-border md:bg-content"), children: [
1612
1592
  /* @__PURE__ */ jsx("div", { ref: setPageRailSlot, className: "flex shrink-0 empty:hidden" }),
1613
1593
  /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 min-h-0 flex-1 flex-col overflow-hidden", children: [
1614
- (pageHeaderSlot || pageBack || hasPageActions) && /* @__PURE__ */ jsxs("div", { className: "flex min-h-12 shrink-0 items-center justify-between gap-3 border-b border-border px-4 py-2", children: [
1615
- /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-3", children: [
1616
- pageHeaderSlot ?? /* @__PURE__ */ jsx(ShellPageTitle, { title: showTitle ? pageTitle : void 0, back: pageBack }),
1617
- /* @__PURE__ */ jsx("div", { ref: setHeaderSlot, className: "flex min-w-0 flex-1 items-center" })
1618
- ] }),
1619
- /* @__PURE__ */ jsx("div", { ref: setActionsSlot, className: "flex shrink-0 items-center gap-2" })
1620
- ] }),
1594
+ (pageHeaderClaimed || pageBack || hasPageActions || barOwnsNav) && /* @__PURE__ */ jsxs(
1595
+ "div",
1596
+ {
1597
+ className: cn(
1598
+ "flex min-h-12 shrink-0 items-center justify-between gap-3 border-b border-border px-4 py-2",
1599
+ !(pageHeaderClaimed || pageBack || hasPageActions) && "md:hidden"
1600
+ ),
1601
+ children: [
1602
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-3", children: [
1603
+ !pageHeaderClaimed && /* @__PURE__ */ jsx(ShellPageTitle, { title: showTitle ? pageTitle : void 0, back: pageBack }),
1604
+ /* @__PURE__ */ jsx("div", { ref: setTitleSlot, className: "flex min-w-0 items-center empty:hidden" }),
1605
+ /* @__PURE__ */ jsx("div", { ref: setHeaderSlot, className: "flex min-w-0 flex-1 items-center" })
1606
+ ] }),
1607
+ /* @__PURE__ */ jsx("div", { ref: setActionsSlot, className: "flex shrink-0 items-center gap-2" })
1608
+ ]
1609
+ }
1610
+ ),
1621
1611
  /* @__PURE__ */ jsx("div", { className: "min-h-0 flex-1 overflow-y-auto pb-16 md:pb-0", children })
1622
1612
  ] })
1623
1613
  ] }) }),
@@ -1635,7 +1625,9 @@ function TopbarLayout({
1635
1625
  onSettings,
1636
1626
  onBilling,
1637
1627
  orgSwitcher,
1638
- userMenuSlot: user ? /* @__PURE__ */ jsx(DrawerUserRow, { user }) : void 0,
1628
+ userMenuSlot: drawerUserMenuSlot ?? (user ? /* @__PURE__ */ jsx(DrawerUserRow, { user }) : void 0),
1629
+ notificationSlot,
1630
+ unreadCount,
1639
1631
  onClose: () => setMobileMenuOpen(false)
1640
1632
  }
1641
1633
  )
@@ -1652,9 +1644,10 @@ function MinimalLayout({
1652
1644
  logo,
1653
1645
  user: _user,
1654
1646
  children,
1655
- headerEnd
1647
+ headerEnd,
1648
+ hasBottomNav = false
1656
1649
  }) {
1657
- return /* @__PURE__ */ jsxs("div", { className: "shell-enter flex h-screen flex-col", children: [
1650
+ return /* @__PURE__ */ jsxs("div", { className: cn("shell-enter fz-viewport-h flex flex-col", hasBottomNav && "fz-has-bottom-bar"), children: [
1658
1651
  /* @__PURE__ */ jsxs("header", { className: "flex h-14 shrink-0 items-center justify-between border-b border-border bg-card px-4 md:px-6", children: [
1659
1652
  /* @__PURE__ */ jsx("div", { className: "flex items-center gap-3", children: logo ?? /* @__PURE__ */ jsx("span", { className: "text-lg font-bold", children: "App" }) }),
1660
1653
  headerEnd && /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", children: headerEnd })
@@ -1663,23 +1656,23 @@ function MinimalLayout({
1663
1656
  ] });
1664
1657
  }
1665
1658
  function MobileOverlay({ onClose, onNavigate, ...sidebarProps }) {
1666
- const [visible, setVisible] = React7.useState(false);
1667
- React7.useEffect(() => {
1659
+ const [visible, setVisible] = React2.useState(false);
1660
+ React2.useEffect(() => {
1668
1661
  const id = requestAnimationFrame(() => setVisible(true));
1669
1662
  return () => cancelAnimationFrame(id);
1670
1663
  }, []);
1671
- const close = React7.useCallback(() => {
1664
+ const close = React2.useCallback(() => {
1672
1665
  setVisible(false);
1673
1666
  window.setTimeout(onClose, 250);
1674
1667
  }, [onClose]);
1675
- React7.useEffect(() => {
1668
+ React2.useEffect(() => {
1676
1669
  const previous = document.body.style.overflow;
1677
1670
  document.body.style.overflow = "hidden";
1678
1671
  return () => {
1679
1672
  document.body.style.overflow = previous;
1680
1673
  };
1681
1674
  }, []);
1682
- React7.useEffect(() => {
1675
+ React2.useEffect(() => {
1683
1676
  const onKey = (e) => {
1684
1677
  if (e.key === "Escape") close();
1685
1678
  };
@@ -1710,6 +1703,7 @@ function MobileOverlay({ onClose, onNavigate, ...sidebarProps }) {
1710
1703
  ...sidebarProps,
1711
1704
  collapsed: false,
1712
1705
  onToggle: close,
1706
+ drawer: true,
1713
1707
  borderless: true,
1714
1708
  onNavigate: (route) => {
1715
1709
  onNavigate?.(route);
@@ -1721,19 +1715,7 @@ function MobileOverlay({ onClose, onNavigate, ...sidebarProps }) {
1721
1715
  )
1722
1716
  ] });
1723
1717
  }
1724
- var BOTTOM_NAV_ICONS = {
1725
- Home: Home,
1726
- Receipt: Receipt,
1727
- ArrowLeftRight: ArrowLeftRight,
1728
- Calendar: Calendar,
1729
- CreditCard: CreditCard,
1730
- Settings: Settings,
1731
- Menu: Menu,
1732
- MessageCircle: MessageCircle,
1733
- Wallet: Wallet,
1734
- PiggyBank,
1735
- Plus: Plus
1736
- };
1718
+ var bottomNavIcon = (name, fallback = Home) => (name ? ICON_MAP[name] : void 0) ?? fallback;
1737
1719
  function FloatingProfileAvatar({ user, onClick }) {
1738
1720
  if (!user) return null;
1739
1721
  const label = user.fullName || user.email;
@@ -1759,43 +1741,80 @@ function MobileBottomNav({
1759
1741
  items,
1760
1742
  currentPath,
1761
1743
  onNavigate,
1762
- onAction
1744
+ onAction,
1745
+ onMore,
1746
+ onAssistant,
1747
+ assistantOpen,
1748
+ moreOpen,
1749
+ retracted
1763
1750
  }) {
1764
1751
  const path = currentPath ?? "";
1765
- return /* @__PURE__ */ jsx("nav", { className: "fixed inset-x-0 bottom-0 z-50 flex h-16 items-center justify-around border-t border-border bg-background md:hidden", children: items.map((item) => {
1766
- if (item.kind === "action") {
1767
- const ActionIcon = BOTTOM_NAV_ICONS[item.icon] ?? Plus;
1768
- return /* @__PURE__ */ jsx("div", { className: "flex flex-1 items-start justify-center", children: /* @__PURE__ */ jsx(
1769
- "button",
1770
- {
1771
- type: "button",
1772
- onClick: () => onAction?.(item.id),
1773
- "aria-label": item.label ?? "Add",
1774
- className: "-mt-6 flex h-14 w-14 items-center justify-center rounded-full bg-primary text-primary-foreground shadow-lg shadow-primary/30 ring-4 ring-background transition-transform active:scale-95",
1775
- children: /* @__PURE__ */ jsx(ActionIcon, { className: "h-6 w-6" })
1752
+ return /* @__PURE__ */ jsx(
1753
+ "nav",
1754
+ {
1755
+ "aria-label": "Navega\xE7\xE3o principal",
1756
+ "aria-hidden": retracted || void 0,
1757
+ className: cn(
1758
+ "fz-safe-b fixed inset-x-0 bottom-0 z-50 flex min-h-16 items-stretch justify-around border-t border-border bg-background md:hidden",
1759
+ "motion-safe:transition-transform motion-safe:duration-200 motion-safe:ease-out",
1760
+ retracted && "pointer-events-none translate-y-full"
1761
+ ),
1762
+ children: items.map((item, i) => {
1763
+ if ("kind" in item && item.kind === "action") {
1764
+ const ActionIcon = bottomNavIcon(item.icon, Plus);
1765
+ return /* @__PURE__ */ jsx("div", { className: "flex flex-1 items-start justify-center", children: /* @__PURE__ */ jsx(
1766
+ "button",
1767
+ {
1768
+ type: "button",
1769
+ onClick: () => onAction?.(item.id),
1770
+ "aria-label": item.label ?? "Add",
1771
+ className: "-mt-6 flex h-14 w-14 items-center justify-center rounded-full bg-primary text-primary-foreground shadow-lg shadow-primary/30 ring-4 ring-background transition-transform active:scale-95",
1772
+ children: /* @__PURE__ */ jsx(ActionIcon, { className: "h-6 w-6" })
1773
+ }
1774
+ ) }, item.id);
1776
1775
  }
1777
- ) }, item.id);
1776
+ const isMore = "kind" in item && item.kind === "more";
1777
+ const isAssistant = "kind" in item && item.kind === "assistant";
1778
+ const label = isMore ? item.label ?? "Mais" : isAssistant ? item.label ?? "IA" : item.label;
1779
+ const Icon = bottomNavIcon(
1780
+ item.icon,
1781
+ isMore ? Menu : isAssistant ? Sparkles : Home
1782
+ );
1783
+ const route = isMore || isAssistant ? void 0 : item.route;
1784
+ const isActive = isMore ? !!moreOpen : isAssistant ? !!assistantOpen : route === "/" ? path === "/" || path === "" : !!route && (path === route || path.startsWith(route + "/"));
1785
+ return /* @__PURE__ */ jsxs(
1786
+ "button",
1787
+ {
1788
+ type: "button",
1789
+ onClick: () => isMore ? onMore?.() : isAssistant ? onAssistant?.() : onNavigate?.(route),
1790
+ "aria-current": isActive && route ? "page" : void 0,
1791
+ "aria-expanded": isMore || isAssistant ? isActive : void 0,
1792
+ className: cn(
1793
+ // min-h-[44px] is the floor a finger needs; the bar was letting
1794
+ // content decide and landing around 40.
1795
+ "relative flex min-h-[44px] flex-1 flex-col items-center justify-center gap-0.5 px-1 py-2 transition-colors",
1796
+ isActive ? "text-primary" : "text-muted-foreground"
1797
+ ),
1798
+ children: [
1799
+ /* @__PURE__ */ jsx(
1800
+ "span",
1801
+ {
1802
+ "aria-hidden": true,
1803
+ className: cn(
1804
+ "absolute inset-x-3 top-0 h-0.5 rounded-full transition-opacity",
1805
+ isActive ? "bg-primary opacity-100" : "opacity-0"
1806
+ )
1807
+ }
1808
+ ),
1809
+ /* @__PURE__ */ jsx(Icon, { className: "h-5 w-5 shrink-0" }),
1810
+ /* @__PURE__ */ jsx("span", { className: "max-w-full truncate text-[11px] leading-tight", children: label })
1811
+ ]
1812
+ },
1813
+ isMore ? "more" : isAssistant ? "assistant" : `${route}-${i}`
1814
+ );
1815
+ })
1778
1816
  }
1779
- const isActive = item.route === "/" ? path === "/" || path === "" : path === item.route || path.startsWith(item.route + "/");
1780
- const Icon = BOTTOM_NAV_ICONS[item.icon] ?? Home;
1781
- return /* @__PURE__ */ jsxs(
1782
- "button",
1783
- {
1784
- type: "button",
1785
- onClick: () => onNavigate?.(item.route),
1786
- className: cn(
1787
- "flex flex-1 flex-col items-center justify-center gap-0.5 px-1 py-1 transition-colors",
1788
- isActive ? "text-primary" : "text-muted-foreground"
1789
- ),
1790
- "aria-current": isActive ? "page" : void 0,
1791
- children: [
1792
- /* @__PURE__ */ jsx(Icon, { className: "h-5 w-5" }),
1793
- /* @__PURE__ */ jsx("span", { className: "text-xs", children: item.label })
1794
- ]
1795
- },
1796
- item.route
1797
- );
1798
- }) });
1817
+ );
1799
1818
  }
1800
1819
  function AppShell({
1801
1820
  variant,
@@ -1819,12 +1838,16 @@ function AppShell({
1819
1838
  topbarStart,
1820
1839
  topbarEnd,
1821
1840
  sidebarTopContent,
1841
+ sidebarIdentity,
1822
1842
  sidebarNavTop,
1823
1843
  sidebarFooterContent,
1824
1844
  bottomNav,
1825
1845
  onBottomNavAction,
1846
+ onAssistantToggle,
1847
+ assistantOpen,
1826
1848
  mobileHeader = "minimal",
1827
1849
  userMenuSlot,
1850
+ drawerUserMenuSlot,
1828
1851
  notificationSlot,
1829
1852
  unreadCount = 0,
1830
1853
  searchLabel,
@@ -1841,13 +1864,26 @@ function AppShell({
1841
1864
  rightRailResizeLabel
1842
1865
  }) {
1843
1866
  const hasBottomNav = !!bottomNav?.length;
1867
+ const barOwnsNav = bottomNavReachesAll(bottomNav);
1868
+ const pageClaimedBack = useHasPageHeaderBack();
1869
+ const barRetracted = !!pageBack || pageClaimedBack;
1870
+ const mobileDrawerOpen = useLayoutStore((st) => st.mobileDrawerOpen);
1871
+ const setMobileDrawerOpen = useLayoutStore((st) => st.setMobileDrawerOpen);
1872
+ React2.useEffect(() => {
1873
+ setMobileDrawerOpen(false);
1874
+ }, [currentPath]);
1844
1875
  const bottomBar = hasBottomNav ? /* @__PURE__ */ jsx(
1845
1876
  MobileBottomNav,
1846
1877
  {
1847
1878
  items: bottomNav,
1848
1879
  currentPath,
1849
1880
  onNavigate,
1850
- onAction: onBottomNavAction
1881
+ onAction: onBottomNavAction,
1882
+ onMore: () => setMobileDrawerOpen(!mobileDrawerOpen),
1883
+ moreOpen: mobileDrawerOpen,
1884
+ onAssistant: onAssistantToggle,
1885
+ assistantOpen,
1886
+ retracted: barRetracted
1851
1887
  }
1852
1888
  ) : null;
1853
1889
  const floatingAvatar = mobileHeader === "transparent" && user ? /* @__PURE__ */ jsx(FloatingProfileAvatar, { user, onClick: onProfile }) : null;
@@ -1868,6 +1904,7 @@ function AppShell({
1868
1904
  userMenuExtras,
1869
1905
  orgSwitcher,
1870
1906
  sidebarTopContent,
1907
+ sidebarIdentity,
1871
1908
  sidebarNavTop,
1872
1909
  sidebarFooterContent,
1873
1910
  frame: contentFrame,
@@ -1880,12 +1917,14 @@ function AppShell({
1880
1917
  notificationSlot,
1881
1918
  unreadCount,
1882
1919
  searchLabel,
1883
- hasBottomNav,
1920
+ hasBottomNav: hasBottomNav && !barRetracted,
1921
+ barOwnsNav,
1884
1922
  mobileHeader,
1885
1923
  sidebarSectionLabels,
1886
1924
  sidebarNavKey,
1887
1925
  sidebarNavDirection,
1888
1926
  sidebarNavChildConnector,
1927
+ railOpen: !!rightRail && rightRailOpen,
1889
1928
  children
1890
1929
  }
1891
1930
  );
@@ -1905,6 +1944,8 @@ function AppShell({
1905
1944
  userPlan,
1906
1945
  billingLabel,
1907
1946
  userMenuExtras,
1947
+ userMenuSlot,
1948
+ drawerUserMenuSlot,
1908
1949
  orgSwitcher,
1909
1950
  frame: contentFrame,
1910
1951
  currentPath,
@@ -1913,13 +1954,14 @@ function AppShell({
1913
1954
  pageTitle,
1914
1955
  pageBack,
1915
1956
  notificationSlot,
1916
- hasBottomNav,
1957
+ hasBottomNav: hasBottomNav && !barRetracted,
1958
+ barOwnsNav,
1917
1959
  children
1918
1960
  }
1919
1961
  );
1920
1962
  break;
1921
1963
  case "minimal":
1922
- layoutElement = /* @__PURE__ */ jsx(MinimalLayout, { logo, user, headerEnd: topbarEnd, children });
1964
+ layoutElement = /* @__PURE__ */ jsx(MinimalLayout, { logo, user, headerEnd: topbarEnd, hasBottomNav: hasBottomNav && !barRetracted, children });
1923
1965
  break;
1924
1966
  default: {
1925
1967
  return null;
@@ -1955,13 +1997,13 @@ function SplitWithRightRail({
1955
1997
  maxWidth = 720,
1956
1998
  resizeLabel = "Resize panel"
1957
1999
  }) {
1958
- const [width, setWidth] = React7.useState(defaultWidth);
1959
- const [dragging, setDragging] = React7.useState(false);
1960
- const clamp = React7.useCallback(
2000
+ const [width, setWidth] = React2.useState(defaultWidth);
2001
+ const [dragging, setDragging] = React2.useState(false);
2002
+ const clamp = React2.useCallback(
1961
2003
  (value) => Math.min(maxWidth, Math.max(minWidth, value)),
1962
2004
  [minWidth, maxWidth]
1963
2005
  );
1964
- React7.useEffect(() => {
2006
+ React2.useEffect(() => {
1965
2007
  if (!dragging) return;
1966
2008
  const onMove = (event) => {
1967
2009
  event.preventDefault();
@@ -1983,7 +2025,7 @@ function SplitWithRightRail({
1983
2025
  document.body.style.cursor = previousCursor;
1984
2026
  };
1985
2027
  }, [dragging, clamp]);
1986
- React7.useEffect(() => {
2028
+ React2.useEffect(() => {
1987
2029
  if (dragging) return;
1988
2030
  onWidthChange?.(width);
1989
2031
  }, [dragging]);
@@ -1993,7 +2035,7 @@ function SplitWithRightRail({
1993
2035
  // chrome, next to the menu — painting them page-white put a bright strip
1994
2036
  // between the dark chrome and the rail, which read as a seam rather than
1995
2037
  // as space.
1996
- /* @__PURE__ */ jsxs("div", { className: cn("flex h-screen w-full overflow-hidden bg-sidebar", !frame && "md:bg-background"), children: [
2038
+ /* @__PURE__ */ jsxs("div", { className: cn("fz-viewport-h flex w-full overflow-hidden bg-sidebar", !frame && "md:bg-background"), children: [
1997
2039
  /* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1", children }),
1998
2040
  /* @__PURE__ */ jsxs(
1999
2041
  "div",
@@ -2026,7 +2068,11 @@ function SplitWithRightRail({
2026
2068
  }
2027
2069
  },
2028
2070
  className: cn(
2029
- "group relative hidden w-1 shrink-0 cursor-col-resize focus-visible:outline-none",
2071
+ // Zero width: the divider USED to be a 4px gutter, which meant the
2072
+ // page and the rail could never touch — there was always a strip of
2073
+ // chrome between them. It now sits exactly on the seam the rail's own
2074
+ // left border draws, and lights that border up when you reach for it.
2075
+ "group relative hidden w-0 shrink-0 cursor-col-resize focus-visible:outline-none",
2030
2076
  open && "md:block"
2031
2077
  ),
2032
2078
  children: [
@@ -2034,7 +2080,7 @@ function SplitWithRightRail({
2034
2080
  "span",
2035
2081
  {
2036
2082
  className: cn(
2037
- "absolute inset-y-6 left-1/2 w-px -translate-x-1/2 rounded-full bg-transparent transition-colors",
2083
+ "absolute inset-y-6 -left-px w-0.5 rounded-full bg-transparent transition-colors",
2038
2084
  "group-hover:bg-primary/60 group-focus-visible:bg-primary",
2039
2085
  dragging && "bg-primary"
2040
2086
  )
@@ -2067,6 +2113,7 @@ function HeaderTabs({
2067
2113
  value,
2068
2114
  onChange,
2069
2115
  className,
2116
+ fill,
2070
2117
  ...rest
2071
2118
  }) {
2072
2119
  return /* @__PURE__ */ jsx(
@@ -2075,7 +2122,8 @@ function HeaderTabs({
2075
2122
  role: "tablist",
2076
2123
  "aria-label": rest["aria-label"],
2077
2124
  className: cn(
2078
- "inline-flex max-w-full items-center gap-5 overflow-x-auto scrollbar-none",
2125
+ "inline-flex max-w-full gap-5 overflow-x-auto scrollbar-none",
2126
+ fill ? "items-stretch" : "items-center",
2079
2127
  className
2080
2128
  ),
2081
2129
  children: tabs.map((tab) => {
@@ -2089,7 +2137,10 @@ function HeaderTabs({
2089
2137
  "aria-selected": active,
2090
2138
  onClick: () => onChange(tab.value),
2091
2139
  className: cn(
2092
- "relative flex shrink-0 items-center gap-1.5 whitespace-nowrap pb-1.5 text-sm transition-colors",
2140
+ "relative flex shrink-0 items-center gap-1.5 whitespace-nowrap text-sm transition-colors",
2141
+ // Filled: the row owns the band's height and the underline sits on
2142
+ // its bottom edge. Otherwise the underline trails the label by 6px.
2143
+ fill ? "h-full" : "pb-1.5",
2093
2144
  active ? "font-medium text-foreground" : "text-muted-foreground hover:text-foreground"
2094
2145
  ),
2095
2146
  children: [
@@ -2124,12 +2175,12 @@ function PageTabs({ actions, className, ...props }) {
2124
2175
  }
2125
2176
  );
2126
2177
  }
2127
- var NavTransitionContext = React7.createContext("slide");
2178
+ var NavTransitionContext = React2.createContext("slide");
2128
2179
  function NavTransitionProvider({ value, children }) {
2129
2180
  return /* @__PURE__ */ jsx(NavTransitionContext.Provider, { value, children });
2130
2181
  }
2131
2182
  function useNavTransition() {
2132
- return React7.useContext(NavTransitionContext);
2183
+ return React2.useContext(NavTransitionContext);
2133
2184
  }
2134
2185
  function resolveTransitionClass(transition, direction) {
2135
2186
  if (transition === "none") return "";
@@ -2164,16 +2215,16 @@ function PremiumBadge({ className }) {
2164
2215
  const tip = t("upgrade.badgeTooltip");
2165
2216
  return /* @__PURE__ */ jsx("span", { title: tip, "aria-label": tip, className: cn("inline-flex shrink-0", className), children: /* @__PURE__ */ jsx(Crown, { className: "h-3.5 w-3.5 text-amber-500", "aria-hidden": true }) });
2166
2217
  }
2167
- var ModuleLayoutContext = React7.createContext("rail");
2218
+ var ModuleLayoutContext = React2.createContext("rail");
2168
2219
  function ModuleLayoutProvider({ variant, children }) {
2169
2220
  return /* @__PURE__ */ jsx(ModuleLayoutContext.Provider, { value: variant, children });
2170
2221
  }
2171
2222
  function useModuleLayout() {
2172
- return React7.useContext(ModuleLayoutContext);
2223
+ return React2.useContext(ModuleLayoutContext);
2173
2224
  }
2174
2225
  function RailNavItem({ item }) {
2175
- const [expanded, setExpanded] = React7.useState(true);
2176
- const Icon = item.icon ? ICON_MAP2[item.icon] ?? null : null;
2226
+ const [expanded, setExpanded] = React2.useState(true);
2227
+ const Icon = item.icon ? ICON_MAP[item.icon] ?? null : null;
2177
2228
  const hasChildren = item.children && item.children.length > 0;
2178
2229
  const hasActiveChild = hasChildren && item.children.some((c) => c.active);
2179
2230
  const isActive = item.active || hasActiveChild;
@@ -2213,10 +2264,14 @@ function RailNavItem({ item }) {
2213
2264
  )) })
2214
2265
  ] });
2215
2266
  }
2267
+ function defaultChildOf(item) {
2268
+ const kids = item.children ?? [];
2269
+ return kids.find((c) => c.defaultChild) ?? kids.find((c) => /(^|-)list$/i.test(c.id)) ?? kids.find((c) => !/(^|-)(new|novo|nova|create)$/i.test(c.id)) ?? kids[0];
2270
+ }
2216
2271
  function MobileTabs({ nav }) {
2217
- const scrollRef = React7.useRef(null);
2218
- const [expandedId, setExpandedId] = React7.useState(null);
2219
- React7.useEffect(() => {
2272
+ const scrollRef = React2.useRef(null);
2273
+ const [expandedId, setExpandedId] = React2.useState(null);
2274
+ React2.useEffect(() => {
2220
2275
  const container = scrollRef.current;
2221
2276
  if (!container) return;
2222
2277
  const activeEl = container.querySelector('[data-active="true"]');
@@ -2227,7 +2282,7 @@ function MobileTabs({ nav }) {
2227
2282
  }, [nav]);
2228
2283
  return /* @__PURE__ */ jsxs("div", { className: "md:hidden -mx-1 mb-4 no-print", children: [
2229
2284
  /* @__PURE__ */ jsx("div", { ref: scrollRef, className: "flex gap-1 px-1 pb-2 overflow-x-auto scrollbar-none", children: nav.map((item) => {
2230
- const Icon = item.icon ? ICON_MAP2[item.icon] ?? null : null;
2285
+ const Icon = item.icon ? ICON_MAP[item.icon] ?? null : null;
2231
2286
  const hasChildren = item.children && item.children.length > 0;
2232
2287
  const isActive = item.active || item.children?.some((c) => c.active);
2233
2288
  const isExpanded = expandedId === item.id;
@@ -2238,6 +2293,7 @@ function MobileTabs({ nav }) {
2238
2293
  "data-active": isActive || void 0,
2239
2294
  onClick: () => {
2240
2295
  if (hasChildren) {
2296
+ if (!isExpanded) defaultChildOf(item)?.onClick?.();
2241
2297
  setExpandedId(isExpanded ? null : item.id);
2242
2298
  } else {
2243
2299
  item.onClick?.();
@@ -2245,11 +2301,15 @@ function MobileTabs({ nav }) {
2245
2301
  }
2246
2302
  },
2247
2303
  className: cn(
2248
- "flex items-center gap-1.5 whitespace-nowrap rounded-full px-3 py-1.5 text-xs font-medium transition-colors shrink-0",
2304
+ // This strip is the most-tapped control in most modules and it
2305
+ // was 28px tall — a third under the comfortable minimum. Height
2306
+ // and text step up on touch; the desktop rail is unaffected.
2307
+ "flex shrink-0 items-center gap-1.5 whitespace-nowrap rounded-full px-4 text-sm font-medium transition-colors",
2308
+ "min-h-[2.75rem] active:scale-[0.98]",
2249
2309
  isActive ? "bg-primary text-primary-foreground" : "bg-muted text-muted-foreground hover:text-foreground"
2250
2310
  ),
2251
2311
  children: [
2252
- Icon && /* @__PURE__ */ jsx(Icon, { className: "h-3 w-3" }),
2312
+ Icon && /* @__PURE__ */ jsx(Icon, { className: "h-3.5 w-3.5" }),
2253
2313
  item.label,
2254
2314
  item.premium && /* @__PURE__ */ jsx(PremiumBadge, {}),
2255
2315
  hasChildren && /* @__PURE__ */ jsx(ChevronDown, { className: cn("h-2.5 w-2.5 transition-transform", isExpanded && "rotate-180") })
@@ -2283,12 +2343,12 @@ function MobileTabs({ nav }) {
2283
2343
  })()
2284
2344
  ] });
2285
2345
  }
2286
- var BackStyleContext = React7.createContext("link");
2346
+ var BackStyleContext = React2.createContext("link");
2287
2347
  function BackStyleProvider({ style, children }) {
2288
2348
  return /* @__PURE__ */ jsx(BackStyleContext.Provider, { value: style, children });
2289
2349
  }
2290
2350
  function useBackStyle() {
2291
- return React7.useContext(BackStyleContext);
2351
+ return React2.useContext(BackStyleContext);
2292
2352
  }
2293
2353
  function SubpageHeader({ title, subtitle, icon, onBack, parentLabel, actions, backStyle, inline }) {
2294
2354
  const t = useTranslation();
@@ -2298,7 +2358,7 @@ function SubpageHeader({ title, subtitle, icon, onBack, parentLabel, actions, ba
2298
2358
  const effectiveOnBack = inline ? onBack : cross.onBack;
2299
2359
  const effectiveParentLabel = inline ? parentLabel : cross.parentLabel;
2300
2360
  useBackHandler(effectiveOnBack);
2301
- const Icon = icon ? ICON_MAP2[icon] ?? null : null;
2361
+ const Icon = icon ? ICON_MAP[icon] ?? null : null;
2302
2362
  if (inline && effectiveOnBack) {
2303
2363
  return /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
2304
2364
  /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3", children: [
@@ -2380,15 +2440,16 @@ function ShellSubpageHeader({
2380
2440
  parentLabel,
2381
2441
  actions
2382
2442
  }) {
2383
- usePageHeader(
2384
- /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [
2385
- Icon && !onBack && /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4 shrink-0 text-muted-foreground" }),
2386
- /* @__PURE__ */ jsx(PageHeaderCrumb, { title, parentLabel, onBack })
2387
- ] }),
2388
- { back: !!onBack }
2389
- );
2390
- if (!actions) return null;
2391
- return /* @__PURE__ */ jsx(PageHeaderActions, { children: /* @__PURE__ */ jsx("div", { className: "no-print flex shrink-0 items-center gap-2", children: actions }) });
2443
+ usePageHeader(true, { back: !!onBack });
2444
+ const crumb = /* @__PURE__ */ jsx(PageTitlePortal, { children: /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [
2445
+ Icon && !onBack && /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4 shrink-0 text-muted-foreground" }),
2446
+ /* @__PURE__ */ jsx(PageHeaderCrumb, { title, parentLabel, onBack })
2447
+ ] }) });
2448
+ if (!actions) return crumb;
2449
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
2450
+ crumb,
2451
+ /* @__PURE__ */ jsx(PageHeaderActions, { children: /* @__PURE__ */ jsx("div", { className: "no-print flex shrink-0 items-center gap-2", children: actions }) })
2452
+ ] });
2392
2453
  }
2393
2454
  function ModuleHeader({ nav }) {
2394
2455
  function defaultChild(item) {
@@ -2399,7 +2460,7 @@ function ModuleHeader({ nav }) {
2399
2460
  const tabs = nav.map((item) => ({
2400
2461
  value: item.id,
2401
2462
  label: item.label,
2402
- icon: item.icon ? ICON_MAP2[item.icon] ?? void 0 : void 0,
2463
+ icon: item.icon ? ICON_MAP[item.icon] ?? void 0 : void 0,
2403
2464
  // Plan-gated views keep their upgrade marker — dropping it would hide the
2404
2465
  // only signal that the tab costs an upgrade.
2405
2466
  badge: item.premium ? /* @__PURE__ */ jsx(PremiumBadge, {}) : void 0
@@ -2438,12 +2499,12 @@ function ModulePage({ title: _title, subtitle, nav, children, className, headerA
2438
2499
  ] }) }),
2439
2500
  /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
2440
2501
  showHeader && headerAction && /* @__PURE__ */ jsx(PageHeaderActions, { children: headerAction }),
2441
- /* @__PURE__ */ jsx(MobileTabs, { nav }),
2502
+ !onSubpage && /* @__PURE__ */ jsx(MobileTabs, { nav }),
2442
2503
  /* @__PURE__ */ jsx(PageTransition, { transitionKey, direction, children })
2443
2504
  ] })
2444
2505
  ] });
2445
2506
  }
2446
2507
 
2447
- export { AppShell, BackStyleProvider, HeaderTabs, ModuleLayoutProvider, ModulePage, NavTransitionProvider, PageHeader, PageHeaderActions, PageHeaderCrumb, PageRail, PageTabs, PageTransition, RailNavItem, SaveBar, SaveBarProvider, Sheet, SheetBody, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger, Sidebar, SubpageHeader, Topbar, useBackHandler, useBackStyle, useCrossModuleBack, useModuleHeaderActionsSlot, useModuleHeaderSlot, useModuleLayout, useNavTransition, useRailRow, useSaveBar, useSaveBarVisible };
2448
- //# sourceMappingURL=chunk-MVH64JFG.js.map
2449
- //# sourceMappingURL=chunk-MVH64JFG.js.map
2508
+ export { AppShell, BackStyleProvider, HeaderTabs, ModuleLayoutProvider, ModulePage, NavTransitionProvider, PageHeader, PageHeaderActions, PageHeaderCrumb, PageRail, PageTabs, PageTransition, RailNavItem, SaveBar, SaveBarProvider, Sheet, SheetBody, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger, Sidebar, SubpageHeader, Topbar, bottomNavReachesAll, useBackHandler, useBackStyle, useCrossModuleBack, useModuleHeaderActionsSlot, useModuleHeaderSlot, useModuleLayout, useNavTransition, useRailRow, useSaveBar, useSaveBarVisible };
2509
+ //# sourceMappingURL=chunk-PZU7XC2M.js.map
2510
+ //# sourceMappingURL=chunk-PZU7XC2M.js.map