@flamingo-stack/openframe-frontend-core 0.0.703 → 0.0.704-snapshot.20261006133653

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 (336) hide show
  1. package/dist/{chunk-3L7KCOGO.cjs → chunk-2QEUFXNS.cjs} +30 -30
  2. package/dist/{chunk-BMJUKS6S.cjs → chunk-3256UJXS.cjs} +47 -47
  3. package/dist/{chunk-GZYLZ4MB.js → chunk-4S7TJYRQ.js} +3 -3
  4. package/dist/{chunk-ENGBNKUJ.js → chunk-5EFZO5EB.js} +675 -569
  5. package/dist/{chunk-TCZCMD5Q.js → chunk-5HBHO5TB.js} +1 -1
  6. package/dist/{chunk-BOUPNYUV.js → chunk-64HOMVYC.js} +5 -5
  7. package/dist/{chunk-YR6JPJF4.js → chunk-6XIF2Z7I.js} +4 -4
  8. package/dist/{chunk-NU724ZVN.cjs → chunk-7GHB2IK6.cjs} +121 -121
  9. package/dist/{chunk-GX3OUTFO.cjs → chunk-7ZYIC7NA.cjs} +69 -55
  10. package/dist/{chunk-53AC23ZN.js → chunk-A6WVQKOZ.js} +24 -24
  11. package/dist/{chunk-4OGAXAEG.cjs → chunk-ABU3TGXA.cjs} +3 -3
  12. package/dist/{chunk-RTXTGLGB.cjs → chunk-ACV4DFQ3.cjs} +4 -4
  13. package/dist/{chunk-RLDAYA2A.cjs → chunk-BH6PJP66.cjs} +29 -29
  14. package/dist/{chunk-BRXOGCH3.cjs → chunk-BIZNSF3U.cjs} +3 -3
  15. package/dist/{chunk-2A4XES7R.cjs → chunk-BM5MVGH3.cjs} +3 -3
  16. package/dist/{chunk-6YCXA7CN.js → chunk-CH5232HV.js} +20 -19
  17. package/dist/{chunk-UIBSSVON.cjs → chunk-D7BTSVMS.cjs} +2 -2
  18. package/dist/{chunk-YBGEPKC2.cjs → chunk-F7Y2ZS5U.cjs} +6 -6
  19. package/dist/{chunk-5V6O73EH.js → chunk-FLKJYMVB.js} +3 -3
  20. package/dist/{chunk-NMGX2ZTU.js → chunk-IFUQVZF4.js} +17 -17
  21. package/dist/{chunk-2NE3DNXY.js → chunk-IKCQK67U.js} +2 -2
  22. package/dist/{chunk-KFDP3DUL.cjs → chunk-IXPUM3AW.cjs} +98 -98
  23. package/dist/{chunk-QGTDWFT6.js → chunk-IZHZMBIX.js} +2469 -1928
  24. package/dist/{chunk-67CXGPTW.cjs → chunk-JKEEFQOX.cjs} +505 -72
  25. package/dist/{chunk-NYSYSKSH.cjs → chunk-M6HTJTFH.cjs} +15 -15
  26. package/dist/{chunk-OGLCD3UL.js → chunk-MFFCUVWG.js} +6 -6
  27. package/dist/{chunk-KJ5LWATV.js → chunk-MKXO4Q5P.js} +7 -7
  28. package/dist/{chunk-EMCQGFXD.cjs → chunk-MMOODW57.cjs} +28 -27
  29. package/dist/{chunk-Q76ZTHNV.js → chunk-MP2C3LJ4.js} +1 -1
  30. package/dist/{chunk-J4K5JOLI.js → chunk-MV4HYG3B.js} +25 -11
  31. package/dist/{chunk-2T6QWNXU.js → chunk-NDESEPW5.js} +99 -11
  32. package/dist/{chunk-HHFPQNP3.js → chunk-NJDHFC55.js} +15 -15
  33. package/dist/{chunk-HUOLB7ZN.cjs → chunk-OHFS7VOU.cjs} +9 -9
  34. package/dist/{chunk-7632TNE7.js → chunk-OSNIZNVH.js} +7 -7
  35. package/dist/{chunk-UTW4SQDI.js → chunk-OUHMD6EU.js} +10 -10
  36. package/dist/{chunk-MXHJROXJ.cjs → chunk-P23CRLU4.cjs} +1245 -1139
  37. package/dist/{chunk-64O4L7WN.js → chunk-PDWVPI3S.js} +11 -55
  38. package/dist/{chunk-DAW6TDZB.js → chunk-PFC2HWQA.js} +30 -16
  39. package/dist/{chunk-G55W76S6.js → chunk-POZ7F77D.js} +12 -13
  40. package/dist/{chunk-PJP5SMOU.js → chunk-PPEZGUL7.js} +500 -67
  41. package/dist/{chunk-SFGXIFYO.cjs → chunk-QC7Q3XCE.cjs} +24 -24
  42. package/dist/{chunk-MDBHZBW7.cjs → chunk-RKV5B7EU.cjs} +92 -4
  43. package/dist/{chunk-5VPIC5MD.cjs → chunk-SAAZEWUR.cjs} +12 -12
  44. package/dist/{chunk-IIMICN7L.cjs → chunk-TYWLNCNH.cjs} +4 -48
  45. package/dist/{chunk-HYLIQMZH.cjs → chunk-UBP5RNGK.cjs} +33 -33
  46. package/dist/{chunk-HUMWQMCU.js → chunk-UT4YHQZW.js} +3 -3
  47. package/dist/{chunk-BSEIEWM5.cjs → chunk-V3H26CNS.cjs} +28 -14
  48. package/dist/{chunk-NGJCWEYX.cjs → chunk-VHCACDKP.cjs} +46 -46
  49. package/dist/{chunk-SL2FOFT2.js → chunk-VMAZCLX4.js} +7 -7
  50. package/dist/{chunk-5FFNNJM7.js → chunk-VR2ZLFCP.js} +1 -1
  51. package/dist/{chunk-NCTYD7HL.js → chunk-VYWMEL5F.js} +14 -14
  52. package/dist/{chunk-HY5YL3U5.cjs → chunk-XBWE423R.cjs} +1994 -1453
  53. package/dist/{chunk-XCIFHW4E.cjs → chunk-XOYH2YQE.cjs} +2 -2
  54. package/dist/{chunk-S6V7A5FS.cjs → chunk-ZQZFIE2K.cjs} +32 -33
  55. package/dist/components/case-studies/index.cjs +24 -24
  56. package/dist/components/case-studies/index.js +10 -10
  57. package/dist/components/chat/chat-archive-page.d.ts +3 -3
  58. package/dist/components/chat/chat-archive-page.d.ts.map +1 -1
  59. package/dist/components/chat/chat-input.d.ts.map +1 -1
  60. package/dist/components/chat/chat-nav-sidebar.d.ts +4 -0
  61. package/dist/components/chat/chat-nav-sidebar.d.ts.map +1 -1
  62. package/dist/components/chat/chat-panel-header.d.ts +3 -2
  63. package/dist/components/chat/chat-panel-header.d.ts.map +1 -1
  64. package/dist/components/chat/embeddable-chat.d.ts +36 -2
  65. package/dist/components/chat/embeddable-chat.d.ts.map +1 -1
  66. package/dist/components/chat/index.cjs +14 -6
  67. package/dist/components/chat/index.d.ts +4 -1
  68. package/dist/components/chat/index.d.ts.map +1 -1
  69. package/dist/components/chat/index.js +13 -5
  70. package/dist/components/chat/mingo-archive-list.d.ts +17 -0
  71. package/dist/components/chat/mingo-archive-list.d.ts.map +1 -0
  72. package/dist/components/chat/mingo-chat-header.d.ts +31 -0
  73. package/dist/components/chat/mingo-chat-header.d.ts.map +1 -0
  74. package/dist/components/chat/mingo-chat-rail.d.ts +54 -0
  75. package/dist/components/chat/mingo-chat-rail.d.ts.map +1 -0
  76. package/dist/components/chat/model-display.d.ts.map +1 -1
  77. package/dist/components/contact/contact-form.d.ts.map +1 -1
  78. package/dist/components/contact/index.cjs +11 -11
  79. package/dist/components/contact/index.js +10 -10
  80. package/dist/components/content-loading-container.d.ts +1 -1
  81. package/dist/components/content-loading-container.d.ts.map +1 -1
  82. package/dist/components/docs/index.cjs +14 -14
  83. package/dist/components/docs/index.js +13 -13
  84. package/dist/components/docs/skeletons.d.ts.map +1 -1
  85. package/dist/components/embeds/index.cjs +11 -11
  86. package/dist/components/embeds/index.js +10 -10
  87. package/dist/components/faq/index.cjs +12 -12
  88. package/dist/components/faq/index.js +11 -11
  89. package/dist/components/features/figma-prototype-viewer.d.ts.map +1 -1
  90. package/dist/components/features/index.cjs +10 -10
  91. package/dist/components/features/index.js +9 -9
  92. package/dist/components/features/media-gallery-manager.d.ts.map +1 -1
  93. package/dist/components/features/parallax-image-showcase.d.ts.map +1 -1
  94. package/dist/components/features/policy-configuration-panel.d.ts.map +1 -1
  95. package/dist/components/features/section-selector.d.ts.map +1 -1
  96. package/dist/components/help-center-pages/index.cjs +119 -119
  97. package/dist/components/help-center-pages/index.js +39 -39
  98. package/dist/components/help-center-pages/trust-center-sections.d.ts +2 -2
  99. package/dist/components/help-center-pages/trust-center-sections.d.ts.map +1 -1
  100. package/dist/components/index.cjs +94 -86
  101. package/dist/components/index.js +32 -24
  102. package/dist/components/layout/article-detail-layout.d.ts +1 -1
  103. package/dist/components/layout/article-detail-layout.d.ts.map +1 -1
  104. package/dist/components/layout/page-container.d.ts.map +1 -1
  105. package/dist/components/layout/title-block.d.ts +2 -2
  106. package/dist/components/layout/title-block.d.ts.map +1 -1
  107. package/dist/components/loading/page-layout-skeleton.d.ts.map +1 -1
  108. package/dist/components/loading/unified-skeleton.d.ts.map +1 -1
  109. package/dist/components/meeting-scheduler/booking-form.d.ts +4 -4
  110. package/dist/components/meeting-scheduler/booking-form.d.ts.map +1 -1
  111. package/dist/components/meeting-scheduler/context-panel.d.ts.map +1 -1
  112. package/dist/components/meeting-scheduler/index.cjs +127 -127
  113. package/dist/components/meeting-scheduler/index.d.ts +2 -2
  114. package/dist/components/meeting-scheduler/index.d.ts.map +1 -1
  115. package/dist/components/meeting-scheduler/index.js +50 -50
  116. package/dist/components/navigation/app-header.d.ts +4 -0
  117. package/dist/components/navigation/app-header.d.ts.map +1 -1
  118. package/dist/components/navigation/app-layout-side-panel.d.ts +142 -0
  119. package/dist/components/navigation/app-layout-side-panel.d.ts.map +1 -0
  120. package/dist/components/navigation/app-layout.d.ts +11 -1
  121. package/dist/components/navigation/app-layout.d.ts.map +1 -1
  122. package/dist/components/navigation/header-mingo-button.d.ts +4 -1
  123. package/dist/components/navigation/header-mingo-button.d.ts.map +1 -1
  124. package/dist/components/navigation/index.cjs +13 -13
  125. package/dist/components/navigation/index.d.ts +1 -0
  126. package/dist/components/navigation/index.d.ts.map +1 -1
  127. package/dist/components/navigation/index.js +12 -12
  128. package/dist/components/navigation/navigation-sidebar.d.ts +8 -1
  129. package/dist/components/navigation/navigation-sidebar.d.ts.map +1 -1
  130. package/dist/components/navigation/sliding-sidebar.d.ts.map +1 -1
  131. package/dist/components/onboarding-guides/index.cjs +14 -14
  132. package/dist/components/onboarding-guides/index.js +13 -13
  133. package/dist/components/persistent-filter-controls.d.ts.map +1 -1
  134. package/dist/components/related-content/index.cjs +7 -7
  135. package/dist/components/related-content/index.js +6 -6
  136. package/dist/components/shared/detail-page-skeleton.d.ts.map +1 -1
  137. package/dist/components/tickets/help-center-create-form.d.ts +4 -4
  138. package/dist/components/tickets/index.cjs +15 -15
  139. package/dist/components/tickets/index.js +14 -14
  140. package/dist/components/ui/announcement-bar-view.d.ts.map +1 -1
  141. package/dist/components/ui/breadcrumb.d.ts.map +1 -1
  142. package/dist/components/ui/checkbox-block.d.ts.map +1 -1
  143. package/dist/components/ui/dashboard-info-card.d.ts +1 -1
  144. package/dist/components/ui/dashboard-info-card.d.ts.map +1 -1
  145. package/dist/components/ui/data-table/data-table-body.d.ts +2 -2
  146. package/dist/components/ui/data-table/data-table-skeleton.d.ts +2 -2
  147. package/dist/components/ui/data-table/data-table-skeleton.d.ts.map +1 -1
  148. package/dist/components/ui/data-table/utils.d.ts +2 -2
  149. package/dist/components/ui/dialog.d.ts.map +1 -1
  150. package/dist/components/ui/drawer.d.ts.map +1 -1
  151. package/dist/components/ui/entity-image.d.ts +2 -2
  152. package/dist/components/ui/file-manager/index.cjs +36 -36
  153. package/dist/components/ui/file-manager/index.js +3 -3
  154. package/dist/components/ui/filter-modal.d.ts.map +1 -1
  155. package/dist/components/ui/index.cjs +18 -10
  156. package/dist/components/ui/index.js +17 -9
  157. package/dist/components/ui/info-card-row.d.ts.map +1 -1
  158. package/dist/components/ui/menubar.d.ts +1 -1
  159. package/dist/components/ui/menubar.d.ts.map +1 -1
  160. package/dist/components/ui/modal-v2.d.ts.map +1 -1
  161. package/dist/components/ui/modal.d.ts.map +1 -1
  162. package/dist/components/ui/no-data/no-data-actions.d.ts.map +1 -1
  163. package/dist/components/ui/no-data/no-data-styles.d.ts +1 -1
  164. package/dist/components/ui/no-data/no-data-styles.d.ts.map +1 -1
  165. package/dist/components/ui/page-actions.d.ts.map +1 -1
  166. package/dist/components/ui/sheet.d.ts.map +1 -1
  167. package/dist/components/ui/table/table-skeleton.d.ts +1 -1
  168. package/dist/components/ui/table/table-skeleton.d.ts.map +1 -1
  169. package/dist/hooks/index.cjs +23 -9
  170. package/dist/hooks/index.js +24 -10
  171. package/dist/hooks/ui/index.d.ts +1 -0
  172. package/dist/hooks/ui/index.d.ts.map +1 -1
  173. package/dist/hooks/ui/use-content-breakpoint.d.ts +32 -0
  174. package/dist/hooks/ui/use-content-breakpoint.d.ts.map +1 -0
  175. package/dist/hooks/ui/use-isomorphic-layout-effect.d.ts +2 -2
  176. package/dist/hooks/ui/use-isomorphic-layout-effect.d.ts.map +1 -1
  177. package/dist/hooks/ui/use-media-query.d.ts.map +1 -1
  178. package/dist/index.cjs +39 -17
  179. package/dist/index.js +41 -19
  180. package/dist/styles/layout-steps.d.ts +30 -0
  181. package/dist/styles/layout-steps.d.ts.map +1 -0
  182. package/dist/tailwind.config.cjs +36 -11
  183. package/dist/tailwind.config.js +36 -11
  184. package/eslint-config/README.md +9 -0
  185. package/eslint-config/base.js +17 -1
  186. package/eslint-config/layout-variants.js +57 -0
  187. package/package.json +3 -1
  188. package/src/ODS_TOKEN_RULES.md +40 -0
  189. package/src/components/chat/chat-archive-page.tsx +9 -7
  190. package/src/components/chat/chat-input.tsx +5 -1
  191. package/src/components/chat/chat-nav-sidebar.tsx +10 -4
  192. package/src/components/chat/chat-panel-header.tsx +8 -5
  193. package/src/components/chat/embeddable-chat.tsx +675 -485
  194. package/src/components/chat/index.ts +4 -0
  195. package/src/components/chat/mingo-archive-list.tsx +108 -0
  196. package/src/components/chat/mingo-chat-header.tsx +104 -0
  197. package/src/components/chat/mingo-chat-rail.tsx +361 -0
  198. package/src/components/chat/model-display.tsx +8 -1
  199. package/src/components/contact/contact-form.tsx +11 -9
  200. package/src/components/content-loading-container.tsx +4 -2
  201. package/src/components/docs/doc-viewer.tsx +7 -7
  202. package/src/components/docs/skeletons.tsx +7 -4
  203. package/src/components/dynamic-skeleton.tsx +1 -1
  204. package/src/components/embeds/claude-embed.tsx +1 -1
  205. package/src/components/embeds/embed-viewer-frame.tsx +4 -4
  206. package/src/components/embeds/figma-embed.tsx +2 -2
  207. package/src/components/embeds/google-sheets-viewer.tsx +1 -1
  208. package/src/components/embeds/pdf-viewer.tsx +3 -3
  209. package/src/components/empty-state.tsx +6 -6
  210. package/src/components/faq/faq-section.tsx +2 -2
  211. package/src/components/faq-accordion.tsx +1 -1
  212. package/src/components/features/board/board-column-header.tsx +1 -1
  213. package/src/components/features/command-box.tsx +3 -3
  214. package/src/components/features/entity-video-section.tsx +2 -2
  215. package/src/components/features/figma-prototype-viewer.tsx +4 -1
  216. package/src/components/features/media-gallery-manager.tsx +3 -1
  217. package/src/components/features/parallax-image-showcase.tsx +9 -6
  218. package/src/components/features/policy-configuration-panel.tsx +9 -7
  219. package/src/components/features/provider-button.tsx +1 -1
  220. package/src/components/features/section-selector.tsx +11 -6
  221. package/src/components/features/select-button.tsx +4 -4
  222. package/src/components/features/seo-editor-preview.tsx +2 -2
  223. package/src/components/features/video-ratio-tabs.tsx +3 -3
  224. package/src/components/features/waitlist-form.tsx +1 -1
  225. package/src/components/help-center-pages/trust-center-page.tsx +1 -1
  226. package/src/components/help-center-pages/trust-center-sections.tsx +14 -14
  227. package/src/components/icon-display.tsx +1 -1
  228. package/src/components/layout/article-detail-layout.tsx +4 -2
  229. package/src/components/layout/list-page-layout.tsx +2 -2
  230. package/src/components/layout/page-container.tsx +26 -14
  231. package/src/components/layout/page-heading.tsx +1 -1
  232. package/src/components/layout/page-layout.tsx +1 -1
  233. package/src/components/layout/title-block.tsx +13 -13
  234. package/src/components/loading/card-skeleton.tsx +11 -11
  235. package/src/components/loading/category-vendor-selector-skeleton.tsx +1 -1
  236. package/src/components/loading/content-skeleton.tsx +6 -6
  237. package/src/components/loading/device-card-skeleton.tsx +1 -1
  238. package/src/components/loading/margin-report-skeleton.tsx +7 -7
  239. package/src/components/loading/msp-profile-form-skeleton.tsx +3 -3
  240. package/src/components/loading/organization-card-skeleton.tsx +2 -2
  241. package/src/components/loading/page-layout-skeleton.tsx +80 -68
  242. package/src/components/loading/unified-skeleton.tsx +16 -6
  243. package/src/components/media-carousel.tsx +3 -3
  244. package/src/components/meeting-scheduler/booking-form.tsx +9 -8
  245. package/src/components/meeting-scheduler/context-panel.tsx +8 -6
  246. package/src/components/meeting-scheduler/directory.tsx +4 -4
  247. package/src/components/meeting-scheduler/index.tsx +15 -15
  248. package/src/components/meeting-scheduler/slot-picker.tsx +12 -12
  249. package/src/components/navigation/app-header.tsx +6 -0
  250. package/src/components/navigation/app-layout-side-panel.tsx +594 -0
  251. package/src/components/navigation/app-layout.tsx +69 -4
  252. package/src/components/navigation/header-mingo-button.tsx +13 -3
  253. package/src/components/navigation/index.ts +5 -0
  254. package/src/components/navigation/navigation-sidebar.tsx +17 -3
  255. package/src/components/navigation/sliding-sidebar.tsx +2 -1
  256. package/src/components/pagination.tsx +3 -3
  257. package/src/components/persistent-filter-controls.tsx +6 -2
  258. package/src/components/platform/ScriptArguments.tsx +1 -1
  259. package/src/components/platform/ScriptInfoSection.tsx +4 -4
  260. package/src/components/platform/SoftwareSourceBadge.tsx +1 -1
  261. package/src/components/shared/article-author-byline.tsx +1 -1
  262. package/src/components/shared/delivery/delivery-lists.tsx +2 -2
  263. package/src/components/shared/delivery/delivery-row.tsx +7 -7
  264. package/src/components/shared/delivery/delivery-table.tsx +3 -3
  265. package/src/components/shared/detail-page-skeleton.tsx +11 -5
  266. package/src/components/shared/dev-section/dev-card-row.tsx +6 -6
  267. package/src/components/shared/dev-section/dev-section-view.tsx +1 -1
  268. package/src/components/shared/product-release/product-release-card-skeleton.tsx +8 -8
  269. package/src/components/shared/product-release/product-release-card.tsx +9 -9
  270. package/src/components/shared/product-release/product-releases-view.tsx +1 -1
  271. package/src/components/shared/product-release/release-detail-page.tsx +5 -5
  272. package/src/components/shared/roadmap/roadmap-grid-skeleton.tsx +1 -1
  273. package/src/components/shared/roadmap/roadmap-grid.tsx +3 -3
  274. package/src/components/tickets/help-center-card.tsx +1 -1
  275. package/src/components/tickets/help-center-create-form.tsx +12 -12
  276. package/src/components/ui/announcement-bar-view.tsx +2 -10
  277. package/src/components/ui/benefit-card.tsx +18 -18
  278. package/src/components/ui/brand-association-grid.tsx +4 -4
  279. package/src/components/ui/breadcrumb.tsx +4 -1
  280. package/src/components/ui/card.tsx +1 -1
  281. package/src/components/ui/checkbox-block.tsx +9 -7
  282. package/src/components/ui/content-loader.tsx +3 -3
  283. package/src/components/ui/cursor-pagination.tsx +3 -3
  284. package/src/components/ui/dashboard-info-card.tsx +13 -9
  285. package/src/components/ui/data-attribution.tsx +2 -2
  286. package/src/components/ui/data-table/data-table-body.tsx +2 -2
  287. package/src/components/ui/data-table/data-table-header.tsx +15 -15
  288. package/src/components/ui/data-table/data-table-skeleton.tsx +6 -6
  289. package/src/components/ui/data-table/utils.ts +5 -5
  290. package/src/components/ui/dialog.tsx +4 -1
  291. package/src/components/ui/drawer.tsx +2 -1
  292. package/src/components/ui/duration-input.tsx +1 -1
  293. package/src/components/ui/entity-image.tsx +3 -3
  294. package/src/components/ui/feature-card.tsx +10 -10
  295. package/src/components/ui/file-manager/file-manager-action-bar.tsx +1 -1
  296. package/src/components/ui/file-manager/file-manager.tsx +1 -1
  297. package/src/components/ui/filter-modal.tsx +8 -1
  298. package/src/components/ui/highlight-card.tsx +7 -7
  299. package/src/components/ui/info-card-row.tsx +10 -6
  300. package/src/components/ui/modal-v2.tsx +7 -2
  301. package/src/components/ui/modal.tsx +3 -2
  302. package/src/components/ui/no-data/no-data-actions.tsx +10 -4
  303. package/src/components/ui/no-data/no-data-styles.ts +1 -1
  304. package/src/components/ui/no-data/no-data.tsx +1 -1
  305. package/src/components/ui/organization-card.tsx +1 -1
  306. package/src/components/ui/page-actions.tsx +34 -5
  307. package/src/components/ui/progress-bar.tsx +2 -2
  308. package/src/components/ui/query-report-table/query-report-table.tsx +1 -1
  309. package/src/components/ui/search-input.tsx +2 -2
  310. package/src/components/ui/service-card.tsx +2 -2
  311. package/src/components/ui/sheet.tsx +2 -1
  312. package/src/components/ui/skeleton.tsx +3 -3
  313. package/src/components/ui/stacked-rows-panel.tsx +4 -4
  314. package/src/components/ui/tab-navigation.tsx +2 -2
  315. package/src/components/ui/tab-selector.tsx +2 -2
  316. package/src/components/ui/table/table-header.tsx +3 -3
  317. package/src/components/ui/table/table-skeleton.tsx +3 -3
  318. package/src/components/ui/table/utils.ts +5 -5
  319. package/src/components/ui/tag-search-input.tsx +3 -3
  320. package/src/components/ui/tags-manager.tsx +5 -5
  321. package/src/components/ui/test-run.tsx +2 -2
  322. package/src/components/ui/ticket-info-section.tsx +1 -1
  323. package/src/components/ui/ticket-notes-section.tsx +1 -1
  324. package/src/components/ui/ticket-status-config-row.tsx +15 -15
  325. package/src/hooks/ui/index.ts +1 -0
  326. package/src/hooks/ui/use-content-breakpoint.ts +58 -0
  327. package/src/hooks/ui/use-media-query.ts +3 -2
  328. package/src/styles/app-globals.css +13 -0
  329. package/src/styles/index.css +2 -0
  330. package/src/styles/layout-steps.ts +14 -0
  331. package/src/styles/ods-colors.css +15 -0
  332. package/src/styles/ods-content-area-tokens.css +27 -0
  333. package/src/styles/ods-content-area.css +31 -0
  334. package/src/styles/ods-effects.css +4 -2
  335. package/src/styles/ods-responsive-tokens.css +14 -4
  336. package/tailwind.config.ts +42 -7
@@ -4,6 +4,7 @@ import * as DialogPrimitive from '@radix-ui/react-dialog';
4
4
  import { X } from 'lucide-react';
5
5
  import { type ComponentPropsWithoutRef, type ElementRef, type HTMLAttributes, forwardRef } from 'react';
6
6
 
7
+ import { ViewportBreakpoints } from '../../hooks/ui/use-content-breakpoint';
7
8
  import { cn } from '../../utils/cn';
8
9
 
9
10
  const Dialog = DialogPrimitive.Root;
@@ -65,7 +66,9 @@ const DialogContent = forwardRef<
65
66
  {/* min-h-0 is load-bearing: a grid item defaults to min-height:auto and
66
67
  would refuse to shrink below its content, so the cap above would push
67
68
  past the viewport instead of handing this box a scrollbar. */}
68
- <div className="grid min-h-0 gap-[var(--spacing-system-mf)] overflow-y-auto">{children}</div>
69
+ <div className="grid min-h-0 gap-[var(--spacing-system-mf)] overflow-y-auto">
70
+ <ViewportBreakpoints>{children}</ViewportBreakpoints>
71
+ </div>
69
72
  <DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
70
73
  <X className="h-4 w-4" />
71
74
  <span className="sr-only">Close</span>
@@ -16,6 +16,7 @@ import {
16
16
  useSyncExternalStore,
17
17
  } from 'react';
18
18
 
19
+ import { ViewportBreakpoints } from '../../hooks/ui/use-content-breakpoint';
19
20
  import { useHeaderHeight } from '../../hooks/ui/use-header-height';
20
21
  import { type PanelDefaultSize, useResizablePanelSize } from '../../hooks/ui/use-resizable-panel-size';
21
22
  import { cn } from '../../utils/cn';
@@ -309,7 +310,7 @@ const DrawerContent = forwardRef<ComponentRef<typeof DialogPrimitive.Content>, D
309
310
  className={cn(drawerPanelVariants({ side, flush, size }), className, panelClassName)}
310
311
  style={{ ...sizeStyle, ...panelStyle }}
311
312
  >
312
- {children}
313
+ <ViewportBreakpoints>{children}</ViewportBreakpoints>
313
314
  </div>
314
315
  </DialogPrimitive.Content>
315
316
  </DrawerPortal>
@@ -115,7 +115,7 @@ export function DurationInput({ value, onChange, invalid, className }: DurationI
115
115
  }
116
116
  }}
117
117
  className={cn(
118
- 'flex h-11 w-full items-center gap-[var(--spacing-system-xxs)] rounded-md border border-ods-border bg-ods-card px-[var(--spacing-system-sf)] transition-colors duration-200 md:h-12',
118
+ 'flex h-11 w-full items-center gap-[var(--spacing-system-xxs)] rounded-md border border-ods-border bg-ods-card px-[var(--spacing-system-sf)] transition-colors duration-200 content-md:h-12',
119
119
  'cursor-text focus-within:border-ods-accent',
120
120
  invalid && 'border-ods-error focus-within:border-ods-error',
121
121
  className,
@@ -7,7 +7,7 @@ import { cn } from '../../utils/cn';
7
7
  import { personInitials } from '../../utils/format';
8
8
 
9
9
  /** The design size, when a caller does not state one. */
10
- const ENTITY_IMAGE_SIZE = 'size-[52px] md:size-[60px]';
10
+ const ENTITY_IMAGE_SIZE = 'size-[52px] content-md:size-[60px]';
11
11
 
12
12
  export interface EntityImageProps {
13
13
  src?: string | null;
@@ -18,10 +18,10 @@ export interface EntityImageProps {
18
18
  * REPLACES the default size, for both the image and its initials fallback.
19
19
  *
20
20
  * Sizing through `className` does NOT work here and cannot be made to:
21
- * the default is responsive (`size-[52px] md:size-[60px]`), and
21
+ * the default is responsive (`size-[52px] content-md:size-[60px]`), and
22
22
  * tailwind-merge lets `size-*` override `w`/`h` but never the reverse — so a
23
23
  * caller passing `h-4 w-6` dropped the base `size-[52px]` and kept
24
- * `md:size-[60px]`, rendering 16px on a phone and 60px on a desktop. State
24
+ * `content-md:size-[60px]`, rendering 16px on a phone and 60px on a desktop. State
25
25
  * the size here instead; it is substituted, not merged.
26
26
  */
27
27
  sizeClassName?: string;
@@ -59,9 +59,9 @@ export interface FeatureCardGridProps {
59
59
  }
60
60
 
61
61
  const GRID_COLUMNS = {
62
- 2: 'grid-cols-1 md:grid-cols-2',
63
- 3: 'grid-cols-1 md:grid-cols-3',
64
- 4: 'grid-cols-1 md:grid-cols-2 lg:grid-cols-4',
62
+ 2: 'grid-cols-1 content-md:grid-cols-2',
63
+ 3: 'grid-cols-1 content-md:grid-cols-3',
64
+ 4: 'grid-cols-1 content-md:grid-cols-2 content-lg:grid-cols-4',
65
65
  } as const;
66
66
 
67
67
  /**
@@ -75,10 +75,10 @@ function fourColumnBorders(index: number, count: number): string {
75
75
  if (index < count - 1) classes.push('border-b');
76
76
  const mdRows = Math.ceil(count / 2);
77
77
  const lgRows = Math.ceil(count / 4);
78
- classes.push(index % 2 === 0 && index < count - 1 ? 'md:border-r' : 'md:border-r-0');
79
- classes.push(Math.floor(index / 2) < mdRows - 1 ? 'md:border-b' : 'md:border-b-0');
80
- classes.push(index % 4 !== 3 && index < count - 1 ? 'lg:border-r' : 'lg:border-r-0');
81
- classes.push(Math.floor(index / 4) < lgRows - 1 ? 'lg:border-b' : 'lg:border-b-0');
78
+ classes.push(index % 2 === 0 && index < count - 1 ? 'content-md:border-r' : 'content-md:border-r-0');
79
+ classes.push(Math.floor(index / 2) < mdRows - 1 ? 'content-md:border-b' : 'content-md:border-b-0');
80
+ classes.push(index % 4 !== 3 && index < count - 1 ? 'content-lg:border-r' : 'content-lg:border-r-0');
81
+ classes.push(Math.floor(index / 4) < lgRows - 1 ? 'content-lg:border-b' : 'content-lg:border-b-0');
82
82
  return ` ${classes.join(' ')}`;
83
83
  }
84
84
 
@@ -117,7 +117,7 @@ export function FeatureCardGrid({
117
117
  if (!isLastInRow) {
118
118
  // On mobile (1 column), never show right border
119
119
  // On desktop (2/3 columns), show right border except for last in row
120
- classes += ' md:border-r border-ods-border';
120
+ classes += ' content-md:border-r border-ods-border';
121
121
  }
122
122
 
123
123
  // Bottom border logic
@@ -130,10 +130,10 @@ export function FeatureCardGrid({
130
130
 
131
131
  // Desktop: override mobile border, show bottom border for all rows except last
132
132
  if (!isLastRow) {
133
- classes += ' md:border-b border-ods-border';
133
+ classes += ' content-md:border-b border-ods-border';
134
134
  } else {
135
135
  // Last row on desktop - remove bottom border
136
- classes += ' md:border-b-0';
136
+ classes += ' content-md:border-b-0';
137
137
  }
138
138
 
139
139
  return classes;
@@ -17,7 +17,7 @@ export function FileManagerActionBar({
17
17
  className,
18
18
  }: FileManagerActionBarProps) {
19
19
  return (
20
- <div className={cn('flex flex-wrap items-center gap-4 md:justify-end', className)}>
20
+ <div className={cn('flex flex-wrap items-center gap-4 content-md:justify-end', className)}>
21
21
  <Button variant="outline" size="small-legacy" onClick={onNewFolder} leftIcon={<FolderPlus className="h-4 w-4" />}>
22
22
  New Folder
23
23
  </Button>
@@ -129,7 +129,7 @@ export function FileManager({
129
129
  return (
130
130
  <div className={cn('flex h-full flex-col bg-ods-bg', className)}>
131
131
  <div className="flex min-h-0 flex-1 flex-col space-y-6 py-2">
132
- <div className="mb-4 flex flex-col justify-between gap-4 md:flex-row">
132
+ <div className="mb-4 flex flex-col justify-between gap-4 content-md:flex-row">
133
133
  <FileManagerBreadcrumb items={breadcrumbItems} onItemClick={handleBreadcrumbClick} />
134
134
 
135
135
  {showActions && (
@@ -175,7 +175,14 @@ export function FilterModal({
175
175
  const isEmpty = !isLoading && !hasSort && !hasFilterGroups && !hasTagFilter && !dateFilter;
176
176
 
177
177
  return (
178
- <ModalV2 isOpen={isOpen} onClose={onClose} className={cn('max-h-[90vh] max-w-none', className)}>
178
+ <ModalV2
179
+ isOpen={isOpen}
180
+ onClose={onClose}
181
+ // Full width as the phone's bottom sheet. A wide window gets the medium dialog
182
+ // width instead: page content narrowed by a docked side panel shows the
183
+ // mobile filter button there too, and a window-wide sheet read as broken.
184
+ className={cn('max-h-[90vh] max-w-none md:max-w-[min(42rem,calc(100vw-2rem))]', className)}
185
+ >
179
186
  <ModalV2Header>
180
187
  <ModalV2Title>{title}</ModalV2Title>
181
188
  </ModalV2Header>
@@ -18,11 +18,11 @@ export function HighlightCard({ item, className = '' }: HighlightCardProps) {
18
18
  const desktopWidth = item.category.length * 14;
19
19
 
20
20
  return (
21
- <div className={`relative bg-ods-bg p-6 md:p-10 ${className}`}>
21
+ <div className={`relative bg-ods-bg p-6 content-md:p-10 ${className}`}>
22
22
  <div className="relative">
23
23
  {/* Mobile highlight */}
24
24
  <div
25
- className="absolute left-[80px] top-0 h-8 rounded-md md:hidden"
25
+ className="absolute left-[80px] top-0 h-8 rounded-md content-md:hidden"
26
26
  style={{
27
27
  backgroundColor: item.highlightBg,
28
28
  width: `${mobileWidth}px`,
@@ -30,7 +30,7 @@ export function HighlightCard({ item, className = '' }: HighlightCardProps) {
30
30
  />
31
31
  {/* Desktop highlight */}
32
32
  <div
33
- className="absolute left-[95px] top-0 hidden h-10 rounded-md md:block"
33
+ className="absolute left-[95px] top-0 hidden h-10 rounded-md content-md:block"
34
34
  style={{
35
35
  backgroundColor: item.highlightBg,
36
36
  width: `${desktopWidth}px`,
@@ -57,7 +57,7 @@ export function HighlightCardGrid({
57
57
  className = '',
58
58
  cardClassName = 'bg-ods-card border border-ods-border rounded-lg p-0 overflow-hidden',
59
59
  }: HighlightCardGridProps) {
60
- const gridCols = columns === 2 ? 'grid-cols-1 md:grid-cols-2' : 'grid-cols-1 md:grid-cols-3';
60
+ const gridCols = columns === 2 ? 'grid-cols-1 content-md:grid-cols-2' : 'grid-cols-1 content-md:grid-cols-3';
61
61
  const itemsPerRow = columns;
62
62
  const rows = Math.ceil(items.length / itemsPerRow);
63
63
 
@@ -68,7 +68,7 @@ export function HighlightCardGrid({
68
68
  if (!isLastInRow) {
69
69
  // On mobile (1 column), never show right border
70
70
  // On desktop (2/3 columns), show right border except for last in row
71
- classes += ' md:border-r border-ods-border';
71
+ classes += ' content-md:border-r border-ods-border';
72
72
  }
73
73
 
74
74
  // Bottom border logic
@@ -81,10 +81,10 @@ export function HighlightCardGrid({
81
81
 
82
82
  // Desktop: override mobile border, show bottom border for all rows except last
83
83
  if (!isLastRow) {
84
- classes += ' md:border-b border-ods-border';
84
+ classes += ' content-md:border-b border-ods-border';
85
85
  } else {
86
86
  // Last row on desktop - remove bottom border
87
- classes += ' md:border-b-0';
87
+ classes += ' content-md:border-b-0';
88
88
  }
89
89
 
90
90
  return classes;
@@ -14,7 +14,7 @@ export interface InfoCardRowProps {
14
14
  }
15
15
 
16
16
  const CELL =
17
- 'flex items-center gap-[var(--spacing-system-s)] min-w-0 p-[var(--spacing-system-xsf)] md:p-[var(--spacing-system-m)] min-h-[60px] md:min-h-[76px]';
17
+ 'flex items-center gap-[var(--spacing-system-s)] min-w-0 p-[var(--spacing-system-xsf)] content-md:p-[var(--spacing-system-m)] min-h-[60px] content-md:min-h-[76px]';
18
18
 
19
19
  function StatCell({ section, className }: { section: InfoCardRowSection; className?: string }) {
20
20
  return (
@@ -24,11 +24,13 @@ function StatCell({ section, className }: { section: InfoCardRowSection; classNa
24
24
  <div className="flex min-w-0 items-center gap-[var(--spacing-system-s)]">
25
25
  <p className="min-w-0 flex-1 truncate text-ods-text-primary text-h3">{section.caption}</p>
26
26
  {section.icon && (
27
- <div className="flex size-6 shrink-0 items-center justify-center md:hidden">{section.icon}</div>
27
+ <div className="flex size-6 shrink-0 items-center justify-center content-md:hidden">{section.icon}</div>
28
28
  )}
29
29
  </div>
30
30
  </div>
31
- {section.icon && <div className="hidden size-6 shrink-0 items-center justify-center md:flex">{section.icon}</div>}
31
+ {section.icon && (
32
+ <div className="hidden size-6 shrink-0 items-center justify-center content-md:flex">{section.icon}</div>
33
+ )}
32
34
  </div>
33
35
  );
34
36
  }
@@ -37,11 +39,13 @@ export function InfoCardRow({ lead, stats, className }: InfoCardRowProps) {
37
39
  return (
38
40
  <div
39
41
  className={cn(
40
- 'flex shrink-0 flex-col overflow-hidden rounded-md border border-ods-border bg-ods-card md:flex-row',
42
+ 'flex shrink-0 flex-col overflow-hidden rounded-md border border-ods-border bg-ods-card content-md:flex-row',
41
43
  className,
42
44
  )}
43
45
  >
44
- <div className={cn(CELL, 'border-b border-ods-border md:flex-1 md:border-b-0 md:border-r')}>
46
+ <div
47
+ className={cn(CELL, 'border-b border-ods-border content-md:flex-1 content-md:border-b-0 content-md:border-r')}
48
+ >
45
49
  {lead.icon && (
46
50
  <div className="flex size-10 shrink-0 items-center justify-center overflow-hidden rounded-sm border border-ods-border [&>*]:size-full [&_img]:object-cover">
47
51
  {lead.icon}
@@ -53,7 +57,7 @@ export function InfoCardRow({ lead, stats, className }: InfoCardRowProps) {
53
57
  </div>
54
58
  </div>
55
59
 
56
- <div className="flex md:flex-1">
60
+ <div className="flex content-md:flex-1">
57
61
  <StatCell section={stats[0]} className="border-r border-ods-border" />
58
62
  <StatCell section={stats[1]} />
59
63
  </div>
@@ -2,6 +2,7 @@
2
2
 
3
3
  import { type ReactNode, createContext, forwardRef, useContext, useRef, useEffect, useState } from 'react';
4
4
  import { RemoveScroll } from 'react-remove-scroll';
5
+ import { ViewportBreakpoints } from '../../hooks/ui/use-content-breakpoint';
5
6
  import { cn } from '../../utils/cn';
6
7
  import { XmarkIcon } from '../icons-v2-generated';
7
8
 
@@ -235,7 +236,9 @@ const Modal = forwardRef<HTMLDivElement, ModalProps>(
235
236
  mirrored there as INSET_TRANSITION_MS — it delays the app's
236
237
  scroll-the-focused-field re-assert until this transition settles. */}
237
238
  <div
238
- className="fixed inset-0 z-[1300] flex items-end justify-center transition-[padding] duration-200 md:items-center"
239
+ // `ods-viewport-layer`: a modal follows the window, so a modal opened from a
240
+ // narrow content area takes the window's tokens, not the area's mobile ones.
241
+ className="ods-viewport-layer fixed inset-0 z-[1300] flex items-end justify-center transition-[padding] duration-200 md:items-center"
239
242
  style={{ paddingBottom: 'var(--of-keyboard-inset, 0px)' }}
240
243
  >
241
244
  <div
@@ -303,7 +306,9 @@ const Modal = forwardRef<HTMLDivElement, ModalProps>(
303
306
  role="dialog"
304
307
  aria-modal="true"
305
308
  >
306
- <ModalContext.Provider value={{ onClose }}>{children}</ModalContext.Provider>
309
+ <ModalContext.Provider value={{ onClose }}>
310
+ <ViewportBreakpoints>{children}</ViewportBreakpoints>
311
+ </ModalContext.Provider>
307
312
  </div>
308
313
  </div>
309
314
  </RemoveScroll>
@@ -2,6 +2,7 @@
2
2
 
3
3
  import { usePreventScroll } from '@react-aria/overlays';
4
4
  import { type ReactNode, forwardRef, useEffect } from 'react';
5
+ import { ViewportBreakpoints } from '../../hooks/ui/use-content-breakpoint';
5
6
  import { cn } from '../../utils/cn';
6
7
 
7
8
  /**
@@ -61,7 +62,7 @@ const Modal = forwardRef<HTMLDivElement, ModalProps>(({ isOpen, onClose, childre
61
62
  if (!isOpen) return null;
62
63
 
63
64
  return (
64
- <div className="fixed inset-0 z-[1300] flex items-center justify-center">
65
+ <div className="ods-viewport-layer fixed inset-0 z-[1300] flex items-center justify-center">
65
66
  <div className="absolute inset-0 bg-ods-overlay" onClick={onClose} aria-hidden="true" />
66
67
  <div
67
68
  ref={ref}
@@ -72,7 +73,7 @@ const Modal = forwardRef<HTMLDivElement, ModalProps>(({ isOpen, onClose, childre
72
73
  role="dialog"
73
74
  aria-modal="true"
74
75
  >
75
- {children}
76
+ <ViewportBreakpoints>{children}</ViewportBreakpoints>
76
77
  </div>
77
78
  </div>
78
79
  );
@@ -25,13 +25,15 @@ const NoDataAction = React.forwardRef<HTMLDivElement, NoDataActionProps>(functio
25
25
  ref={ref}
26
26
  className={cn(
27
27
  'flex flex-1 items-center gap-[var(--spacing-system-s)] p-[var(--spacing-system-m)] text-ods-text-secondary',
28
- 'md:w-[224px] md:flex-none md:flex-col md:items-start md:gap-[var(--spacing-system-xs)]',
28
+ 'content-md:w-[224px] content-md:flex-none content-md:flex-col content-md:items-start content-md:gap-[var(--spacing-system-xs)]',
29
29
  className,
30
30
  )}
31
31
  {...props}
32
32
  >
33
33
  {icon && <span className={cn(noDataIconClasses, 'text-ods-text-primary')}>{icon}</span>}
34
- <span className="min-w-0 flex-1 break-words text-h6 md:w-full md:flex-none">{label ?? children}</span>
34
+ <span className="min-w-0 flex-1 break-words text-h6 content-md:w-full content-md:flex-none">
35
+ {label ?? children}
36
+ </span>
35
37
  </div>
36
38
  );
37
39
  });
@@ -61,7 +63,11 @@ const NoDataActions = React.forwardRef<HTMLDivElement, NoDataActionsProps>(funct
61
63
  return (
62
64
  <div
63
65
  ref={ref}
64
- className={cn(noDataActionsVariants({ variant }), 'flex w-full flex-col md:w-auto md:flex-row', className)}
66
+ className={cn(
67
+ noDataActionsVariants({ variant }),
68
+ 'flex w-full flex-col content-md:w-auto content-md:flex-row',
69
+ className,
70
+ )}
65
71
  {...props}
66
72
  >
67
73
  {blocks.map((block, index) => (
@@ -69,7 +75,7 @@ const NoDataActions = React.forwardRef<HTMLDivElement, NoDataActionsProps>(funct
69
75
  {index > 0 && (
70
76
  <div
71
77
  aria-hidden="true"
72
- className="shrink-0 self-stretch border-t border-ods-border md:border-l md:border-t-0"
78
+ className="shrink-0 self-stretch border-t border-ods-border content-md:border-l content-md:border-t-0"
73
79
  />
74
80
  )}
75
81
  {block}
@@ -7,7 +7,7 @@ import { cva, type VariantProps } from 'class-variance-authority';
7
7
  // Leading-icon glyph scale: 16px on mobile, 24px on md+. Color is intentionally
8
8
  // omitted so the glyph inherits the current text color.
9
9
  export const noDataIconClasses =
10
- 'inline-flex shrink-0 items-center justify-center [&_svg]:h-4 [&_svg]:w-4 md:[&_svg]:h-6 md:[&_svg]:w-6';
10
+ 'inline-flex shrink-0 items-center justify-center [&_svg]:h-4 [&_svg]:w-4 content-md:[&_svg]:h-6 content-md:[&_svg]:w-6';
11
11
 
12
12
  // Surface for the info-block group container: a transparent, bordered panel with
13
13
  // rounded corners. `overflow-hidden` keeps only the outer blocks round — inner
@@ -65,7 +65,7 @@ const NoData = React.forwardRef<HTMLDivElement, NoDataProps>(function NoDataImpl
65
65
  leftIcon={buttonIcon}
66
66
  onClick={onButtonClick}
67
67
  {...buttonProps}
68
- className={cn('w-full md:w-auto', buttonProps?.className)}
68
+ className={cn('w-full content-md:w-auto', buttonProps?.className)}
69
69
  >
70
70
  {buttonLabel}
71
71
  </Button>
@@ -127,7 +127,7 @@ export function OrganizationCard({
127
127
  customFooter
128
128
  ) : footerStats && footerStats.length > 0 ? (
129
129
  <div className="flex w-full min-w-0 items-center justify-between gap-2">
130
- <div className="flex min-w-0 flex-shrink items-center gap-3 md:gap-4">
130
+ <div className="flex min-w-0 flex-shrink items-center gap-3 content-md:gap-4">
131
131
  {footerStats.map((stat, index) => (
132
132
  <div key={index} className="flex flex-shrink-0 items-center gap-1">
133
133
  {stat.icon}
@@ -355,7 +355,7 @@ function IconButtonsVariant({
355
355
  return (
356
356
  <>
357
357
  {/* Desktop: every action as an icon button + optional overflow menu */}
358
- <div className={cn('hidden items-center md:flex', ACTIONS_GAP, className)}>
358
+ <div className={cn('hidden items-center content-md:flex', ACTIONS_GAP, className)}>
359
359
  {selector}
360
360
  {loading ? (
361
361
  <ActionSkeletons actions={desktopActions} />
@@ -370,7 +370,7 @@ function IconButtonsVariant({
370
370
  </div>
371
371
 
372
372
  {/* Mobile: single icon button OR all actions merged into one "..." menu */}
373
- <div className={cn('flex md:hidden', className)}>
373
+ <div className={cn('flex content-md:hidden', className)}>
374
374
  {loading ? (
375
375
  <MobileTriggerSkeleton />
376
376
  ) : useSingleActionMobile && singleAction ? (
@@ -386,6 +386,12 @@ function IconButtonsVariant({
386
386
  /**
387
387
  * Primary buttons variant — primary + outline buttons on desktop,
388
388
  * fixed bottom bar on mobile.
389
+ *
390
+ * The bottom bar spans the WINDOW, so it stays on the viewport `md` step. A
391
+ * content area narrowed by a docked side panel is a third case: the window is
392
+ * wide (no bar) but the title row has no room for every labelled button, so the
393
+ * actions compact into the row instead. `md:content-max-md:` is exactly that case
394
+ * and never matches outside a content area.
389
395
  */
390
396
  function PrimaryButtonsVariant({
391
397
  actions,
@@ -407,7 +413,7 @@ function PrimaryButtonsVariant({
407
413
 
408
414
  return (
409
415
  <>
410
- <div className={cn('hidden items-center md:flex', ACTIONS_GAP, className)}>
416
+ <div className={cn('hidden items-center md:content-md:flex', ACTIONS_GAP, className)}>
411
417
  {loading ? (
412
418
  // No overflow trigger in this variant — an unknown action set settles
413
419
  // into a labelled button, not a square one.
@@ -419,6 +425,10 @@ function PrimaryButtonsVariant({
419
425
  )}
420
426
  </div>
421
427
 
428
+ <div className={cn('hidden items-center md:content-max-md:flex', ACTIONS_GAP, className)}>
429
+ {loading ? <MobileTriggerSkeleton /> : <CompactPrimaryActions actions={desktopActions} />}
430
+ </div>
431
+
422
432
  <MobileBottomActions actions={sortedActions} loading={loading} />
423
433
  </>
424
434
  );
@@ -449,7 +459,7 @@ function MenuPrimaryVariant({
449
459
 
450
460
  return (
451
461
  <>
452
- <div className={cn('hidden items-center md:flex', ACTIONS_GAP, className)}>
462
+ <div className={cn('hidden items-center content-md:flex', ACTIONS_GAP, className)}>
453
463
  {selector}
454
464
  {loading ? (
455
465
  <ActionSkeletons actions={desktopActions} />
@@ -465,7 +475,7 @@ function MenuPrimaryVariant({
465
475
  )}
466
476
  </div>
467
477
 
468
- <div className={cn('flex md:hidden', className)}>
478
+ <div className={cn('flex content-md:hidden', className)}>
469
479
  {loading ? (
470
480
  <MobileTriggerSkeleton />
471
481
  ) : hasMobileMenuItems || selector ? (
@@ -479,6 +489,25 @@ function MenuPrimaryVariant({
479
489
  );
480
490
  }
481
491
 
492
+ /**
493
+ * `primary-buttons` in a narrow content area of a wide window: the accent (and
494
+ * icon-only) actions stay buttons, the other labelled ones fold into one "..."
495
+ * menu — unless that menu would hold a single action, which stays a button.
496
+ */
497
+ function CompactPrimaryActions({ actions }: { actions: PageActionButton[] }) {
498
+ const foldable = actions.filter(action => action.variant !== 'accent' && !!action.label);
499
+ const fold = foldable.length > 1;
500
+ const inline = fold ? actions.filter(action => !foldable.includes(action)) : actions;
501
+ return (
502
+ <>
503
+ {fold && <ActionsMenuDropdown groups={[{ items: foldable.flatMap(actionToMenuItems) }]} />}
504
+ {inline.map((action, idx) => (
505
+ <React.Fragment key={`compact-${actionKey(action, idx)}`}>{renderActionButton(action)}</React.Fragment>
506
+ ))}
507
+ </>
508
+ );
509
+ }
510
+
482
511
  function MobileBottomActions({ actions, loading }: { actions: PageActionButton[]; loading?: boolean }) {
483
512
  return (
484
513
  <div
@@ -2,7 +2,7 @@
2
2
 
3
3
  import type React from 'react';
4
4
  import { useEffect, useRef, useState } from 'react';
5
- import { useMdUp } from '../../hooks';
5
+ import { useContentMdUp } from '../../hooks/ui/use-content-breakpoint';
6
6
 
7
7
  type ProgressBarProps = {
8
8
  progress: number; // 0–100
@@ -27,7 +27,7 @@ export const ProgressBar: React.FC<ProgressBarProps> = ({
27
27
  mobileHeight = 8,
28
28
  inverted = false,
29
29
  }) => {
30
- const isMdUp = useMdUp() ?? true;
30
+ const isMdUp = useContentMdUp() ?? true;
31
31
  const effectiveSegmentWidth = isMdUp ? segmentWidth : mobileSegmentWidth;
32
32
  const effectiveHeight = isMdUp ? height : mobileHeight;
33
33
 
@@ -65,7 +65,7 @@ export function QueryReportTable({
65
65
  {headerActions}
66
66
  {showExport && data.length > 0 && (
67
67
  <Button
68
- className="bg-ods-card max-md:hidden"
68
+ className="bg-ods-card content-max-md:hidden"
69
69
  variant="outline"
70
70
  leftIcon={<Download02Icon size={18} className="text-ods-text-secondary" />}
71
71
  onClick={handleExport}
@@ -117,7 +117,7 @@ export interface SearchInputProps {
117
117
 
118
118
  const containerStyles = cn(
119
119
  // Layout & spacing — matches lib Input component
120
- 'flex h-11 cursor-text items-center gap-2 rounded-[6px] border px-3 md:h-12',
120
+ 'flex h-11 cursor-text items-center gap-2 rounded-[6px] border px-3 content-md:h-12',
121
121
  'has-[:focus-visible]:outline-none',
122
122
  'group',
123
123
  'transition-colors duration-200',
@@ -456,7 +456,7 @@ export function SearchInput({
456
456
  }}
457
457
  >
458
458
  {/* Start Adornment — pinned left, shrink-0 */}
459
- <span className="flex-shrink-0 text-ods-text-secondary transition-colors duration-200 group-has-[:focus]:text-ods-accent [&_svg]:size-4 md:[&_svg]:size-6">
459
+ <span className="flex-shrink-0 text-ods-text-secondary transition-colors duration-200 group-has-[:focus]:text-ods-accent [&_svg]:size-4 content-md:[&_svg]:size-6">
460
460
  {startAdornment !== undefined ? startAdornment : <SearchIcon />}
461
461
  </span>
462
462
 
@@ -113,10 +113,10 @@ function ServiceCardRowItem({ row }: { row: ServiceCardRow }) {
113
113
 
114
114
  return (
115
115
  <div className="flex min-w-0 items-center gap-3">
116
- {row.label && <div className="w-20 shrink-0 text-ods-text-primary text-h6 md:w-24">{row.label}</div>}
116
+ {row.label && <div className="w-20 shrink-0 text-ods-text-primary text-h6 content-md:w-24">{row.label}</div>}
117
117
  <div
118
118
  className={cn(
119
- 'flex h-12 min-w-0 flex-1 items-center justify-between rounded-md border border-ods-border bg-ods-bg px-3 md:px-4',
119
+ 'flex h-12 min-w-0 flex-1 items-center justify-between rounded-md border border-ods-border bg-ods-bg px-3 content-md:px-4',
120
120
  row.monospace ? 'font-mono' : '',
121
121
  )}
122
122
  >
@@ -5,6 +5,7 @@ import { cva, type VariantProps } from 'class-variance-authority';
5
5
  import { X } from 'lucide-react';
6
6
  import { type ComponentPropsWithoutRef, type ElementRef, type HTMLAttributes, forwardRef } from 'react';
7
7
 
8
+ import { ViewportBreakpoints } from '../../hooks/ui/use-content-breakpoint';
8
9
  import { cn } from '../../utils/cn';
9
10
 
10
11
  const Sheet = SheetPrimitive.Root;
@@ -57,7 +58,7 @@ const SheetContent = forwardRef<ElementRef<typeof SheetPrimitive.Content>, Sheet
57
58
  <SheetPortal>
58
59
  <SheetOverlay />
59
60
  <SheetPrimitive.Content ref={ref} className={cn(sheetVariants({ side }), className)} {...props}>
60
- {children}
61
+ <ViewportBreakpoints>{children}</ViewportBreakpoints>
61
62
  <SheetPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary">
62
63
  <X className="h-4 w-4" />
63
64
  <span className="sr-only">Close</span>
@@ -68,9 +68,9 @@ const SkeletonGrid = forwardRef<HTMLDivElement, SkeletonGridProps>(
68
68
  ref={ref}
69
69
  className={cn(
70
70
  `grid grid-cols-1 gap-6`,
71
- columns === 2 && 'md:grid-cols-2',
72
- columns === 3 && 'md:grid-cols-3',
73
- columns === 4 && 'md:grid-cols-4',
71
+ columns === 2 && 'content-md:grid-cols-2',
72
+ columns === 3 && 'content-md:grid-cols-3',
73
+ columns === 4 && 'content-md:grid-cols-4',
74
74
  className,
75
75
  )}
76
76
  {...props}
@@ -83,15 +83,15 @@ const CELL_ITEMS_ALIGN: Record<PanelColumnAlign, string> = {
83
83
  };
84
84
 
85
85
  const HIDE_CLASS: Record<PanelHideAt, string> = {
86
- md: 'hidden md:flex',
87
- lg: 'hidden lg:flex',
88
- xl: 'hidden xl:flex',
86
+ md: 'hidden content-md:flex',
87
+ lg: 'hidden content-lg:flex',
88
+ xl: 'hidden content-xl:flex',
89
89
  };
90
90
 
91
91
  // Base column-row class. Row height is content-driven via the min-heights, matching
92
92
  // the joined-row pattern used across the detail layouts (56px mobile, 80px tablet+).
93
93
  const ROW_CLASS =
94
- 'flex items-center gap-[var(--spacing-system-m)] px-[var(--spacing-system-m)] min-h-14 md:min-h-20 border-b border-ods-border last:border-b-0';
94
+ 'flex items-center gap-[var(--spacing-system-m)] px-[var(--spacing-system-m)] min-h-14 content-md:min-h-20 border-b border-ods-border last:border-b-0';
95
95
 
96
96
  function CellValue({ column }: { column: PanelColumn }) {
97
97
  const valueNode = (
@@ -399,7 +399,7 @@ const TabBar = memo(function TabBarImpl({
399
399
  <div className="relative flex items-center justify-center">
400
400
  <tab.icon
401
401
  className={cn(
402
- 'h-4 w-4 transition-colors md:h-6 md:w-6',
402
+ 'h-4 w-4 transition-colors content-md:h-6 content-md:w-6',
403
403
  isActive ? 'text-ods-accent' : 'text-ods-text-secondary',
404
404
  )}
405
405
  />
@@ -412,7 +412,7 @@ const TabBar = memo(function TabBarImpl({
412
412
  ) : null}
413
413
 
414
414
  {tab.sublabel ? (
415
- <span className="flex min-w-0 flex-col items-start text-left md:flex-row md:items-baseline md:gap-[var(--spacing-system-xsf)]">
415
+ <span className="flex min-w-0 flex-col items-start text-left content-md:flex-row content-md:items-baseline content-md:gap-[var(--spacing-system-xsf)]">
416
416
  <span
417
417
  className={cn(
418
418
  'whitespace-nowrap transition-colors text-h4',
@@ -78,7 +78,7 @@ export function TabSelector({
78
78
  'flex gap-[var(--spacing-system-xxs)] bg-ods-bg p-[var(--spacing-system-xxs)]',
79
79
  flush
80
80
  ? 'h-10 w-full'
81
- : cn('h-11 rounded-md border border-ods-border md:h-12', scrollable ? 'overflow-x-auto' : 'w-full'),
81
+ : cn('h-11 rounded-md border border-ods-border content-md:h-12', scrollable ? 'overflow-x-auto' : 'w-full'),
82
82
  )}
83
83
  >
84
84
  {items.map(item => {
@@ -112,7 +112,7 @@ export function TabSelector({
112
112
  )}
113
113
  >
114
114
  {item.icon && (
115
- <span className="flex size-4 shrink-0 items-center justify-center md:size-6">{item.icon}</span>
115
+ <span className="flex size-4 shrink-0 items-center justify-center content-md:size-6">{item.icon}</span>
116
116
  )}
117
117
  {item.label}
118
118
  {item.badge && item.badge}