@fayz-ai/ui 0.15.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 (65) hide show
  1. package/dist/analytics/context.d.ts +15 -1
  2. package/dist/analytics/context.d.ts.map +1 -1
  3. package/dist/analytics/index.d.ts +1 -1
  4. package/dist/analytics/index.d.ts.map +1 -1
  5. package/dist/{chunk-5KMAEQA3.js → chunk-5VF45NLE.js} +4 -4
  6. package/dist/{chunk-5KMAEQA3.js.map → chunk-5VF45NLE.js.map} +1 -1
  7. package/dist/{chunk-J7F3Z52K.js → chunk-6BJTH3L6.js} +4 -4
  8. package/dist/chunk-6BJTH3L6.js.map +1 -0
  9. package/dist/{chunk-KD2V3TAX.js → chunk-E44WOPCR.js} +61 -49
  10. package/dist/chunk-E44WOPCR.js.map +1 -0
  11. package/dist/{chunk-FFJ2C5YH.js → chunk-HKGTF2YA.js} +41 -16
  12. package/dist/chunk-HKGTF2YA.js.map +1 -0
  13. package/dist/{chunk-NA3GE4H3.js → chunk-PZU7XC2M.js} +362 -252
  14. package/dist/chunk-PZU7XC2M.js.map +1 -0
  15. package/dist/{chunk-SLWTKHNY.js → chunk-SDPYDWWV.js} +371 -83
  16. package/dist/chunk-SDPYDWWV.js.map +1 -0
  17. package/dist/dashboard/index.js +3 -3
  18. package/dist/hooks/useIsWide.d.ts +16 -0
  19. package/dist/hooks/useIsWide.d.ts.map +1 -0
  20. package/dist/index.d.ts +4 -3
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js +30 -10
  23. package/dist/index.js.map +1 -1
  24. package/dist/layout/AppShell.d.ts +25 -2
  25. package/dist/layout/AppShell.d.ts.map +1 -1
  26. package/dist/layout/ModulePage.d.ts +4 -0
  27. package/dist/layout/ModulePage.d.ts.map +1 -1
  28. package/dist/layout/SaveBar.d.ts.map +1 -1
  29. package/dist/layout/Sidebar.d.ts.map +1 -1
  30. package/dist/layout/Topbar.d.ts +5 -1
  31. package/dist/layout/Topbar.d.ts.map +1 -1
  32. package/dist/layout/index.d.ts +1 -1
  33. package/dist/layout/index.d.ts.map +1 -1
  34. package/dist/layout/index.js +3 -3
  35. package/dist/primitives/checkbox.d.ts +4 -0
  36. package/dist/primitives/checkbox.d.ts.map +1 -1
  37. package/dist/primitives/index.d.ts +3 -0
  38. package/dist/primitives/index.d.ts.map +1 -1
  39. package/dist/primitives/index.js +4 -4
  40. package/dist/primitives/list-view.d.ts +5 -1
  41. package/dist/primitives/list-view.d.ts.map +1 -1
  42. package/dist/primitives/modal.d.ts.map +1 -1
  43. package/dist/primitives/page-actions.d.ts +55 -0
  44. package/dist/primitives/page-actions.d.ts.map +1 -0
  45. package/dist/primitives/page-header.d.ts +5 -2
  46. package/dist/primitives/page-header.d.ts.map +1 -1
  47. package/dist/primitives/page-toolbar.d.ts.map +1 -1
  48. package/dist/primitives/refresh-button.d.ts.map +1 -1
  49. package/dist/primitives/row-actions.d.ts +11 -0
  50. package/dist/primitives/row-actions.d.ts.map +1 -0
  51. package/dist/primitives/whatsapp-icon.d.ts +7 -0
  52. package/dist/primitives/whatsapp-icon.d.ts.map +1 -0
  53. package/dist/stores/index.d.ts +1 -1
  54. package/dist/stores/index.d.ts.map +1 -1
  55. package/dist/stores/layout.store.d.ts +1 -0
  56. package/dist/stores/layout.store.d.ts.map +1 -1
  57. package/dist/stores/page-header.store.d.ts +39 -5
  58. package/dist/stores/page-header.store.d.ts.map +1 -1
  59. package/dist/styles.css +166 -3
  60. package/package.json +2 -2
  61. package/dist/chunk-FFJ2C5YH.js.map +0 -1
  62. package/dist/chunk-J7F3Z52K.js.map +0 -1
  63. package/dist/chunk-KD2V3TAX.js.map +0 -1
  64. package/dist/chunk-NA3GE4H3.js.map +0 -1
  65. package/dist/chunk-SLWTKHNY.js.map +0 -1
@@ -1,9 +1,9 @@
1
- import { useHasPageHeaderHost, usePageHeader, useLayoutStore, useRegisterPageActions, useHasPageHeaderBack, usePageHeaderSlot, useHasPageHeaderOutlet, useHasPageActions, usePageHeaderHost } from './chunk-FFJ2C5YH.js';
1
+ import { useHasPageHeaderHost, usePageHeader, PageTitlePortal, useLayoutStore, useHasPageHeaderBack, useRegisterPageActions, usePageHeaderClaimed, usePageTitleSlotHost, useHasPageHeaderOutlet, useHasPageActions, usePageHeaderHost } from './chunk-HKGTF2YA.js';
2
2
  import { cn } from './chunk-OKZ6WQB3.js';
3
3
  import { ICON_MAP } from './chunk-4GK6IHOI.js';
4
- import * as React7 from 'react';
4
+ import * as React2 from 'react';
5
5
  import * as DialogPrimitive from '@radix-ui/react-dialog';
6
- import { X, Loader2, ArrowLeft, Search, Bell, PanelLeft, PanelLeftClose, Menu, Crown, ChevronDown, ChevronRight, ChevronLeft, ExternalLink, CreditCard, Sparkles, Home, Plus, PiggyBank, Wallet, MessageCircle, Settings, Calendar, ArrowLeftRight, Receipt, UserCog, 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';
7
7
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
8
8
  import { useTranslation, useNavReferrer, routeModule, navHistoryBack } from '@fayz-ai/core';
9
9
  import * as Popover from '@radix-ui/react-popover';
@@ -29,19 +29,19 @@ var Sheet = DialogPrimitive.Root;
29
29
  var SheetTrigger = DialogPrimitive.Trigger;
30
30
  var SheetClose = DialogPrimitive.Close;
31
31
  var SheetPortal = DialogPrimitive.Portal;
32
- var SheetOverlay = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
32
+ var SheetOverlay = React2.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
33
33
  DialogPrimitive.Overlay,
34
34
  {
35
35
  ref,
36
36
  className: cn(
37
- "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]",
38
38
  className
39
39
  ),
40
40
  ...props
41
41
  }
42
42
  ));
43
43
  SheetOverlay.displayName = "SheetOverlay";
44
- var SheetContent = React7.forwardRef(({ className, children, width, hideClose, overlay = "blur", onPointerDownOutside, onInteractOutside, ...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: [
45
45
  /* @__PURE__ */ jsx(SheetOverlay, { className: overlay === "none" ? "bg-transparent backdrop-blur-0" : overlay === "dim" ? "backdrop-blur-0" : void 0 }),
46
46
  /* @__PURE__ */ jsxs(
47
47
  DialogPrimitive.Content,
@@ -62,7 +62,7 @@ var SheetContent = React7.forwardRef(({ className, children, width, hideClose, o
62
62
  onInteractOutside?.(e);
63
63
  },
64
64
  className: cn(
65
- "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",
66
66
  "w-full",
67
67
  width ?? "max-w-md",
68
68
  className
@@ -99,7 +99,7 @@ function SheetFooter({ className, ...props }) {
99
99
  );
100
100
  }
101
101
  SheetFooter.displayName = "SheetFooter";
102
- var SheetTitle = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
102
+ var SheetTitle = React2.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
103
103
  DialogPrimitive.Title,
104
104
  {
105
105
  ref,
@@ -108,7 +108,7 @@ var SheetTitle = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE_
108
108
  }
109
109
  ));
110
110
  SheetTitle.displayName = "SheetTitle";
111
- var SheetDescription = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
111
+ var SheetDescription = React2.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
112
112
  DialogPrimitive.Description,
113
113
  {
114
114
  ref,
@@ -127,8 +127,8 @@ function SheetBody({ className, ...props }) {
127
127
  );
128
128
  }
129
129
  SheetBody.displayName = "SheetBody";
130
- var SaveBarApiContext = React7.createContext(null);
131
- var SaveBarStateContext = React7.createContext({ visible: false, saving: false });
130
+ var SaveBarApiContext = React2.createContext(null);
131
+ var SaveBarStateContext = React2.createContext({ visible: false, saving: false });
132
132
  var _saveBarVisible = false;
133
133
  var _saveBarSubs = /* @__PURE__ */ new Set();
134
134
  function setSaveBarVisibleSignal(v) {
@@ -137,7 +137,7 @@ function setSaveBarVisibleSignal(v) {
137
137
  _saveBarSubs.forEach((fn) => fn());
138
138
  }
139
139
  function useSaveBarVisible() {
140
- return React7.useSyncExternalStore(
140
+ return React2.useSyncExternalStore(
141
141
  (cb) => {
142
142
  _saveBarSubs.add(cb);
143
143
  return () => {
@@ -149,15 +149,15 @@ function useSaveBarVisible() {
149
149
  );
150
150
  }
151
151
  function SaveBarProvider({ children }) {
152
- const [state, setState] = React7.useState({ visible: false, saving: false });
153
- const handlers = React7.useRef({});
154
- const api = React7.useMemo(() => ({ setState, handlers }), []);
155
- const backStack = React7.useRef([]);
156
- const backApi = React7.useMemo(() => ({ stack: backStack }), []);
157
- 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(() => {
158
158
  setSaveBarVisibleSignal(state.visible);
159
159
  }, [state.visible]);
160
- React7.useEffect(() => {
160
+ React2.useEffect(() => {
161
161
  function onKey(e) {
162
162
  if (state.visible && !state.saving) {
163
163
  if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
@@ -185,7 +185,7 @@ function SaveBarProvider({ children }) {
185
185
  }, [state.visible, state.saving]);
186
186
  return /* @__PURE__ */ jsx(SaveBarApiContext.Provider, { value: api, children: /* @__PURE__ */ jsx(BackNavApiContext.Provider, { value: backApi, children: /* @__PURE__ */ jsx(SaveBarStateContext.Provider, { value: state, children }) }) });
187
187
  }
188
- var BackNavApiContext = React7.createContext(null);
188
+ var BackNavApiContext = React2.createContext(null);
189
189
  function isEditableTarget(target) {
190
190
  const el = target;
191
191
  if (!el || !el.tagName) return false;
@@ -193,12 +193,12 @@ function isEditableTarget(target) {
193
193
  return tag === "input" || tag === "textarea" || tag === "select" || el.isContentEditable;
194
194
  }
195
195
  function useBackHandler(onBack) {
196
- const api = React7.useContext(BackNavApiContext);
197
- const ref = React7.useRef(onBack);
198
- React7.useEffect(() => {
196
+ const api = React2.useContext(BackNavApiContext);
197
+ const ref = React2.useRef(onBack);
198
+ React2.useEffect(() => {
199
199
  ref.current = onBack;
200
200
  });
201
- React7.useEffect(() => {
201
+ React2.useEffect(() => {
202
202
  if (!api) return;
203
203
  const stack = api.stack.current;
204
204
  stack.push(ref);
@@ -209,23 +209,23 @@ function useBackHandler(onBack) {
209
209
  }, [api]);
210
210
  }
211
211
  function useSaveBar({ dirty, saving = false, onSave, onDiscard, message, saveLabel, discardLabel }) {
212
- const api = React7.useContext(SaveBarApiContext);
213
- React7.useEffect(() => {
212
+ const api = React2.useContext(SaveBarApiContext);
213
+ React2.useEffect(() => {
214
214
  if (api) api.handlers.current = { onSave, onDiscard };
215
215
  });
216
- React7.useEffect(() => {
216
+ React2.useEffect(() => {
217
217
  if (!api) return;
218
218
  api.setState({ visible: dirty, saving, message, saveLabel, discardLabel });
219
219
  return () => api.setState((s) => ({ ...s, visible: false }));
220
220
  }, [api, dirty, saving, message, saveLabel, discardLabel]);
221
221
  }
222
222
  function SaveBar({ className }) {
223
- const state = React7.useContext(SaveBarStateContext);
224
- const api = React7.useContext(SaveBarApiContext);
223
+ const state = React2.useContext(SaveBarStateContext);
224
+ const api = React2.useContext(SaveBarApiContext);
225
225
  const t = useTranslation();
226
- const [rendered, setRendered] = React7.useState(state.visible);
227
- const [closing, setClosing] = React7.useState(false);
228
- React7.useEffect(() => {
226
+ const [rendered, setRendered] = React2.useState(state.visible);
227
+ const [closing, setClosing] = React2.useState(false);
228
+ React2.useEffect(() => {
229
229
  if (state.visible) {
230
230
  setRendered(true);
231
231
  setClosing(false);
@@ -241,40 +241,52 @@ function SaveBar({ className }) {
241
241
  }, [state.visible, rendered]);
242
242
  if (!api || !rendered) return null;
243
243
  const { saving, message, saveLabel, discardLabel } = state;
244
- 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(
245
- "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",
246
- closing ? "animate-out fade-out slide-out-to-bottom-2" : "animate-in fade-in slide-in-from-bottom-2"
247
- ), children: [
248
- /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2 text-sm font-medium", children: [
249
- /* @__PURE__ */ jsx("span", { className: "h-2 w-2 shrink-0 rounded-full bg-amber-400" }),
250
- message ?? t("saveBar.unsaved")
251
- ] }),
252
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
253
- /* @__PURE__ */ jsx(
254
- "button",
255
- {
256
- type: "button",
257
- disabled: saving,
258
- onClick: () => api.handlers.current.onDiscard?.(),
259
- 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",
260
- children: discardLabel ?? t("common.discard")
261
- }
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
262
253
  ),
263
- /* @__PURE__ */ jsxs(
264
- "button",
265
- {
266
- type: "button",
267
- disabled: saving,
268
- onClick: () => api.handlers.current.onSave?.(),
269
- 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",
270
- children: [
271
- saving && /* @__PURE__ */ jsx(Loader2, { className: "mr-1.5 h-3.5 w-3.5 animate-spin" }),
272
- saveLabel ?? t("common.save")
273
- ]
274
- }
275
- )
276
- ] })
277
- ] }) });
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
+ );
278
290
  }
279
291
  function useCrossModuleBack(onBack, parentLabel) {
280
292
  const currentPath = typeof window !== "undefined" ? (window.location.hash ? window.location.hash.slice(1) : window.location.pathname) || "/" : "/";
@@ -289,10 +301,14 @@ function useCrossModuleBack(onBack, parentLabel) {
289
301
  function PageHeaderCrumb({
290
302
  title,
291
303
  parentLabel,
292
- onBack
304
+ onBack,
305
+ headingLevel = "h2"
293
306
  }) {
307
+ const Heading = headingLevel;
308
+ const asHeading = typeof title === "string" && title !== "";
294
309
  if (!onBack) {
295
- 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 });
296
312
  }
297
313
  return /* @__PURE__ */ jsxs("nav", { className: "flex min-w-0 items-center gap-1.5 text-sm text-muted-foreground", children: [
298
314
  /* @__PURE__ */ jsxs(
@@ -313,6 +329,11 @@ function PageHeaderCrumb({
313
329
  // Title still waiting on data: a pulsing bar, otherwise the line
314
330
  // appears from nowhere and shoves the rest when the record lands.
315
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 })
316
337
  ) : /* @__PURE__ */ jsx("span", { className: "min-w-0 truncate font-medium text-foreground", children: title })
317
338
  ] })
318
339
  ] });
@@ -321,7 +342,7 @@ function PageHeader({ title, onBack, parentLabel, backLabel }) {
321
342
  const hasHost = useHasPageHeaderHost();
322
343
  const { onBack: effectiveOnBack, parentLabel: effectiveParent } = useCrossModuleBack(onBack, parentLabel);
323
344
  useBackHandler(effectiveOnBack);
324
- const node = React7.useMemo(() => /* @__PURE__ */ jsx(
345
+ const node = React2.useMemo(() => /* @__PURE__ */ jsx(
325
346
  PageHeaderCrumb,
326
347
  {
327
348
  title,
@@ -329,21 +350,21 @@ function PageHeader({ title, onBack, parentLabel, backLabel }) {
329
350
  onBack: effectiveOnBack
330
351
  }
331
352
  ), [effectiveOnBack, effectiveParent, backLabel, title]);
332
- usePageHeader(hasHost ? node : null, { back: !!effectiveOnBack });
333
- if (hasHost) return null;
353
+ usePageHeader(hasHost, { back: !!effectiveOnBack });
354
+ if (hasHost) return /* @__PURE__ */ jsx(PageTitlePortal, { children: node });
334
355
  return /* @__PURE__ */ jsx("div", { className: "mb-4", children: node });
335
356
  }
336
357
  var GUTTER_PX = 8;
337
358
  var CLOSE_DELAY_MS = 140;
338
- var useIsomorphicLayoutEffect = typeof window !== "undefined" ? React7.useLayoutEffect : React7.useEffect;
359
+ var useIsomorphicLayoutEffect = typeof window !== "undefined" ? React2.useLayoutEffect : React2.useEffect;
339
360
  function iconOf(name) {
340
361
  if (!name) return null;
341
362
  return ICON_MAP[name] ?? null;
342
363
  }
343
- var RailFlyoutContext = React7.createContext(null);
364
+ var RailFlyoutContext = React2.createContext(null);
344
365
  var RailFlyoutProvider = RailFlyoutContext.Provider;
345
366
  function useRailRow(content) {
346
- const flyout = React7.useContext(RailFlyoutContext);
367
+ const flyout = React2.useContext(RailFlyoutContext);
347
368
  if (!flyout || !content) return {};
348
369
  return flyout.rowProps(content);
349
370
  }
@@ -353,19 +374,19 @@ function useRailFlyout({
353
374
  activeRoute,
354
375
  onNavigate
355
376
  }) {
356
- const [state, setState] = React7.useState(null);
357
- const [pos, setPos] = React7.useState({ top: 0, arrow: 20 });
358
- const [animate, setAnimate] = React7.useState(false);
359
- const panelRef = React7.useRef(null);
360
- const closeTimer = React7.useRef(null);
361
- const lastContent = React7.useRef(null);
362
- 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(() => {
363
384
  if (closeTimer.current) {
364
385
  clearTimeout(closeTimer.current);
365
386
  closeTimer.current = null;
366
387
  }
367
388
  }, []);
368
- const show = React7.useCallback(
389
+ const show = React2.useCallback(
369
390
  (anchor, content2) => {
370
391
  if (!enabled) return;
371
392
  cancelClose();
@@ -373,19 +394,19 @@ function useRailFlyout({
373
394
  },
374
395
  [enabled, cancelClose]
375
396
  );
376
- const hide = React7.useCallback(() => {
397
+ const hide = React2.useCallback(() => {
377
398
  cancelClose();
378
399
  closeTimer.current = setTimeout(() => setState(null), CLOSE_DELAY_MS);
379
400
  }, [cancelClose]);
380
- const dismiss = React7.useCallback(() => {
401
+ const dismiss = React2.useCallback(() => {
381
402
  cancelClose();
382
403
  setState(null);
383
404
  }, [cancelClose]);
384
- React7.useEffect(() => () => cancelClose(), [cancelClose]);
385
- React7.useEffect(() => {
405
+ React2.useEffect(() => () => cancelClose(), [cancelClose]);
406
+ React2.useEffect(() => {
386
407
  if (!enabled) dismiss();
387
408
  }, [enabled, dismiss]);
388
- const reposition = React7.useCallback(() => {
409
+ const reposition = React2.useCallback(() => {
389
410
  const rail = railRef.current;
390
411
  const anchor = state?.anchor;
391
412
  if (!rail || !anchor) return;
@@ -400,7 +421,7 @@ function useRailFlyout({
400
421
  setPos({ top, arrow });
401
422
  }, [railRef, state]);
402
423
  useIsomorphicLayoutEffect(reposition, [reposition, state?.content.key]);
403
- React7.useEffect(() => {
424
+ React2.useEffect(() => {
404
425
  if (!state) return;
405
426
  const rail = railRef.current;
406
427
  const onScroll = () => reposition();
@@ -412,7 +433,7 @@ function useRailFlyout({
412
433
  };
413
434
  }, [state, railRef, reposition]);
414
435
  const visible = !!state;
415
- React7.useEffect(() => {
436
+ React2.useEffect(() => {
416
437
  if (!visible) {
417
438
  setAnimate(false);
418
439
  return;
@@ -420,7 +441,7 @@ function useRailFlyout({
420
441
  const id = requestAnimationFrame(() => setAnimate(true));
421
442
  return () => cancelAnimationFrame(id);
422
443
  }, [visible]);
423
- React7.useEffect(() => {
444
+ React2.useEffect(() => {
424
445
  if (!visible) return;
425
446
  const onKey = (event) => {
426
447
  if (event.key === "Escape") dismiss();
@@ -428,7 +449,7 @@ function useRailFlyout({
428
449
  window.addEventListener("keydown", onKey);
429
450
  return () => window.removeEventListener("keydown", onKey);
430
451
  }, [visible, dismiss]);
431
- const rowProps = React7.useCallback(
452
+ const rowProps = React2.useCallback(
432
453
  (content2) => ({
433
454
  onMouseEnter: (event) => show(event.currentTarget, content2),
434
455
  onFocus: (event) => show(event.currentTarget, content2),
@@ -545,7 +566,7 @@ function FlyoutBody({
545
566
  ] });
546
567
  }
547
568
  var RAIL_PUSH_MS = 260;
548
- var useIsomorphicLayoutEffect2 = typeof window !== "undefined" ? React7.useLayoutEffect : React7.useEffect;
569
+ var useIsomorphicLayoutEffect2 = typeof window !== "undefined" ? React2.useLayoutEffect : React2.useEffect;
549
570
  function getIcon(name) {
550
571
  return ICON_MAP[name] ?? Home;
551
572
  }
@@ -645,7 +666,7 @@ function NavGroup({
645
666
  flyout
646
667
  }) {
647
668
  const isChildActive = item.children?.some((c) => c.route === activeRoute);
648
- const [open, setOpen] = React7.useState(() => isChildActive ?? false);
669
+ const [open, setOpen] = React2.useState(() => isChildActive ?? false);
649
670
  const Icon = getIcon(item.icon);
650
671
  if (collapsed) {
651
672
  const hoverProps = flyout?.rowProps(flyoutContentOf(item));
@@ -719,7 +740,7 @@ function NavGroup({
719
740
  child.premium && /* @__PURE__ */ jsx(Crown, { className: "h-3 w-3 shrink-0 text-amber-500", "aria-hidden": true })
720
741
  ] });
721
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 });
722
- if (!childConnector) return /* @__PURE__ */ jsx(React7.Fragment, { children: row }, child.id);
743
+ if (!childConnector) return /* @__PURE__ */ jsx(React2.Fragment, { children: row }, child.id);
723
744
  const activeIndex = item.children.findIndex((c) => c.route === activeRoute);
724
745
  const onPath = activeIndex >= 0 && childIndex < activeIndex;
725
746
  return (
@@ -781,9 +802,9 @@ function NavGroup({
781
802
  ] });
782
803
  }
783
804
  function withCollapsed(slot, collapsed) {
784
- if (!React7.isValidElement(slot)) return slot;
805
+ if (!React2.isValidElement(slot)) return slot;
785
806
  if (typeof slot.type === "string") return slot;
786
- return React7.cloneElement(slot, { collapsed });
807
+ return React2.cloneElement(slot, { collapsed });
787
808
  }
788
809
  function Sidebar({
789
810
  navigation,
@@ -812,7 +833,7 @@ function Sidebar({
812
833
  const currentPath = currentPathProp;
813
834
  const setCommandPaletteOpen = useLayoutStore((s) => s.setCommandPaletteOpen);
814
835
  const activeRoute = resolveActiveRoute(navigation, currentPath);
815
- const railRef = React7.useRef(null);
836
+ const railRef = React2.useRef(null);
816
837
  const flyout = useRailFlyout({
817
838
  enabled: collapsed,
818
839
  railRef,
@@ -861,10 +882,10 @@ function Sidebar({
861
882
  mainItems.length > 0 && renderSection(mainItems, sectionLabels?.main),
862
883
  secondaryItems.length > 0 && /* @__PURE__ */ jsx("div", { className: "mt-6", children: renderSection(secondaryItems, sectionLabels?.secondary) })
863
884
  ] });
864
- const [outgoing, setOutgoing] = React7.useState(null);
865
- const lastBodyRef = React7.useRef(null);
866
- const lastKeyRef = React7.useRef(navKey);
867
- 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);
868
889
  useIsomorphicLayoutEffect2(() => {
869
890
  if (lastKeyRef.current !== navKey) {
870
891
  const previousBody = lastBodyRef.current;
@@ -877,7 +898,7 @@ function Sidebar({
877
898
  }
878
899
  lastBodyRef.current = navBody;
879
900
  });
880
- React7.useEffect(() => () => {
901
+ React2.useEffect(() => () => {
881
902
  if (timerRef.current) clearTimeout(timerRef.current);
882
903
  }, []);
883
904
  return (
@@ -1022,34 +1043,41 @@ function Sidebar({
1022
1043
  // it the padded/bordered wrapper shows as a blank block.
1023
1044
  /* @__PURE__ */ jsx("div", { className: cn("space-y-2 p-2 empty:hidden", !borderless && "border-t border-sidebar-border"), children: footerContent }),
1024
1045
  withCollapsed(orgSwitcher, collapsed),
1025
- /* @__PURE__ */ jsx("div", { className: cn("p-2", !borderless && "border-t border-sidebar-border"), children: /* @__PURE__ */ jsxs("div", { className: cn(
1026
- "flex items-center rounded-button",
1027
- collapsed ? "flex-col gap-1" : "gap-1 px-1"
1028
- ), children: [
1029
- userMenuSlot,
1030
- !collapsed && /* @__PURE__ */ jsx("div", { className: "flex-1" }),
1031
- notificationSlot ?? /* @__PURE__ */ jsxs(
1032
- "button",
1033
- {
1034
- 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",
1035
- "aria-label": unreadCount > 0 ? `${unreadCount} unread notifications` : "Notifications",
1036
- children: [
1037
- /* @__PURE__ */ jsx(Bell, { className: "h-4 w-4" }),
1038
- unreadCount > 0 && /* @__PURE__ */ jsx("span", { className: "absolute right-1 top-1 h-2 w-2 rounded-full bg-destructive" })
1039
- ]
1040
- }
1041
- ),
1042
- !drawer && /* @__PURE__ */ jsx(
1043
- "button",
1044
- {
1045
- onClick: onToggle,
1046
- 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",
1047
- "aria-label": collapsed ? "Expand sidebar" : "Collapse sidebar",
1048
- title: collapsed ? "Expand sidebar" : "Collapse sidebar",
1049
- children: collapsed ? /* @__PURE__ */ jsx(PanelLeft, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx(PanelLeftClose, { className: "h-4 w-4" })
1050
- }
1051
- )
1052
- ] }) }),
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(
1069
+ "button",
1070
+ {
1071
+ onClick: onToggle,
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" })
1076
+ }
1077
+ )
1078
+ ] })
1079
+ }
1080
+ ),
1053
1081
  flyout.panel
1054
1082
  ]
1055
1083
  }
@@ -1066,7 +1094,7 @@ function TopbarUserMenu({
1066
1094
  onBilling,
1067
1095
  userMenuExtras
1068
1096
  }) {
1069
- const [open, setOpen] = React7.useState(false);
1097
+ const [open, setOpen] = React2.useState(false);
1070
1098
  if (!user) return null;
1071
1099
  const initial = (user.fullName?.trim()?.[0] ?? user.email?.[0] ?? "?").toUpperCase();
1072
1100
  const item = (label, onClick, Icon) => onClick ? /* @__PURE__ */ jsxs(
@@ -1150,7 +1178,7 @@ function getIcon2(name) {
1150
1178
  return ICON_MAP[name] ?? Home;
1151
1179
  }
1152
1180
  function NavDropdown({ item, currentPath, onNavigate }) {
1153
- const [open, setOpen] = React7.useState(false);
1181
+ const [open, setOpen] = React2.useState(false);
1154
1182
  const Icon = getIcon2(item.icon);
1155
1183
  const isChildActive = item.children?.some(
1156
1184
  (child) => currentPath === child.route || currentPath.startsWith(child.route + "/")
@@ -1208,6 +1236,7 @@ function Topbar({
1208
1236
  logo,
1209
1237
  user,
1210
1238
  onMenuClick,
1239
+ hideMobileChrome,
1211
1240
  orgSwitcher,
1212
1241
  leftContent,
1213
1242
  rightContent,
@@ -1231,7 +1260,7 @@ function Topbar({
1231
1260
  onNavigate?.(route);
1232
1261
  };
1233
1262
  return /* @__PURE__ */ jsxs("header", { "data-print": "hide", className: "sticky top-0 z-50 w-full shrink-0", children: [
1234
- /* @__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: [
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: [
1235
1264
  /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-center gap-3", children: [
1236
1265
  onMenuClick && /* @__PURE__ */ jsx(
1237
1266
  "button",
@@ -1316,24 +1345,27 @@ function Topbar({
1316
1345
  }) }) }) })
1317
1346
  ] });
1318
1347
  }
1319
- var ModuleHeaderSlotContext = React7.createContext(null);
1348
+ var ModuleHeaderSlotContext = React2.createContext(null);
1320
1349
  function useModuleHeaderSlot() {
1321
- return React7.useContext(ModuleHeaderSlotContext);
1350
+ return React2.useContext(ModuleHeaderSlotContext);
1322
1351
  }
1323
- var ModuleHeaderActionsSlotContext = React7.createContext(null);
1352
+ var ModuleHeaderActionsSlotContext = React2.createContext(null);
1324
1353
  function useModuleHeaderActionsSlot() {
1325
- return React7.useContext(ModuleHeaderActionsSlotContext);
1354
+ return React2.useContext(ModuleHeaderActionsSlotContext);
1326
1355
  }
1327
- var PageRailSlotContext = React7.createContext(null);
1356
+ var PageRailSlotContext = React2.createContext(null);
1328
1357
  function usePageRailSlot() {
1329
- 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");
1330
1362
  }
1331
1363
  function ShellPageTitle({
1332
1364
  title,
1333
1365
  back
1334
1366
  }) {
1335
1367
  if (!title && !back) return null;
1336
- 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" });
1337
1369
  }
1338
1370
  function SidebarLayout({
1339
1371
  navigation = [],
@@ -1362,6 +1394,7 @@ function SidebarLayout({
1362
1394
  unreadCount = 0,
1363
1395
  searchLabel,
1364
1396
  hasBottomNav = false,
1397
+ barOwnsNav = false,
1365
1398
  mobileHeader = "minimal",
1366
1399
  sidebarSectionLabels,
1367
1400
  sidebarNavKey,
@@ -1370,16 +1403,19 @@ function SidebarLayout({
1370
1403
  railOpen
1371
1404
  }) {
1372
1405
  const sidebarCollapsed = useLayoutStore((s) => s.sidebarCollapsed);
1373
- const pageHeaderSlot = usePageHeaderSlot();
1406
+ const pageHeaderClaimed = usePageHeaderClaimed();
1407
+ const setTitleSlot = usePageTitleSlotHost();
1374
1408
  usePageHeaderHost();
1375
1409
  const setSidebarCollapsed = useLayoutStore((s) => s.setSidebarCollapsed);
1376
- const [mobileMenuOpen, setMobileMenuOpen] = React7.useState(false);
1377
- const [headerSlot, setHeaderSlot] = React7.useState(null);
1378
- const [actionsSlot, setActionsSlot] = React7.useState(null);
1379
- 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);
1380
1415
  const hasPageActions = useHasPageActions();
1381
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(
1382
- "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",
1383
1419
  // Frame mode: the inset gap around the framed card takes the sidebar (menu)
1384
1420
  // color, so the white card reads as a panel floating on the same tone as the
1385
1421
  // left menu. The card's own md:m-2 margin reveals this behind it.
@@ -1437,7 +1473,7 @@ function SidebarLayout({
1437
1473
  mobileHeader !== "minimal" && !hasPageActions && "hidden md:flex"
1438
1474
  ), children: [
1439
1475
  /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-3", children: [
1440
- !hasBottomNav && /* @__PURE__ */ jsx(
1476
+ !barOwnsNav && /* @__PURE__ */ jsx(
1441
1477
  "button",
1442
1478
  {
1443
1479
  onClick: () => setMobileMenuOpen(true),
@@ -1446,7 +1482,8 @@ function SidebarLayout({
1446
1482
  children: /* @__PURE__ */ jsx(Menu, { className: "h-5 w-5" })
1447
1483
  }
1448
1484
  ),
1449
- 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" }),
1450
1487
  topbarStart,
1451
1488
  /* @__PURE__ */ jsx("div", { ref: setHeaderSlot, className: "flex min-w-0 flex-1 items-center" })
1452
1489
  ] }),
@@ -1499,6 +1536,7 @@ function TopbarLayout({
1499
1536
  billingLabel,
1500
1537
  userMenuExtras,
1501
1538
  userMenuSlot,
1539
+ drawerUserMenuSlot,
1502
1540
  orgSwitcher,
1503
1541
  frame,
1504
1542
  currentPath,
@@ -1507,19 +1545,23 @@ function TopbarLayout({
1507
1545
  pageTitle,
1508
1546
  pageBack,
1509
1547
  notificationSlot,
1510
- hasBottomNav = false
1548
+ unreadCount = 0,
1549
+ hasBottomNav = false,
1550
+ barOwnsNav = false
1511
1551
  }) {
1512
- const [mobileMenuOpen, setMobileMenuOpen] = React7.useState(false);
1513
- const [headerSlot, setHeaderSlot] = React7.useState(null);
1514
- const [actionsSlot, setActionsSlot] = React7.useState(null);
1515
- const [pageRailSlot, setPageRailSlot] = React7.useState(null);
1516
- 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();
1517
1559
  const hasOutlet = useHasPageHeaderOutlet();
1518
1560
  const hasPageActions = useHasPageActions();
1519
1561
  usePageHeaderHost();
1520
- const pageHeaderSlot = hasOutlet ? null : rawSlot;
1562
+ const pageHeaderClaimed = hasOutlet ? false : rawClaimed;
1521
1563
  const showTitle = !hasOutlet && !!pageTitle;
1522
- 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: [
1523
1565
  /* @__PURE__ */ jsx(
1524
1566
  Topbar,
1525
1567
  {
@@ -1542,19 +1584,30 @@ function TopbarLayout({
1542
1584
  topbarEnd,
1543
1585
  notificationSlot
1544
1586
  ] }) : void 0,
1545
- onMenuClick: hasBottomNav ? void 0 : () => setMobileMenuOpen(true)
1587
+ onMenuClick: barOwnsNav ? void 0 : () => setMobileMenuOpen(true),
1588
+ hideMobileChrome: barOwnsNav
1546
1589
  }
1547
1590
  ),
1548
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: [
1549
1592
  /* @__PURE__ */ jsx("div", { ref: setPageRailSlot, className: "flex shrink-0 empty:hidden" }),
1550
1593
  /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 min-h-0 flex-1 flex-col overflow-hidden", children: [
1551
- (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: [
1552
- /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-3", children: [
1553
- pageHeaderSlot ?? /* @__PURE__ */ jsx(ShellPageTitle, { title: showTitle ? pageTitle : void 0, back: pageBack }),
1554
- /* @__PURE__ */ jsx("div", { ref: setHeaderSlot, className: "flex min-w-0 flex-1 items-center" })
1555
- ] }),
1556
- /* @__PURE__ */ jsx("div", { ref: setActionsSlot, className: "flex shrink-0 items-center gap-2" })
1557
- ] }),
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
+ ),
1558
1611
  /* @__PURE__ */ jsx("div", { className: "min-h-0 flex-1 overflow-y-auto pb-16 md:pb-0", children })
1559
1612
  ] })
1560
1613
  ] }) }),
@@ -1572,7 +1625,9 @@ function TopbarLayout({
1572
1625
  onSettings,
1573
1626
  onBilling,
1574
1627
  orgSwitcher,
1575
- userMenuSlot: user ? /* @__PURE__ */ jsx(DrawerUserRow, { user }) : void 0,
1628
+ userMenuSlot: drawerUserMenuSlot ?? (user ? /* @__PURE__ */ jsx(DrawerUserRow, { user }) : void 0),
1629
+ notificationSlot,
1630
+ unreadCount,
1576
1631
  onClose: () => setMobileMenuOpen(false)
1577
1632
  }
1578
1633
  )
@@ -1589,9 +1644,10 @@ function MinimalLayout({
1589
1644
  logo,
1590
1645
  user: _user,
1591
1646
  children,
1592
- headerEnd
1647
+ headerEnd,
1648
+ hasBottomNav = false
1593
1649
  }) {
1594
- 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: [
1595
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: [
1596
1652
  /* @__PURE__ */ jsx("div", { className: "flex items-center gap-3", children: logo ?? /* @__PURE__ */ jsx("span", { className: "text-lg font-bold", children: "App" }) }),
1597
1653
  headerEnd && /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", children: headerEnd })
@@ -1600,23 +1656,23 @@ function MinimalLayout({
1600
1656
  ] });
1601
1657
  }
1602
1658
  function MobileOverlay({ onClose, onNavigate, ...sidebarProps }) {
1603
- const [visible, setVisible] = React7.useState(false);
1604
- React7.useEffect(() => {
1659
+ const [visible, setVisible] = React2.useState(false);
1660
+ React2.useEffect(() => {
1605
1661
  const id = requestAnimationFrame(() => setVisible(true));
1606
1662
  return () => cancelAnimationFrame(id);
1607
1663
  }, []);
1608
- const close = React7.useCallback(() => {
1664
+ const close = React2.useCallback(() => {
1609
1665
  setVisible(false);
1610
1666
  window.setTimeout(onClose, 250);
1611
1667
  }, [onClose]);
1612
- React7.useEffect(() => {
1668
+ React2.useEffect(() => {
1613
1669
  const previous = document.body.style.overflow;
1614
1670
  document.body.style.overflow = "hidden";
1615
1671
  return () => {
1616
1672
  document.body.style.overflow = previous;
1617
1673
  };
1618
1674
  }, []);
1619
- React7.useEffect(() => {
1675
+ React2.useEffect(() => {
1620
1676
  const onKey = (e) => {
1621
1677
  if (e.key === "Escape") close();
1622
1678
  };
@@ -1659,19 +1715,7 @@ function MobileOverlay({ onClose, onNavigate, ...sidebarProps }) {
1659
1715
  )
1660
1716
  ] });
1661
1717
  }
1662
- var BOTTOM_NAV_ICONS = {
1663
- Home: Home,
1664
- Receipt,
1665
- ArrowLeftRight,
1666
- Calendar,
1667
- CreditCard: CreditCard,
1668
- Settings: Settings,
1669
- Menu: Menu,
1670
- MessageCircle,
1671
- Wallet,
1672
- PiggyBank,
1673
- Plus
1674
- };
1718
+ var bottomNavIcon = (name, fallback = Home) => (name ? ICON_MAP[name] : void 0) ?? fallback;
1675
1719
  function FloatingProfileAvatar({ user, onClick }) {
1676
1720
  if (!user) return null;
1677
1721
  const label = user.fullName || user.email;
@@ -1697,43 +1741,80 @@ function MobileBottomNav({
1697
1741
  items,
1698
1742
  currentPath,
1699
1743
  onNavigate,
1700
- onAction
1744
+ onAction,
1745
+ onMore,
1746
+ onAssistant,
1747
+ assistantOpen,
1748
+ moreOpen,
1749
+ retracted
1701
1750
  }) {
1702
1751
  const path = currentPath ?? "";
1703
- 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) => {
1704
- if (item.kind === "action") {
1705
- const ActionIcon = BOTTOM_NAV_ICONS[item.icon] ?? Plus;
1706
- return /* @__PURE__ */ jsx("div", { className: "flex flex-1 items-start justify-center", children: /* @__PURE__ */ jsx(
1707
- "button",
1708
- {
1709
- type: "button",
1710
- onClick: () => onAction?.(item.id),
1711
- "aria-label": item.label ?? "Add",
1712
- 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",
1713
- 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);
1714
1775
  }
1715
- ) }, 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
+ })
1716
1816
  }
1717
- const isActive = item.route === "/" ? path === "/" || path === "" : path === item.route || path.startsWith(item.route + "/");
1718
- const Icon = BOTTOM_NAV_ICONS[item.icon] ?? Home;
1719
- return /* @__PURE__ */ jsxs(
1720
- "button",
1721
- {
1722
- type: "button",
1723
- onClick: () => onNavigate?.(item.route),
1724
- className: cn(
1725
- "flex flex-1 flex-col items-center justify-center gap-0.5 px-1 py-1 transition-colors",
1726
- isActive ? "text-primary" : "text-muted-foreground"
1727
- ),
1728
- "aria-current": isActive ? "page" : void 0,
1729
- children: [
1730
- /* @__PURE__ */ jsx(Icon, { className: "h-5 w-5" }),
1731
- /* @__PURE__ */ jsx("span", { className: "text-xs", children: item.label })
1732
- ]
1733
- },
1734
- item.route
1735
- );
1736
- }) });
1817
+ );
1737
1818
  }
1738
1819
  function AppShell({
1739
1820
  variant,
@@ -1762,8 +1843,11 @@ function AppShell({
1762
1843
  sidebarFooterContent,
1763
1844
  bottomNav,
1764
1845
  onBottomNavAction,
1846
+ onAssistantToggle,
1847
+ assistantOpen,
1765
1848
  mobileHeader = "minimal",
1766
1849
  userMenuSlot,
1850
+ drawerUserMenuSlot,
1767
1851
  notificationSlot,
1768
1852
  unreadCount = 0,
1769
1853
  searchLabel,
@@ -1780,13 +1864,26 @@ function AppShell({
1780
1864
  rightRailResizeLabel
1781
1865
  }) {
1782
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]);
1783
1875
  const bottomBar = hasBottomNav ? /* @__PURE__ */ jsx(
1784
1876
  MobileBottomNav,
1785
1877
  {
1786
1878
  items: bottomNav,
1787
1879
  currentPath,
1788
1880
  onNavigate,
1789
- onAction: onBottomNavAction
1881
+ onAction: onBottomNavAction,
1882
+ onMore: () => setMobileDrawerOpen(!mobileDrawerOpen),
1883
+ moreOpen: mobileDrawerOpen,
1884
+ onAssistant: onAssistantToggle,
1885
+ assistantOpen,
1886
+ retracted: barRetracted
1790
1887
  }
1791
1888
  ) : null;
1792
1889
  const floatingAvatar = mobileHeader === "transparent" && user ? /* @__PURE__ */ jsx(FloatingProfileAvatar, { user, onClick: onProfile }) : null;
@@ -1820,7 +1917,8 @@ function AppShell({
1820
1917
  notificationSlot,
1821
1918
  unreadCount,
1822
1919
  searchLabel,
1823
- hasBottomNav,
1920
+ hasBottomNav: hasBottomNav && !barRetracted,
1921
+ barOwnsNav,
1824
1922
  mobileHeader,
1825
1923
  sidebarSectionLabels,
1826
1924
  sidebarNavKey,
@@ -1847,6 +1945,7 @@ function AppShell({
1847
1945
  billingLabel,
1848
1946
  userMenuExtras,
1849
1947
  userMenuSlot,
1948
+ drawerUserMenuSlot,
1850
1949
  orgSwitcher,
1851
1950
  frame: contentFrame,
1852
1951
  currentPath,
@@ -1855,13 +1954,14 @@ function AppShell({
1855
1954
  pageTitle,
1856
1955
  pageBack,
1857
1956
  notificationSlot,
1858
- hasBottomNav,
1957
+ hasBottomNav: hasBottomNav && !barRetracted,
1958
+ barOwnsNav,
1859
1959
  children
1860
1960
  }
1861
1961
  );
1862
1962
  break;
1863
1963
  case "minimal":
1864
- layoutElement = /* @__PURE__ */ jsx(MinimalLayout, { logo, user, headerEnd: topbarEnd, children });
1964
+ layoutElement = /* @__PURE__ */ jsx(MinimalLayout, { logo, user, headerEnd: topbarEnd, hasBottomNav: hasBottomNav && !barRetracted, children });
1865
1965
  break;
1866
1966
  default: {
1867
1967
  return null;
@@ -1897,13 +1997,13 @@ function SplitWithRightRail({
1897
1997
  maxWidth = 720,
1898
1998
  resizeLabel = "Resize panel"
1899
1999
  }) {
1900
- const [width, setWidth] = React7.useState(defaultWidth);
1901
- const [dragging, setDragging] = React7.useState(false);
1902
- const clamp = React7.useCallback(
2000
+ const [width, setWidth] = React2.useState(defaultWidth);
2001
+ const [dragging, setDragging] = React2.useState(false);
2002
+ const clamp = React2.useCallback(
1903
2003
  (value) => Math.min(maxWidth, Math.max(minWidth, value)),
1904
2004
  [minWidth, maxWidth]
1905
2005
  );
1906
- React7.useEffect(() => {
2006
+ React2.useEffect(() => {
1907
2007
  if (!dragging) return;
1908
2008
  const onMove = (event) => {
1909
2009
  event.preventDefault();
@@ -1925,7 +2025,7 @@ function SplitWithRightRail({
1925
2025
  document.body.style.cursor = previousCursor;
1926
2026
  };
1927
2027
  }, [dragging, clamp]);
1928
- React7.useEffect(() => {
2028
+ React2.useEffect(() => {
1929
2029
  if (dragging) return;
1930
2030
  onWidthChange?.(width);
1931
2031
  }, [dragging]);
@@ -1935,7 +2035,7 @@ function SplitWithRightRail({
1935
2035
  // chrome, next to the menu — painting them page-white put a bright strip
1936
2036
  // between the dark chrome and the rail, which read as a seam rather than
1937
2037
  // as space.
1938
- /* @__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: [
1939
2039
  /* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1", children }),
1940
2040
  /* @__PURE__ */ jsxs(
1941
2041
  "div",
@@ -2075,12 +2175,12 @@ function PageTabs({ actions, className, ...props }) {
2075
2175
  }
2076
2176
  );
2077
2177
  }
2078
- var NavTransitionContext = React7.createContext("slide");
2178
+ var NavTransitionContext = React2.createContext("slide");
2079
2179
  function NavTransitionProvider({ value, children }) {
2080
2180
  return /* @__PURE__ */ jsx(NavTransitionContext.Provider, { value, children });
2081
2181
  }
2082
2182
  function useNavTransition() {
2083
- return React7.useContext(NavTransitionContext);
2183
+ return React2.useContext(NavTransitionContext);
2084
2184
  }
2085
2185
  function resolveTransitionClass(transition, direction) {
2086
2186
  if (transition === "none") return "";
@@ -2115,15 +2215,15 @@ function PremiumBadge({ className }) {
2115
2215
  const tip = t("upgrade.badgeTooltip");
2116
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 }) });
2117
2217
  }
2118
- var ModuleLayoutContext = React7.createContext("rail");
2218
+ var ModuleLayoutContext = React2.createContext("rail");
2119
2219
  function ModuleLayoutProvider({ variant, children }) {
2120
2220
  return /* @__PURE__ */ jsx(ModuleLayoutContext.Provider, { value: variant, children });
2121
2221
  }
2122
2222
  function useModuleLayout() {
2123
- return React7.useContext(ModuleLayoutContext);
2223
+ return React2.useContext(ModuleLayoutContext);
2124
2224
  }
2125
2225
  function RailNavItem({ item }) {
2126
- const [expanded, setExpanded] = React7.useState(true);
2226
+ const [expanded, setExpanded] = React2.useState(true);
2127
2227
  const Icon = item.icon ? ICON_MAP[item.icon] ?? null : null;
2128
2228
  const hasChildren = item.children && item.children.length > 0;
2129
2229
  const hasActiveChild = hasChildren && item.children.some((c) => c.active);
@@ -2164,10 +2264,14 @@ function RailNavItem({ item }) {
2164
2264
  )) })
2165
2265
  ] });
2166
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
+ }
2167
2271
  function MobileTabs({ nav }) {
2168
- const scrollRef = React7.useRef(null);
2169
- const [expandedId, setExpandedId] = React7.useState(null);
2170
- React7.useEffect(() => {
2272
+ const scrollRef = React2.useRef(null);
2273
+ const [expandedId, setExpandedId] = React2.useState(null);
2274
+ React2.useEffect(() => {
2171
2275
  const container = scrollRef.current;
2172
2276
  if (!container) return;
2173
2277
  const activeEl = container.querySelector('[data-active="true"]');
@@ -2189,6 +2293,7 @@ function MobileTabs({ nav }) {
2189
2293
  "data-active": isActive || void 0,
2190
2294
  onClick: () => {
2191
2295
  if (hasChildren) {
2296
+ if (!isExpanded) defaultChildOf(item)?.onClick?.();
2192
2297
  setExpandedId(isExpanded ? null : item.id);
2193
2298
  } else {
2194
2299
  item.onClick?.();
@@ -2196,11 +2301,15 @@ function MobileTabs({ nav }) {
2196
2301
  }
2197
2302
  },
2198
2303
  className: cn(
2199
- "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]",
2200
2309
  isActive ? "bg-primary text-primary-foreground" : "bg-muted text-muted-foreground hover:text-foreground"
2201
2310
  ),
2202
2311
  children: [
2203
- Icon && /* @__PURE__ */ jsx(Icon, { className: "h-3 w-3" }),
2312
+ Icon && /* @__PURE__ */ jsx(Icon, { className: "h-3.5 w-3.5" }),
2204
2313
  item.label,
2205
2314
  item.premium && /* @__PURE__ */ jsx(PremiumBadge, {}),
2206
2315
  hasChildren && /* @__PURE__ */ jsx(ChevronDown, { className: cn("h-2.5 w-2.5 transition-transform", isExpanded && "rotate-180") })
@@ -2234,12 +2343,12 @@ function MobileTabs({ nav }) {
2234
2343
  })()
2235
2344
  ] });
2236
2345
  }
2237
- var BackStyleContext = React7.createContext("link");
2346
+ var BackStyleContext = React2.createContext("link");
2238
2347
  function BackStyleProvider({ style, children }) {
2239
2348
  return /* @__PURE__ */ jsx(BackStyleContext.Provider, { value: style, children });
2240
2349
  }
2241
2350
  function useBackStyle() {
2242
- return React7.useContext(BackStyleContext);
2351
+ return React2.useContext(BackStyleContext);
2243
2352
  }
2244
2353
  function SubpageHeader({ title, subtitle, icon, onBack, parentLabel, actions, backStyle, inline }) {
2245
2354
  const t = useTranslation();
@@ -2331,15 +2440,16 @@ function ShellSubpageHeader({
2331
2440
  parentLabel,
2332
2441
  actions
2333
2442
  }) {
2334
- usePageHeader(
2335
- /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [
2336
- Icon && !onBack && /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4 shrink-0 text-muted-foreground" }),
2337
- /* @__PURE__ */ jsx(PageHeaderCrumb, { title, parentLabel, onBack })
2338
- ] }),
2339
- { back: !!onBack }
2340
- );
2341
- if (!actions) return null;
2342
- 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
+ ] });
2343
2453
  }
2344
2454
  function ModuleHeader({ nav }) {
2345
2455
  function defaultChild(item) {
@@ -2389,12 +2499,12 @@ function ModulePage({ title: _title, subtitle, nav, children, className, headerA
2389
2499
  ] }) }),
2390
2500
  /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
2391
2501
  showHeader && headerAction && /* @__PURE__ */ jsx(PageHeaderActions, { children: headerAction }),
2392
- /* @__PURE__ */ jsx(MobileTabs, { nav }),
2502
+ !onSubpage && /* @__PURE__ */ jsx(MobileTabs, { nav }),
2393
2503
  /* @__PURE__ */ jsx(PageTransition, { transitionKey, direction, children })
2394
2504
  ] })
2395
2505
  ] });
2396
2506
  }
2397
2507
 
2398
- 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 };
2399
- //# sourceMappingURL=chunk-NA3GE4H3.js.map
2400
- //# sourceMappingURL=chunk-NA3GE4H3.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