@burdenoff/fe-libs 2026.819.4 → 2026.823.1

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 (239) hide show
  1. package/README.md +66 -0
  2. package/dist/button/Button.d.ts +1 -1
  3. package/dist/chrome/AppShellLayout.d.ts +14 -0
  4. package/dist/chrome/AppShellLayout.d.ts.map +1 -1
  5. package/dist/chrome/AppShellLayout.js +13 -10
  6. package/dist/chrome/DynamicFooter.d.ts +6 -1
  7. package/dist/chrome/DynamicFooter.d.ts.map +1 -1
  8. package/dist/chrome/DynamicFooter.js +21 -20
  9. package/dist/chrome/Footer.d.ts +6 -0
  10. package/dist/chrome/Footer.d.ts.map +1 -1
  11. package/dist/chrome/Footer.js +46 -45
  12. package/dist/chrome/FooterBar.d.ts +6 -1
  13. package/dist/chrome/FooterBar.d.ts.map +1 -1
  14. package/dist/chrome/FooterBar.js +28 -26
  15. package/dist/chrome/HeaderBar.d.ts.map +1 -1
  16. package/dist/chrome/HeaderBar.js +3 -0
  17. package/dist/chrome/MobileTabBar.d.ts +26 -0
  18. package/dist/chrome/MobileTabBar.d.ts.map +1 -0
  19. package/dist/chrome/MobileTabBar.js +194 -0
  20. package/dist/chrome/NativePageTransition.d.ts +10 -0
  21. package/dist/chrome/NativePageTransition.d.ts.map +1 -0
  22. package/dist/chrome/NativePageTransition.js +19 -0
  23. package/dist/chrome/NativeTopBar.d.ts +21 -0
  24. package/dist/chrome/NativeTopBar.d.ts.map +1 -0
  25. package/dist/chrome/NativeTopBar.js +72 -0
  26. package/dist/chrome/NotificationCenter.d.ts.map +1 -1
  27. package/dist/chrome/NotificationCenter.js +70 -65
  28. package/dist/chrome/ProductSideNav.d.ts.map +1 -1
  29. package/dist/chrome/ProductSideNav.js +44 -74
  30. package/dist/chrome/SwipeNavigation.d.ts +36 -0
  31. package/dist/chrome/SwipeNavigation.d.ts.map +1 -0
  32. package/dist/chrome/SwipeNavigation.js +111 -0
  33. package/dist/chrome/index.d.ts +13 -7
  34. package/dist/chrome/index.d.ts.map +1 -1
  35. package/dist/chrome/mobileTabBar/selectTabs.d.ts +45 -0
  36. package/dist/chrome/mobileTabBar/selectTabs.d.ts.map +1 -0
  37. package/dist/chrome/mobileTabBar/selectTabs.js +28 -0
  38. package/dist/chrome/mobileTabBar/useMobileTabBarVisible.d.ts +18 -0
  39. package/dist/chrome/mobileTabBar/useMobileTabBarVisible.d.ts.map +1 -0
  40. package/dist/chrome/mobileTabBar/useMobileTabBarVisible.js +16 -0
  41. package/dist/chrome/mobileTabBar/useVisibleNavItems.d.ts +18 -0
  42. package/dist/chrome/mobileTabBar/useVisibleNavItems.d.ts.map +1 -0
  43. package/dist/chrome/mobileTabBar/useVisibleNavItems.js +41 -0
  44. package/dist/chrome/swipe/swipeMath.d.ts +25 -0
  45. package/dist/chrome/swipe/swipeMath.d.ts.map +1 -0
  46. package/dist/chrome/swipe/swipeMath.js +14 -0
  47. package/dist/chrome/swipe/swipeState.d.ts +10 -0
  48. package/dist/chrome/swipe/swipeState.d.ts.map +1 -0
  49. package/dist/chrome/swipe/swipeState.js +11 -0
  50. package/dist/chrome.js +7 -1
  51. package/dist/shared/components/DesktopUpdateToast.d.ts +9 -0
  52. package/dist/shared/components/DesktopUpdateToast.d.ts.map +1 -0
  53. package/dist/shared/components/DesktopUpdateToast.js +39 -0
  54. package/dist/shared/components/PWAInstallBanner.js +1 -1
  55. package/dist/shared/components/SSORedirectGuard.d.ts.map +1 -1
  56. package/dist/shared/components/SSORedirectGuard.js +41 -45
  57. package/dist/shared/components/index.d.ts +2 -0
  58. package/dist/shared/components/index.d.ts.map +1 -1
  59. package/dist/shared/config/authBridgeUrls.d.ts +10 -0
  60. package/dist/shared/config/authBridgeUrls.d.ts.map +1 -1
  61. package/dist/shared/config/authBridgeUrls.js +16 -12
  62. package/dist/shared/config/gatewayUrls.d.ts.map +1 -1
  63. package/dist/shared/config/gatewayUrls.js +22 -19
  64. package/dist/shared/config/index.d.ts +1 -1
  65. package/dist/shared/config/index.d.ts.map +1 -1
  66. package/dist/shared/events/index.d.ts +13 -1
  67. package/dist/shared/events/index.d.ts.map +1 -1
  68. package/dist/shared/hooks/shell/useNavRegistry.d.ts +7 -0
  69. package/dist/shared/hooks/shell/useNavRegistry.d.ts.map +1 -1
  70. package/dist/shared/hooks/usePWA.d.ts.map +1 -1
  71. package/dist/shared/hooks/usePWA.js +34 -30
  72. package/dist/shared/native/consumers.d.ts +15 -1
  73. package/dist/shared/native/consumers.d.ts.map +1 -1
  74. package/dist/shared/native/consumers.js +26 -7
  75. package/dist/shared/native/desktop.d.ts +123 -0
  76. package/dist/shared/native/desktop.d.ts.map +1 -0
  77. package/dist/shared/native/desktop.js +280 -0
  78. package/dist/shared/native/entry.d.ts +39 -0
  79. package/dist/shared/native/entry.d.ts.map +1 -0
  80. package/dist/shared/native/entry.js +13 -0
  81. package/dist/shared/native/hooks/index.d.ts +10 -0
  82. package/dist/shared/native/hooks/index.d.ts.map +1 -1
  83. package/dist/shared/native/hooks/index.js +6 -0
  84. package/dist/shared/native/hooks/useActionSheet.d.ts +63 -0
  85. package/dist/shared/native/hooks/useActionSheet.d.ts.map +1 -0
  86. package/dist/shared/native/hooks/useActionSheet.js +45 -0
  87. package/dist/shared/native/hooks/useDesktop.d.ts +15 -0
  88. package/dist/shared/native/hooks/useDesktop.d.ts.map +1 -0
  89. package/dist/shared/native/hooks/useDesktop.js +18 -0
  90. package/dist/shared/native/hooks/useDesktopUpdateState.d.ts +7 -0
  91. package/dist/shared/native/hooks/useDesktopUpdateState.d.ts.map +1 -0
  92. package/dist/shared/native/hooks/useDesktopUpdateState.js +20 -0
  93. package/dist/shared/native/hooks/useDesktopWindowState.d.ts +7 -0
  94. package/dist/shared/native/hooks/useDesktopWindowState.d.ts.map +1 -0
  95. package/dist/shared/native/hooks/useDesktopWindowState.js +9 -0
  96. package/dist/shared/native/hooks/useHaptics.d.ts +1 -0
  97. package/dist/shared/native/hooks/useHaptics.d.ts.map +1 -1
  98. package/dist/shared/native/hooks/useHaptics.js +6 -6
  99. package/dist/shared/native/hooks/useNetworkStatus.d.ts +23 -0
  100. package/dist/shared/native/hooks/useNetworkStatus.d.ts.map +1 -0
  101. package/dist/shared/native/hooks/useNetworkStatus.js +29 -0
  102. package/dist/shared/native/hooks/usePullToRefresh.d.ts +62 -0
  103. package/dist/shared/native/hooks/usePullToRefresh.d.ts.map +1 -0
  104. package/dist/shared/native/hooks/usePullToRefresh.js +84 -0
  105. package/dist/shared/native/index.d.ts +6 -1
  106. package/dist/shared/native/index.d.ts.map +1 -1
  107. package/dist/shared/native/platform.d.ts.map +1 -1
  108. package/dist/shared/native/platform.js +2 -1
  109. package/dist/shared/native/purchases.d.ts +7 -1
  110. package/dist/shared/native/purchases.d.ts.map +1 -1
  111. package/dist/shared/native/purchases.js +3 -3
  112. package/dist/shared/native/types.d.ts +136 -0
  113. package/dist/shared/native/types.d.ts.map +1 -1
  114. package/dist/shared/providers/AppShellProvider.d.ts.map +1 -1
  115. package/dist/shared/providers/AppShellProvider.js +117 -115
  116. package/dist/shared/providers/shell/ThemeProvider.d.ts.map +1 -1
  117. package/dist/shared/providers/shell/ThemeProvider.js +64 -51
  118. package/dist/shared/providers/shell/authProvider/useSsoDepositBootGuard.d.ts.map +1 -1
  119. package/dist/shared/providers/shell/authProvider/useSsoDepositBootGuard.js +25 -29
  120. package/dist/shared/utils/gatewayFetchRewrite.d.ts.map +1 -1
  121. package/dist/shared/utils/gatewayFetchRewrite.js +87 -84
  122. package/dist/shared-components.js +2 -1
  123. package/dist/shared-config.js +10 -10
  124. package/dist/shared-native.js +21 -13
  125. package/dist/shared.js +120 -111
  126. package/dist/shell-native/actionSheet.d.ts +7 -0
  127. package/dist/shell-native/actionSheet.d.ts.map +1 -0
  128. package/dist/shell-native/actionSheet.js +26 -0
  129. package/dist/shell-native/app-lifecycle.d.ts +4 -0
  130. package/dist/shell-native/app-lifecycle.d.ts.map +1 -0
  131. package/dist/shell-native/app-lifecycle.js +12 -0
  132. package/dist/shell-native/auth-navigation.d.ts +4 -0
  133. package/dist/shell-native/auth-navigation.d.ts.map +1 -0
  134. package/dist/shell-native/auth-navigation.js +95 -0
  135. package/dist/shell-native/back-button.d.ts +8 -0
  136. package/dist/shell-native/back-button.d.ts.map +1 -0
  137. package/dist/shell-native/back-button.js +49 -0
  138. package/dist/shell-native/boot.d.ts +10 -0
  139. package/dist/shell-native/boot.d.ts.map +1 -0
  140. package/dist/shell-native/boot.js +65 -0
  141. package/dist/shell-native/bridge.d.ts +7 -0
  142. package/dist/shell-native/bridge.d.ts.map +1 -0
  143. package/dist/shell-native/bridge.js +30 -0
  144. package/dist/shell-native/browser.d.ts +12 -0
  145. package/dist/shell-native/browser.d.ts.map +1 -0
  146. package/dist/shell-native/browser.js +45 -0
  147. package/dist/shell-native/capacitor-config.d.ts +21 -0
  148. package/dist/shell-native/capacitor-config.d.ts.map +1 -0
  149. package/dist/shell-native/capacitor-config.js +103 -0
  150. package/dist/shell-native/clipboard.d.ts +3 -0
  151. package/dist/shell-native/clipboard.d.ts.map +1 -0
  152. package/dist/shell-native/clipboard.js +26 -0
  153. package/dist/shell-native/deep-link.d.ts +19 -0
  154. package/dist/shell-native/deep-link.d.ts.map +1 -0
  155. package/dist/shell-native/deep-link.js +53 -0
  156. package/dist/shell-native/desktop-bridge.d.ts +14 -0
  157. package/dist/shell-native/desktop-bridge.d.ts.map +1 -0
  158. package/dist/shell-native/desktop-bridge.js +17 -0
  159. package/dist/shell-native/device.d.ts +4 -0
  160. package/dist/shell-native/device.d.ts.map +1 -0
  161. package/dist/shell-native/device.js +27 -0
  162. package/dist/shell-native/dialog.d.ts +4 -0
  163. package/dist/shell-native/dialog.d.ts.map +1 -0
  164. package/dist/shell-native/dialog.js +13 -0
  165. package/dist/shell-native/feedback.d.ts +6 -0
  166. package/dist/shell-native/feedback.d.ts.map +1 -0
  167. package/dist/shell-native/feedback.js +29 -0
  168. package/dist/shell-native/index.d.ts +54 -0
  169. package/dist/shell-native/index.d.ts.map +1 -0
  170. package/dist/shell-native/keyboard.d.ts +15 -0
  171. package/dist/shell-native/keyboard.d.ts.map +1 -0
  172. package/dist/shell-native/keyboard.js +42 -0
  173. package/dist/shell-native/network.d.ts +9 -0
  174. package/dist/shell-native/network.d.ts.map +1 -0
  175. package/dist/shell-native/network.js +40 -0
  176. package/dist/shell-native/ota.d.ts +3 -0
  177. package/dist/shell-native/ota.d.ts.map +1 -0
  178. package/dist/shell-native/ota.js +21 -0
  179. package/dist/shell-native/platform.d.ts +21 -0
  180. package/dist/shell-native/platform.d.ts.map +1 -0
  181. package/dist/shell-native/platform.js +51 -0
  182. package/dist/shell-native/preview.d.ts +18 -0
  183. package/dist/shell-native/preview.d.ts.map +1 -0
  184. package/dist/shell-native/preview.js +124 -0
  185. package/dist/shell-native/push.d.ts +14 -0
  186. package/dist/shell-native/push.d.ts.map +1 -0
  187. package/dist/shell-native/push.js +62 -0
  188. package/dist/shell-native/pwa.d.ts +14 -0
  189. package/dist/shell-native/pwa.d.ts.map +1 -0
  190. package/dist/shell-native/pwa.js +49 -0
  191. package/dist/shell-native/service-worker.d.ts +5 -0
  192. package/dist/shell-native/service-worker.d.ts.map +1 -0
  193. package/dist/shell-native/service-worker.js +144 -0
  194. package/dist/shell-native/share.d.ts +4 -0
  195. package/dist/shell-native/share.d.ts.map +1 -0
  196. package/dist/shell-native/share.js +19 -0
  197. package/dist/shell-native/splash.d.ts +16 -0
  198. package/dist/shell-native/splash.d.ts.map +1 -0
  199. package/dist/shell-native/splash.js +25 -0
  200. package/dist/shell-native/system-bars.d.ts +24 -0
  201. package/dist/shell-native/system-bars.d.ts.map +1 -0
  202. package/dist/shell-native/system-bars.js +49 -0
  203. package/dist/shell-native/toast.d.ts +4 -0
  204. package/dist/shell-native/toast.d.ts.map +1 -0
  205. package/dist/shell-native/toast.js +16 -0
  206. package/dist/shell-native/types.d.ts +292 -0
  207. package/dist/shell-native/types.d.ts.map +1 -0
  208. package/dist/shell-native/web-manifest.d.ts +36 -0
  209. package/dist/shell-native/web-manifest.d.ts.map +1 -0
  210. package/dist/shell-native/web-manifest.js +55 -0
  211. package/dist/shell-native.d.ts +2 -0
  212. package/dist/shell-native.js +29 -0
  213. package/dist/ui/action-sheet.d.ts +29 -0
  214. package/dist/ui/action-sheet.d.ts.map +1 -0
  215. package/dist/ui/action-sheet.js +53 -0
  216. package/dist/ui/badge.d.ts +1 -1
  217. package/dist/ui/button.d.ts +1 -1
  218. package/dist/ui/index.d.ts +6 -0
  219. package/dist/ui/index.d.ts.map +1 -1
  220. package/dist/ui/native-welcome.d.ts +61 -0
  221. package/dist/ui/native-welcome.d.ts.map +1 -0
  222. package/dist/ui/native-welcome.js +152 -0
  223. package/dist/ui/offline-banner.d.ts +22 -0
  224. package/dist/ui/offline-banner.d.ts.map +1 -0
  225. package/dist/ui/offline-banner.js +34 -0
  226. package/dist/ui/pull-to-refresh.d.ts +29 -0
  227. package/dist/ui/pull-to-refresh.d.ts.map +1 -0
  228. package/dist/ui/pull-to-refresh.js +44 -0
  229. package/dist/ui/pwa-update-toast.d.ts +34 -0
  230. package/dist/ui/pwa-update-toast.d.ts.map +1 -0
  231. package/dist/ui/pwa-update-toast.js +43 -0
  232. package/dist/ui/select.d.ts +38 -0
  233. package/dist/ui/select.d.ts.map +1 -1
  234. package/dist/ui/select.js +162 -61
  235. package/dist/ui.js +40 -35
  236. package/package.json +93 -4
  237. package/src/shared/styles/app-shell.css +3 -0
  238. package/src/shared/styles/native.css +577 -0
  239. package/dist/node_modules/@capacitor/core/dist/index.js +0 -268
@@ -0,0 +1,577 @@
1
+ /**
2
+ * Native platform stylesheet — installed-app look & feel.
3
+ *
4
+ * Imported automatically by app-shell.css. Rules are gated on the platform
5
+ * classes that `@burdenoff/fe-libs/shell-native` (`bootNativeShell`) puts on
6
+ * <body>: `.platform-ios`, `.platform-android`, `.platform-web`,
7
+ * `.platform-native` (ios|android), `.platform-preview` (presentation preview
8
+ * via `?__nativePreview=`), plus `html[data-desktop-*]` set by the Electron
9
+ * bridge (`bootDesktopShell`).
10
+ *
11
+ * Token-only: every color comes from --color-* tokens. No raw colors.
12
+ *
13
+ * Sections are owned by workstreams (do not reorder the anchors):
14
+ * 1. variables (WS-B)
15
+ * 2. base reset (WS-B)
16
+ * 3. tab bar / top bar (WS-B)
17
+ * 4. swipe + transitions (WS-B)
18
+ * 5. welcome / pwa / misc (WS-C)
19
+ * 6. desktop (Electron) (WS-D)
20
+ */
21
+
22
+ /* === 1. variables (WS-B) === */
23
+ :root {
24
+ --safe-top: env(safe-area-inset-top, 0px);
25
+ --safe-bottom: env(safe-area-inset-bottom, 0px);
26
+ --safe-left: env(safe-area-inset-left, 0px);
27
+ --safe-right: env(safe-area-inset-right, 0px);
28
+ --keyboard-height: 0px;
29
+ --mobile-tabbar-height: 0px;
30
+ --native-topbar-height: 0px;
31
+ --swipe-progress: 0;
32
+
33
+ /* Native chrome metrics (platform overrides in section 3). */
34
+ --native-tabbar-row-height: 56px;
35
+ --native-topbar-row-height: 48px;
36
+ /* Page-transition duration; overridable per instance via the
37
+ `durationMs` prop on <NativePageTransition>. */
38
+ --page-transition-duration: 260ms;
39
+ --swipe-transition-duration: 220ms;
40
+ }
41
+
42
+ .platform-ios {
43
+ --native-tabbar-row-height: 49px;
44
+ --native-topbar-row-height: 44px;
45
+ --page-transition-duration: 280ms;
46
+ /* iOS system text stack — matches the platform's own chrome. */
47
+ --font-family-body: -apple-system, BlinkMacSystemFont, 'SF Pro Text', system-ui, sans-serif;
48
+ }
49
+
50
+ .platform-android {
51
+ --native-tabbar-row-height: 80px;
52
+ --native-topbar-row-height: 56px;
53
+ --page-transition-duration: 210ms;
54
+ --font-family-body: Roboto, system-ui, sans-serif;
55
+ }
56
+
57
+ /* === 2. base reset (WS-B) === */
58
+
59
+ /* Appendix-D reset: a WebView should not feel like a browser. The platform
60
+ classes live on <body>, so <html> is reached with :has(). */
61
+ .platform-native,
62
+ html:has(body.platform-native) {
63
+ -webkit-tap-highlight-color: transparent;
64
+ overscroll-behavior: none;
65
+ -webkit-touch-callout: none;
66
+ -webkit-text-size-adjust: 100%;
67
+ text-size-adjust: 100%;
68
+ }
69
+
70
+ /* The platform font stacks are redefined on the platform class (section 1);
71
+ body consumes --font-family-body (see app-shell.css), so the override lands
72
+ automatically — restated here so it survives a shell that sets its own
73
+ body font-family. */
74
+ .platform-native {
75
+ font-family: var(--font-family-body);
76
+ }
77
+
78
+ /* Shell chrome is furniture, not content — never selectable... */
79
+ .platform-native [data-shell-chrome] {
80
+ -webkit-user-select: none;
81
+ user-select: none;
82
+ }
83
+
84
+ /* ...but real text input and anything explicitly marked stays selectable. */
85
+ .platform-native input,
86
+ .platform-native textarea,
87
+ .platform-native [contenteditable],
88
+ .platform-native .selectable {
89
+ -webkit-user-select: text;
90
+ user-select: text;
91
+ }
92
+
93
+ /* Kill the 300ms tap delay everywhere; the swipe root still needs vertical
94
+ panning so the page can scroll while the edge gesture is armed. */
95
+ .platform-native * {
96
+ touch-action: manipulation;
97
+ }
98
+
99
+ .platform-native [data-swipe-root],
100
+ .platform-native [data-shell-scroll-container] {
101
+ touch-action: pan-y;
102
+ }
103
+
104
+ /* Hover states never resolve on touch — use press states instead. */
105
+ @media (hover: none) {
106
+ .platform-native :is(button, [role='button'], [role='tab'], a):active {
107
+ opacity: 0.72;
108
+ transform: scale(0.98);
109
+ transition:
110
+ transform 80ms ease-out,
111
+ opacity 80ms ease-out;
112
+ }
113
+ }
114
+
115
+ /* Native apps have no visible scrollbars. */
116
+ .platform-native ::-webkit-scrollbar {
117
+ display: none;
118
+ }
119
+
120
+ .platform-native * {
121
+ scrollbar-width: none;
122
+ }
123
+
124
+ /* Web-only chrome (install banners, browser CTAs, cross-product switcher). */
125
+ .platform-native .web-only {
126
+ display: none !important;
127
+ }
128
+
129
+ /* 44px minimum touch targets — scoped to shell chrome so product tables and
130
+ dense toolbars are not forcibly inflated. `.dense` opts a subtree out. */
131
+ .platform-native
132
+ :is([data-shell-chrome], [data-mobile-tabbar], [data-native-topbar])
133
+ :is(button, a, [role='button'], [role='tab']) {
134
+ min-height: 44px;
135
+ min-width: 44px;
136
+ }
137
+
138
+ .platform-native
139
+ :is([data-shell-chrome], [data-mobile-tabbar], [data-native-topbar])
140
+ .dense
141
+ :is(button, a, [role='button'], [role='tab']) {
142
+ min-height: 0;
143
+ min-width: 0;
144
+ }
145
+
146
+ /* Safe areas are owned by ONE layer. On native (or any shell that opted into
147
+ native-compact mode) that layer is AppShellLayout, so #root must not add a
148
+ second inset on top of it (app-shell.css applies env() padding for web). */
149
+ .platform-native #root,
150
+ #root:has([data-native-shell]) {
151
+ padding: 0;
152
+ }
153
+
154
+ /* Keyboard handling — `body.keyboard-open` + `--keyboard-height` are set by
155
+ the shell-native boot runtime from the Capacitor keyboard events. */
156
+ body.keyboard-open [data-mobile-tabbar]:not([data-keyboard-keep]) {
157
+ display: none;
158
+ }
159
+
160
+ body.keyboard-open [data-native-shell] main {
161
+ padding-bottom: var(--keyboard-height);
162
+ }
163
+
164
+ /* Content clears the fixed tab bar (the bar publishes its measured height). */
165
+ .platform-native [data-native-shell] main {
166
+ padding-bottom: var(--mobile-tabbar-height);
167
+ }
168
+
169
+ /* === 3. tab bar / top bar (WS-B) === */
170
+
171
+ /* ---- MobileTabBar (bottom tabs) ---- */
172
+ [data-mobile-tabbar] {
173
+ min-height: var(--native-tabbar-row-height);
174
+ padding-bottom: max(var(--safe-bottom), env(safe-area-inset-bottom, 0px));
175
+ padding-left: max(var(--safe-left), env(safe-area-inset-left, 0px));
176
+ padding-right: max(var(--safe-right), env(safe-area-inset-right, 0px));
177
+ border-top: 1px solid var(--color-border-subtle);
178
+ }
179
+
180
+ [data-mobile-tabbar] .mobile-tab {
181
+ padding-top: 6px;
182
+ padding-bottom: 6px;
183
+ }
184
+
185
+ [data-mobile-tabbar] .mobile-tab__icon > svg {
186
+ width: 24px;
187
+ height: 24px;
188
+ }
189
+
190
+ [data-mobile-tabbar] .mobile-tab__label {
191
+ font-size: 11px;
192
+ line-height: 1.2;
193
+ }
194
+
195
+ /* iOS: translucent, hairline separator, 24px glyph over a 10px label. */
196
+ .platform-ios [data-mobile-tabbar] {
197
+ background-color: color-mix(in srgb, var(--color-bg-surface) 80%, transparent);
198
+ -webkit-backdrop-filter: saturate(180%) blur(20px);
199
+ backdrop-filter: saturate(180%) blur(20px);
200
+ }
201
+
202
+ .platform-ios [data-mobile-tabbar] .mobile-tab__label {
203
+ font-size: 10px;
204
+ letter-spacing: 0.01em;
205
+ }
206
+
207
+ /* Android: Material 3 navigation bar — 80px row, active pill behind the icon. */
208
+ .platform-android [data-mobile-tabbar] {
209
+ background-color: var(--color-bg-surface);
210
+ }
211
+
212
+ .platform-android [data-mobile-tabbar] .mobile-tab {
213
+ padding-top: 12px;
214
+ padding-bottom: 16px;
215
+ gap: 4px;
216
+ }
217
+
218
+ .platform-android [data-mobile-tabbar] .mobile-tab__icon {
219
+ border-radius: 9999px;
220
+ padding: 4px 20px;
221
+ transition:
222
+ background-color 150ms ease-out,
223
+ padding 150ms ease-out;
224
+ }
225
+
226
+ .platform-android [data-mobile-tabbar] .mobile-tab[data-active] .mobile-tab__icon {
227
+ background-color: var(--color-bg-accentSubtle);
228
+ }
229
+
230
+ .platform-android [data-mobile-tabbar] .mobile-tab__label {
231
+ font-size: 12px;
232
+ }
233
+
234
+ /* ---- NativeTopBar ---- */
235
+ [data-native-topbar] {
236
+ padding-top: max(var(--safe-top), env(safe-area-inset-top, 0px));
237
+ padding-left: max(var(--safe-left), env(safe-area-inset-left, 0px));
238
+ padding-right: max(var(--safe-right), env(safe-area-inset-right, 0px));
239
+ border-bottom: 1px solid var(--color-border-subtle);
240
+ }
241
+
242
+ [data-native-topbar] .native-topbar__row {
243
+ min-height: var(--native-topbar-row-height);
244
+ padding-left: 8px;
245
+ padding-right: 8px;
246
+ }
247
+
248
+ /* iOS: blurred translucent bar, title optically centered in the row. */
249
+ .platform-ios [data-native-topbar] {
250
+ background-color: color-mix(in srgb, var(--color-bg-surface) 80%, transparent);
251
+ -webkit-backdrop-filter: saturate(180%) blur(20px);
252
+ backdrop-filter: saturate(180%) blur(20px);
253
+ }
254
+
255
+ .platform-ios [data-native-topbar] .native-topbar__title {
256
+ text-align: center;
257
+ }
258
+
259
+ /* Android: opaque 56px bar, title left-aligned next to the back arrow, and no
260
+ iOS-style back TEXT label. */
261
+ .platform-android [data-native-topbar] {
262
+ background-color: var(--color-bg-surface);
263
+ }
264
+
265
+ .platform-android [data-native-topbar] .native-topbar__backlabel {
266
+ display: none;
267
+ }
268
+
269
+ .platform-android [data-native-topbar] .native-topbar__title {
270
+ position: static;
271
+ margin: 0;
272
+ max-width: none;
273
+ justify-content: flex-start;
274
+ flex: 1 1 auto;
275
+ text-align: left;
276
+ padding-left: 8px;
277
+ }
278
+
279
+ .platform-android [data-native-topbar] .native-topbar__lead {
280
+ flex: 0 0 auto;
281
+ }
282
+
283
+ /* === 4. swipe + transitions (WS-B) === */
284
+
285
+ /* ---- Edge-swipe navigation (SwipeNavigation) ---- */
286
+ /* The swiped element carries data-swipe-dir + --swipe-progress (0..1). */
287
+ [data-swipe-dir] {
288
+ will-change: transform;
289
+ }
290
+
291
+ [data-swipe-dir='back'] {
292
+ transform: translateX(calc(var(--swipe-progress, 0) * 100%));
293
+ }
294
+
295
+ [data-swipe-dir='forward'] {
296
+ transform: translateX(calc(var(--swipe-progress, 0) * -100%));
297
+ }
298
+
299
+ /* Commit / spring-back animate; the drag itself tracks the finger 1:1. */
300
+ [data-swipe-dir][data-swipe-committing] {
301
+ transition: transform var(--swipe-transition-duration) cubic-bezier(0.32, 0.72, 0, 1);
302
+ }
303
+
304
+ /* Dim the page underneath as it slides away. */
305
+ [data-swipe-dir]::before {
306
+ content: '';
307
+ position: fixed;
308
+ inset: 0;
309
+ pointer-events: none;
310
+ z-index: 1;
311
+ background-color: var(--color-bg-inverse);
312
+ opacity: calc(var(--swipe-progress, 0) * 0.18);
313
+ }
314
+
315
+ /* ---- Page transitions (NativePageTransition) ---- */
316
+ [data-page-transition='enter-forward'] {
317
+ animation: boff-page-enter-forward var(--page-transition-duration) cubic-bezier(0.32, 0.72, 0, 1)
318
+ both;
319
+ }
320
+
321
+ [data-page-transition='enter-back'] {
322
+ animation: boff-page-enter-back var(--page-transition-duration) cubic-bezier(0.32, 0.72, 0, 1)
323
+ both;
324
+ }
325
+
326
+ /* Android replaces the iOS slide with a fade-through + 8px shared-axis Y. */
327
+ .platform-android [data-page-transition='enter-forward'],
328
+ .platform-android [data-page-transition='enter-back'] {
329
+ animation: boff-page-enter-android var(--page-transition-duration) ease-out both;
330
+ }
331
+
332
+ @keyframes boff-page-enter-forward {
333
+ from {
334
+ transform: translateX(30%);
335
+ opacity: 0.6;
336
+ }
337
+ to {
338
+ transform: translateX(0);
339
+ opacity: 1;
340
+ }
341
+ }
342
+
343
+ @keyframes boff-page-enter-back {
344
+ from {
345
+ transform: translateX(-15%);
346
+ opacity: 0.6;
347
+ }
348
+ to {
349
+ transform: translateX(0);
350
+ opacity: 1;
351
+ }
352
+ }
353
+
354
+ @keyframes boff-page-enter-android {
355
+ from {
356
+ transform: translateY(8px);
357
+ opacity: 0;
358
+ }
359
+ to {
360
+ transform: translateY(0);
361
+ opacity: 1;
362
+ }
363
+ }
364
+
365
+ /* Reduced motion: no transforms, no animations — navigation still happens,
366
+ it just lands instantly. */
367
+ @media (prefers-reduced-motion: reduce) {
368
+ [data-swipe-dir],
369
+ [data-swipe-dir='back'],
370
+ [data-swipe-dir='forward'] {
371
+ transform: none;
372
+ transition: none;
373
+ will-change: auto;
374
+ }
375
+
376
+ [data-swipe-dir]::before {
377
+ display: none;
378
+ }
379
+
380
+ [data-page-transition] {
381
+ animation: none;
382
+ }
383
+
384
+ .platform-native :is(button, [role='button'], [role='tab'], a):active {
385
+ transform: none;
386
+ transition: none;
387
+ }
388
+ }
389
+
390
+ /* === 5. welcome / pwa / misc (WS-C) === */
391
+
392
+ /* Native welcome (installed-app entry screen) ---------------------------- */
393
+ [data-native-welcome] {
394
+ /* Full-bleed entry surface: no document scroll, the carousel scrolls instead. */
395
+ overscroll-behavior: none;
396
+ }
397
+
398
+ .platform-ios [data-native-welcome] h1 {
399
+ /* iOS large-title feel: tighter tracking, heavier weight. */
400
+ letter-spacing: -0.02em;
401
+ font-weight: 700;
402
+ }
403
+
404
+ .platform-android [data-native-welcome] h1 {
405
+ /* Material headline: normal tracking, medium weight. */
406
+ letter-spacing: 0;
407
+ font-weight: 600;
408
+ }
409
+
410
+ [data-native-welcome-carousel] {
411
+ scrollbar-width: none;
412
+ -ms-overflow-style: none;
413
+ /* Horizontal swipes belong to the carousel, never the back-swipe gesture. */
414
+ overscroll-behavior-x: contain;
415
+ touch-action: pan-x;
416
+ }
417
+
418
+ [data-native-welcome-carousel]::-webkit-scrollbar {
419
+ display: none;
420
+ }
421
+
422
+ @media (prefers-reduced-motion: reduce) {
423
+ [data-native-welcome-carousel] {
424
+ scroll-behavior: auto;
425
+ }
426
+ }
427
+
428
+ /* Offline banner --------------------------------------------------------- */
429
+ [data-offline-banner] {
430
+ /* Sits under the notch and under the native top bar when one is mounted. */
431
+ top: calc(
432
+ max(var(--safe-top, 0px), env(safe-area-inset-top, 0px)) + var(--native-topbar-height, 0px)
433
+ );
434
+ }
435
+
436
+ @media (prefers-reduced-motion: no-preference) {
437
+ [data-offline-banner] {
438
+ animation: boff-native-slide-down 200ms var(--motion-easing-out, ease-out) both;
439
+ }
440
+ }
441
+
442
+ @keyframes boff-native-slide-down {
443
+ from {
444
+ opacity: 0;
445
+ transform: translateY(-100%);
446
+ }
447
+ to {
448
+ opacity: 1;
449
+ transform: none;
450
+ }
451
+ }
452
+
453
+ /* Pull-to-refresh -------------------------------------------------------- */
454
+ [data-pull-to-refresh] {
455
+ /* Keep the browser's own overscroll/refresh out of the gesture. */
456
+ overscroll-behavior-y: contain;
457
+ -webkit-overflow-scrolling: touch;
458
+ }
459
+
460
+ [data-pull-to-refresh-indicator] {
461
+ transition:
462
+ transform 180ms var(--motion-easing-out, ease-out),
463
+ opacity 180ms var(--motion-easing-out, ease-out);
464
+ }
465
+
466
+ [data-pull-to-refresh][data-state='pulling'] [data-pull-to-refresh-indicator],
467
+ [data-pull-to-refresh][data-state='armed'] [data-pull-to-refresh-indicator] {
468
+ /* While the finger is down the indicator tracks the pull with no easing lag. */
469
+ transition: none;
470
+ }
471
+
472
+ @media (prefers-reduced-motion: reduce) {
473
+ [data-pull-to-refresh-indicator] {
474
+ transition: none;
475
+ }
476
+ }
477
+
478
+ /* PWA update toast ------------------------------------------------------- */
479
+ [data-pwa-update-toast] {
480
+ /* Above the home indicator and the mobile tab bar. */
481
+ bottom: calc(
482
+ max(var(--safe-bottom, 0px), env(safe-area-inset-bottom, 0px)) +
483
+ var(--mobile-tabbar-height, 0px) + 0.75rem
484
+ );
485
+ }
486
+
487
+ @media (prefers-reduced-motion: no-preference) {
488
+ [data-pwa-update-toast] {
489
+ animation: boff-native-slide-up 220ms var(--motion-easing-out, ease-out) both;
490
+ }
491
+ }
492
+
493
+ @keyframes boff-native-slide-up {
494
+ from {
495
+ opacity: 0;
496
+ transform: translateY(1rem);
497
+ }
498
+ to {
499
+ opacity: 1;
500
+ transform: none;
501
+ }
502
+ }
503
+
504
+ /* Action sheet / native select rows -------------------------------------- */
505
+ [data-action-sheet-option],
506
+ [data-action-sheet-cancel],
507
+ [data-native-select-option] {
508
+ /* Native minimum target size (spec §4/§5.7). */
509
+ min-height: 48px;
510
+ -webkit-tap-highlight-color: transparent;
511
+ }
512
+
513
+ .platform-ios [data-action-sheet-option],
514
+ .platform-ios [data-action-sheet-cancel] {
515
+ /* iOS action sheets center their rows in a rounded card stack. */
516
+ font-size: 1.0625rem;
517
+ }
518
+
519
+ /* === 6. desktop (Electron) (WS-D) === */
520
+
521
+ /*
522
+ * DOM contract, set by `bootDesktopShell()` (shared/native/desktop.ts):
523
+ * html[data-desktop-platform="darwin|win32|linux"]
524
+ * html[data-desktop-titlebar="hidden-inset|native"]
525
+ * html[data-desktop-fullscreen|data-desktop-focused|data-desktop-maximized]="true|false"
526
+ * body.platform-desktop
527
+ * Chrome opts into window dragging with `data-desktop-drag-region`; interactive
528
+ * groups inside it opt back out with `data-no-drag`.
529
+ */
530
+
531
+ /* Frameless macOS window: the header IS the titlebar. Reserve room for the
532
+ traffic lights on the left (they overlay the web contents at hidden-inset). */
533
+ html[data-desktop-titlebar='hidden-inset'] [data-desktop-drag-region] {
534
+ -webkit-app-region: drag;
535
+ app-region: drag;
536
+ padding-left: 84px;
537
+ }
538
+
539
+ /* Fullscreen hides the traffic lights — give the padding back. */
540
+ html[data-desktop-titlebar='hidden-inset'][data-desktop-fullscreen='true']
541
+ [data-desktop-drag-region] {
542
+ padding-left: 0;
543
+ }
544
+
545
+ /* Anything clickable inside a drag region must stay clickable. */
546
+ [data-desktop-drag-region]
547
+ :is(
548
+ button,
549
+ a,
550
+ input,
551
+ select,
552
+ textarea,
553
+ [role='button'],
554
+ [role='menuitem'],
555
+ [role='combobox'],
556
+ [role='tab'],
557
+ [data-no-drag]
558
+ ) {
559
+ -webkit-app-region: no-drag;
560
+ app-region: no-drag;
561
+ }
562
+
563
+ /* Desktop chrome should feel native: no text selection on the shell frame.
564
+ Content areas keep normal selection. */
565
+ html[data-desktop-platform] :is([data-shell-chrome], [data-desktop-drag-region]) {
566
+ user-select: none;
567
+ -webkit-user-select: none;
568
+ }
569
+
570
+ /* Dim the frameless titlebar when the window loses focus (macOS convention). */
571
+ html[data-desktop-titlebar='hidden-inset'][data-desktop-focused='false']
572
+ [data-desktop-drag-region] {
573
+ opacity: 0.92;
574
+ }
575
+
576
+ /* NOTE: scrollbars are intentionally left at the platform default on desktop —
577
+ desktop users expect real scrollbars, unlike the mobile shells. */