@digithings-ai/ui 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (424) hide show
  1. package/README.md +48 -2
  2. package/package.json +143 -4
  3. package/src/components/NavShell.tsx +776 -0
  4. package/src/components/SocialRow.tsx +87 -0
  5. package/src/components/Spinner.tsx +27 -0
  6. package/src/components/StackLogo.tsx +40 -0
  7. package/src/components/Terminal.tsx +110 -0
  8. package/src/components/TerminalManifest.tsx +212 -0
  9. package/src/components/ThemeProvider.tsx +271 -0
  10. package/src/components/account/AuthCard.tsx +309 -0
  11. package/src/components/assistant-ui/elements/mermaid-diagram.aui.tsx +63 -0
  12. package/src/components/assistant-ui/elements/mermaid-diagram.tsx +340 -0
  13. package/src/components/brand/BrandWordmark.tsx +32 -0
  14. package/src/components/changelog/ReleaseRail.tsx +81 -0
  15. package/src/components/changelog/index.ts +1 -0
  16. package/src/components/chat/ChatCodeBlock.tsx +67 -0
  17. package/src/components/chat/ChatMarkdown.tsx +66 -0
  18. package/src/components/chat/ChatMarkdownSource.tsx +203 -0
  19. package/src/components/chat/ChatMermaidBlock.tsx +202 -0
  20. package/src/components/chat/ChatMessage.tsx +77 -0
  21. package/src/components/chat/ChatPlayback.tsx +422 -0
  22. package/src/components/chat/ChatStreamCursor.tsx +25 -0
  23. package/src/components/chat/ChatThinking.tsx +113 -0
  24. package/src/components/chat/ChatToolCall.tsx +190 -0
  25. package/src/components/chat/ChatTranscript.tsx +60 -0
  26. package/src/components/chat/ChatWidgetFrame.tsx +107 -0
  27. package/src/components/chat/DigichatBootLoader.tsx +683 -0
  28. package/src/components/chat/DigichatLauncher.tsx +361 -0
  29. package/src/components/chat/DigichatThread.tsx +118 -0
  30. package/src/components/chat/DigichatThreadList.tsx +54 -0
  31. package/src/components/chat/DigichatWordmark.tsx +24 -0
  32. package/src/components/chat/DotMatrix.tsx +650 -0
  33. package/src/components/chat/boot-lab.tsx +766 -0
  34. package/src/components/chat/boot-signal.ts +144 -0
  35. package/src/components/chat/chat-surfaces.ts +52 -0
  36. package/src/components/chat/connection-status.ts +40 -0
  37. package/src/components/chat/digichat-wordmark.ts +355 -0
  38. package/src/components/chat/disclosure-chevron.tsx +49 -0
  39. package/src/components/chat/followable-href.ts +9 -0
  40. package/src/components/chat/gallery-thread/action-icons.tsx +25 -0
  41. package/src/components/chat/gallery-thread/attachment.aui.tsx +262 -0
  42. package/src/components/chat/gallery-thread/block-caret.tsx +439 -0
  43. package/src/components/chat/gallery-thread/cn.ts +1 -0
  44. package/src/components/chat/gallery-thread/composer-keyboard.ts +106 -0
  45. package/src/components/chat/gallery-thread/composer-trigger-popover.aui.tsx +235 -0
  46. package/src/components/chat/gallery-thread/css.d.ts +6 -0
  47. package/src/components/chat/gallery-thread/file.tsx +238 -0
  48. package/src/components/chat/gallery-thread/follow-up-suggestions.aui.tsx +92 -0
  49. package/src/components/chat/gallery-thread/format-json-dump.ts +112 -0
  50. package/src/components/chat/gallery-thread/hooks/use-attachment-src.ts +46 -0
  51. package/src/components/chat/gallery-thread/hooks/use-copy-to-clipboard.ts +9 -0
  52. package/src/components/chat/gallery-thread/image.tsx +519 -0
  53. package/src/components/chat/gallery-thread/markdown-text.tsx +278 -0
  54. package/src/components/chat/gallery-thread/message-error.aui.tsx +105 -0
  55. package/src/components/chat/gallery-thread/message-timing.aui.tsx +143 -0
  56. package/src/components/chat/gallery-thread/private-work-step.tsx +188 -0
  57. package/src/components/chat/gallery-thread/reasoning.tsx +332 -0
  58. package/src/components/chat/gallery-thread/scroll-mode.ts +174 -0
  59. package/src/components/chat/gallery-thread/source.tsx +76 -0
  60. package/src/components/chat/gallery-thread/thread.aui.tsx +1619 -0
  61. package/src/components/chat/gallery-thread/tooltip-icon-button.tsx +53 -0
  62. package/src/components/chat/gallery-thread/typed-welcome-copy.tsx +18 -0
  63. package/src/components/chat/gallery-thread/ui/tooltip.tsx +22 -0
  64. package/src/components/chat/gallery-thread/work-chain-summary.tsx +48 -0
  65. package/src/components/chat/gallery-thread/work-chain.tsx +110 -0
  66. package/src/components/chat/gallery-thread/work-status.ts +66 -0
  67. package/src/components/chat/overlay-scrollbar.tsx +86 -0
  68. package/src/components/chat/sanitize-mermaid-svg.ts +38 -0
  69. package/src/components/chat/skins/SOURCE.md +36 -0
  70. package/src/components/chat/skins/base/attachment.aui.tsx +287 -0
  71. package/src/components/chat/skins/base/aui-default/README.md +45 -0
  72. package/src/components/chat/skins/base/aui-default/collapsible-root.tsx +19 -0
  73. package/src/components/chat/skins/base/aui-default/collapsible.tsx +21 -0
  74. package/src/components/chat/skins/base/aui-default/reasoning.aui.tsx +120 -0
  75. package/src/components/chat/skins/base/aui-default/reasoning.tsx +331 -0
  76. package/src/components/chat/skins/base/aui-default/surfaces.ts +7 -0
  77. package/src/components/chat/skins/base/aui-default/tool-fallback.aui.tsx +772 -0
  78. package/src/components/chat/skins/base/aui-default/tool-group.aui.tsx +241 -0
  79. package/src/components/chat/skins/base/docs-model-options.ts +6 -0
  80. package/src/components/chat/skins/base/elements/assistant-trace.aui.tsx +169 -0
  81. package/src/components/chat/skins/base/elements/composer-trigger-popover.aui.tsx +271 -0
  82. package/src/components/chat/skins/base/elements/directive-text.aui.tsx +29 -0
  83. package/src/components/chat/skins/base/elements/directive-text.tsx +76 -0
  84. package/src/components/chat/skins/base/elements/message-timing.aui.tsx +104 -0
  85. package/src/components/chat/skins/base/elements/model-selector.aui.tsx +140 -0
  86. package/src/components/chat/skins/base/elements/model-selector.tsx +720 -0
  87. package/src/components/chat/skins/base/elements/quote.aui.tsx +290 -0
  88. package/src/components/chat/skins/base/elements/work-chain.tsx +100 -0
  89. package/src/components/chat/skins/base/markdown-text.tsx +294 -0
  90. package/src/components/chat/skins/base/model.ts +1 -0
  91. package/src/components/chat/skins/base/thread.tsx +1050 -0
  92. package/src/components/chat/skins/base/tooltip-icon-button.tsx +56 -0
  93. package/src/components/chat/skins/base/ui/avatar.tsx +115 -0
  94. package/src/components/chat/skins/base/ui/badge.tsx +51 -0
  95. package/src/components/chat/skins/base/ui/button.tsx +67 -0
  96. package/src/components/chat/skins/base/ui/command.tsx +185 -0
  97. package/src/components/chat/skins/base/ui/dialog.tsx +168 -0
  98. package/src/components/chat/skins/base/ui/dot-matrix.tsx +267 -0
  99. package/src/components/chat/skins/base/ui/popover.tsx +97 -0
  100. package/src/components/chat/skins/base/ui/skeleton.tsx +20 -0
  101. package/src/components/chat/skins/base/ui/textarea.tsx +24 -0
  102. package/src/components/chat/skins/base/ui/tooltip.tsx +73 -0
  103. package/src/components/chat/skins/base-assistant-ui/config-provider.tsx +90 -0
  104. package/src/components/chat/skins/base-assistant-ui/defaults.ts +419 -0
  105. package/src/components/chat/skins/base-assistant-ui/index.tsx +44 -0
  106. package/src/components/chat/skins/chatgpt.tsx +448 -0
  107. package/src/components/chat/skins/claude.tsx +380 -0
  108. package/src/components/chat/skins/digichat-composer-toolbar.tsx +151 -0
  109. package/src/components/chat/skins/digichat-context-meter-shares.ts +158 -0
  110. package/src/components/chat/skins/digichat-context-meter.tsx +243 -0
  111. package/src/components/chat/skins/digichat-meter-tone.ts +26 -0
  112. package/src/components/chat/skins/digichat-usage-meter.tsx +103 -0
  113. package/src/components/chat/skins/digichat.tsx +144 -0
  114. package/src/components/chat/skins/expo-react-native.tsx +29 -0
  115. package/src/components/chat/skins/gemini.tsx +322 -0
  116. package/src/components/chat/skins/grok-icon.tsx +34 -0
  117. package/src/components/chat/skins/grok.tsx +369 -0
  118. package/src/components/chat/skins/index.ts +46 -0
  119. package/src/components/chat/skins/perplexity.tsx +462 -0
  120. package/src/components/chat/skins/product-page-assistant/assistant-modal.tsx +73 -0
  121. package/src/components/chat/skins/product-page-assistant/index.tsx +19 -0
  122. package/src/components/chat/skins/product-page-assistant/product-dashboard.tsx +164 -0
  123. package/src/components/chat/skins/react-ink.tsx +97 -0
  124. package/src/components/chat/skins/thread-skin-host-contract.ts +102 -0
  125. package/src/components/chat/skins/thread-skin-view.tsx +156 -0
  126. package/src/components/chat/skins/thread-skins.ts +143 -0
  127. package/src/components/chat/skins/tooltip-icon-button.tsx +35 -0
  128. package/src/components/chat/skins/ui/dropdown-menu.tsx +272 -0
  129. package/src/components/chat/skins/webpage-assistant/assistant-config.ts +238 -0
  130. package/src/components/chat/skins/webpage-assistant/assistant-modal.tsx +78 -0
  131. package/src/components/chat/skins/webpage-assistant/config-provider.tsx +132 -0
  132. package/src/components/chat/skins/webpage-assistant/docs-app.tsx +106 -0
  133. package/src/components/chat/skins/webpage-assistant/docs-article.tsx +109 -0
  134. package/src/components/chat/skins/webpage-assistant/docs-assistant.tsx +73 -0
  135. package/src/components/chat/skins/webpage-assistant/docs-nav.tsx +81 -0
  136. package/src/components/chat/skins/webpage-assistant/host-ui.ts +79 -0
  137. package/src/components/chat/skins/webpage-assistant/index.tsx +12 -0
  138. package/src/components/chat/skins/webpage-assistant/markdown-page.tsx +11 -0
  139. package/src/components/chat/skins/webpage-assistant/pages.ts +9 -0
  140. package/src/components/chat/skins/webpage-assistant/types.ts +178 -0
  141. package/src/components/chat/skins/webpage-assistant/use-mobile.ts +19 -0
  142. package/src/components/chat/stock/SOURCE.md +90 -0
  143. package/src/components/chat/stock/artifacts-panel.tsx +305 -0
  144. package/src/components/chat/stock/attachment.aui.tsx +280 -0
  145. package/src/components/chat/stock/chat-notice.tsx +143 -0
  146. package/src/components/chat/stock/chat-private-mode.tsx +64 -0
  147. package/src/components/chat/stock/deploy-ui-context.tsx +89 -0
  148. package/src/components/chat/stock/embed-chat-prefs.tsx +471 -0
  149. package/src/components/chat/stock/file.tsx +265 -0
  150. package/src/components/chat/stock/follow-up-suggestions.aui.tsx +83 -0
  151. package/src/components/chat/stock/image.tsx +531 -0
  152. package/src/components/chat/stock/index.ts +129 -0
  153. package/src/components/chat/stock/json-view.tsx +140 -0
  154. package/src/components/chat/stock/markdown-text.tsx +319 -0
  155. package/src/components/chat/stock/message-error.aui.tsx +96 -0
  156. package/src/components/chat/stock/message-export-menu.tsx +156 -0
  157. package/src/components/chat/stock/palette-row-preview.tsx +26 -0
  158. package/src/components/chat/stock/reasoning.aui.tsx +120 -0
  159. package/src/components/chat/stock/reasoning.tsx +335 -0
  160. package/src/components/chat/stock/skin-chrome.tsx +81 -0
  161. package/src/components/chat/stock/skin-credit.tsx +63 -0
  162. package/src/components/chat/stock/skin-palette-bridge.tsx +235 -0
  163. package/src/components/chat/stock/skin-palette-context.ts +16 -0
  164. package/src/components/chat/stock/skin-runtime.tsx +52 -0
  165. package/src/components/chat/stock/skin-slash-palette.tsx +393 -0
  166. package/src/components/chat/stock/skin-types.ts +217 -0
  167. package/src/components/chat/stock/stock-chat-prefs-host.tsx +501 -0
  168. package/src/components/chat/stock/stock-send-gate.tsx +94 -0
  169. package/src/components/chat/stock/stock-shape.tsx +39 -0
  170. package/src/components/chat/stock/thread.aui.tsx +645 -0
  171. package/src/components/chat/stock/tool-display.ts +6 -0
  172. package/src/components/chat/stock/tool-fallback.aui.tsx +866 -0
  173. package/src/components/chat/stock/tool-group.aui.tsx +244 -0
  174. package/src/components/chat/stock/tool-surface.ts +120 -0
  175. package/src/components/chat/stock/tooltip-icon-button.tsx +49 -0
  176. package/src/components/chat/stock/ui/avatar.tsx +108 -0
  177. package/src/components/chat/stock/ui/button.tsx +81 -0
  178. package/src/components/chat/stock/ui/collapsible.tsx +21 -0
  179. package/src/components/chat/stock/ui/dialog.tsx +163 -0
  180. package/src/components/chat/stock/ui/skeleton.tsx +13 -0
  181. package/src/components/chat/stock/ui/textarea.tsx +20 -0
  182. package/src/components/chat/stock/ui/tooltip.tsx +68 -0
  183. package/src/components/chat/stock/use-attachment-src.ts +42 -0
  184. package/src/components/chat/stock/use-copy-to-clipboard.ts +29 -0
  185. package/src/components/chat/strip-model-boilerplate.ts +42 -0
  186. package/src/components/chat/suggestion-cycle.tsx +230 -0
  187. package/src/components/chat/suggestion-scroll-row.tsx +81 -0
  188. package/src/components/chat/trace-grouping/chain-group-by.ts +44 -0
  189. package/src/components/chat/trace-grouping/chain-state.ts +141 -0
  190. package/src/components/chat/trace-grouping/index.ts +32 -0
  191. package/src/components/chat/trace-grouping/tool-row.ts +74 -0
  192. package/src/components/chat/trace-grouping/use-deploy-work-disclosure.ts +35 -0
  193. package/src/components/chat/trace-grouping/use-work-chain.ts +103 -0
  194. package/src/components/chat/trace-i18n/core.ts +48 -0
  195. package/src/components/chat/trace-i18n/index.tsx +23 -0
  196. package/src/components/chat/trace-i18n/locales/en.ts +24 -0
  197. package/src/components/chat/trace-i18n/locales/fr.ts +28 -0
  198. package/src/components/chat/trace-i18n/types.ts +35 -0
  199. package/src/components/chat/trace-spacing.ts +249 -0
  200. package/src/components/chat/transcript/activity-view.ts +375 -0
  201. package/src/components/chat/transcript/chat-export.ts +133 -0
  202. package/src/components/chat/transcript/index.ts +10 -0
  203. package/src/components/chat/transcript/save-file.ts +209 -0
  204. package/src/components/chat/transcript/transcript-markdown.ts +428 -0
  205. package/src/components/chat/transcript/types.ts +81 -0
  206. package/src/components/chat/turn-outcome.ts +52 -0
  207. package/src/components/chat/use-warmup-phase.ts +18 -0
  208. package/src/components/chat/work-status-lead.tsx +49 -0
  209. package/src/components/chrome.tsx +264 -0
  210. package/src/components/command-palette/CommandPalette.tsx +404 -0
  211. package/src/components/command-palette/index.ts +6 -0
  212. package/src/components/contact/ContactMailto.tsx +112 -0
  213. package/src/components/contact/index.ts +5 -0
  214. package/src/components/conviction/ConvictionMeter.tsx +40 -0
  215. package/src/components/conviction/SignedConvictionBadge.tsx +26 -0
  216. package/src/components/conviction/index.ts +7 -0
  217. package/src/components/data-layout/BentoGrid.tsx +52 -0
  218. package/src/components/data-layout/CardRail.tsx +150 -0
  219. package/src/components/data-layout/DotMatrixStat.tsx +65 -0
  220. package/src/components/data-layout/FeatureCell.tsx +80 -0
  221. package/src/components/data-layout/MediaFrame.tsx +125 -0
  222. package/src/components/data-layout/ModuleGrid.tsx +735 -0
  223. package/src/components/data-layout/Odometer.tsx +149 -0
  224. package/src/components/data-layout/ProductFrame.tsx +79 -0
  225. package/src/components/data-layout/TestimonialWall.tsx +139 -0
  226. package/src/components/data-layout/index.ts +19 -0
  227. package/src/components/data-layout/treemap.ts +368 -0
  228. package/src/components/deck/DeckStack.tsx +243 -0
  229. package/src/components/desk/DataTable.tsx +373 -0
  230. package/src/components/desk/KpiGrid.tsx +80 -0
  231. package/src/components/desk/Sparkline.tsx +63 -0
  232. package/src/components/desk/StateBlock.tsx +132 -0
  233. package/src/components/desk/index.ts +18 -0
  234. package/src/components/diagrams/ArchitectureDiagram.tsx +350 -0
  235. package/src/components/diagrams/ArchitectureTour.tsx +912 -0
  236. package/src/components/diagrams/TerminalSchematic.tsx +192 -0
  237. package/src/components/diagrams/architecture-svg.tsx +384 -0
  238. package/src/components/diagrams/index.ts +30 -0
  239. package/src/components/diagrams/mermaid-theme.ts +115 -0
  240. package/src/components/diagrams/tour-camera.ts +171 -0
  241. package/src/components/docs/CodeTabs.tsx +90 -0
  242. package/src/components/docs/CopyCommand.tsx +151 -0
  243. package/src/components/docs/DocsLayout.tsx +197 -0
  244. package/src/components/docs/DocsSearch.tsx +97 -0
  245. package/src/components/docs/Endpoint.tsx +135 -0
  246. package/src/components/document/DocumentFrame.tsx +43 -0
  247. package/src/components/document/GlyphList.tsx +51 -0
  248. package/src/components/document/PageTitle.tsx +44 -0
  249. package/src/components/document/Prose.tsx +38 -0
  250. package/src/components/document/Section.tsx +55 -0
  251. package/src/components/document/index.ts +13 -0
  252. package/src/components/effects-chrome/AnnouncementBar.tsx +88 -0
  253. package/src/components/effects-chrome/HorizontalScrollTrack.tsx +461 -0
  254. package/src/components/effects-chrome/Pipeline.tsx +226 -0
  255. package/src/components/effects-chrome/RotatingPrompts.tsx +116 -0
  256. package/src/components/effects-chrome/StackingPanels.tsx +101 -0
  257. package/src/components/effects-chrome/TabStrip.tsx +232 -0
  258. package/src/components/effects-chrome/ToastStack.tsx +201 -0
  259. package/src/components/effects-chrome/horizontal-track-core.ts +70 -0
  260. package/src/components/effects-chrome/index.ts +35 -0
  261. package/src/components/emblems.tsx +188 -0
  262. package/src/components/faviconTheme.ts +23 -0
  263. package/src/components/figure/Figure.tsx +38 -0
  264. package/src/components/figure/index.ts +2 -0
  265. package/src/components/finance-charts/DrawdownPlot.tsx +76 -0
  266. package/src/components/finance-charts/EquityCurve.tsx +71 -0
  267. package/src/components/finance-charts/PriceChart.tsx +119 -0
  268. package/src/components/finance-charts/chart-host.tsx +236 -0
  269. package/src/components/finance-charts/chart-scale.ts +93 -0
  270. package/src/components/finance-charts/demo-data.ts +88 -0
  271. package/src/components/finance-charts/index.ts +46 -0
  272. package/src/components/finance-charts/lw-chart.tsx +292 -0
  273. package/src/components/finance-composites/MarketBar.tsx +243 -0
  274. package/src/components/finance-composites/OrderBook.tsx +133 -0
  275. package/src/components/finance-composites/PerformanceDashboard.tsx +185 -0
  276. package/src/components/finance-composites/SortableTable.tsx +144 -0
  277. package/src/components/finance-composites/StockTicker.tsx +65 -0
  278. package/src/components/finance-composites/SyncedTearsheet.tsx +226 -0
  279. package/src/components/finance-composites/index.ts +28 -0
  280. package/src/components/finance-tearsheet/KpiStrip.tsx +49 -0
  281. package/src/components/finance-tearsheet/LiveBadge.tsx +29 -0
  282. package/src/components/finance-tearsheet/ReturnsMatrix.tsx +321 -0
  283. package/src/components/finance-tearsheet/StrategyTearsheet.tsx +99 -0
  284. package/src/components/finance-tearsheet/TearsheetCard.tsx +54 -0
  285. package/src/components/finance-tearsheet/TradeLogTable.tsx +73 -0
  286. package/src/components/finance-tearsheet/charts.tsx +2624 -0
  287. package/src/components/finance-tearsheet/demo-data.ts +177 -0
  288. package/src/components/finance-tearsheet/format.ts +41 -0
  289. package/src/components/finance-tearsheet/index.ts +120 -0
  290. package/src/components/finance-tearsheet/print.ts +52 -0
  291. package/src/components/finance-tearsheet/risk-bands.ts +48 -0
  292. package/src/components/finance-tearsheet/stats.ts +35 -0
  293. package/src/components/finance-tearsheet/strategy-sheet-model.ts +238 -0
  294. package/src/components/finance-tearsheet/types.ts +58 -0
  295. package/src/components/footer/FooterCells.tsx +109 -0
  296. package/src/components/footer/index.ts +6 -0
  297. package/src/components/graph.tsx +158 -0
  298. package/src/components/icons.tsx +98 -0
  299. package/src/components/landing/PixelWordmark.tsx +36 -0
  300. package/src/components/logos.ts +39 -0
  301. package/src/components/marquee/Marquee.tsx +204 -0
  302. package/src/components/metrics/PerfMetrics.tsx +68 -0
  303. package/src/components/metrics/StatCounter.tsx +104 -0
  304. package/src/components/page-geometry.tsx +177 -0
  305. package/src/components/pricing/Pricing.tsx +174 -0
  306. package/src/components/pricing/PricingMatrix.tsx +219 -0
  307. package/src/components/prose/index.ts +2 -0
  308. package/src/components/prose/prose.tsx +86 -0
  309. package/src/components/repo-activity/RepoActivity.tsx +599 -0
  310. package/src/components/repo-activity/RepoHeatmap.tsx +225 -0
  311. package/src/components/repo-activity/demo.ts +201 -0
  312. package/src/components/repo-activity/fetch.ts +211 -0
  313. package/src/components/repo-activity/heatmap.ts +123 -0
  314. package/src/components/repo-activity/index.ts +28 -0
  315. package/src/components/repo-activity/types.ts +99 -0
  316. package/src/components/stages/NumberedStages.tsx +116 -0
  317. package/src/components/symbols/marks.tsx +116 -0
  318. package/src/components/symbols/terminal-lockup.tsx +136 -0
  319. package/src/components/symbols/terminal-marks.tsx +226 -0
  320. package/src/components/terminal/ChatResponseLoader.tsx +67 -0
  321. package/src/components/terminal/ContainerBootLoader.tsx +82 -0
  322. package/src/components/terminal/TerminalStepCaret.tsx +202 -0
  323. package/src/components/themeInit.ts +24 -0
  324. package/src/components/typography/MotionReveal.tsx +108 -0
  325. package/src/components/typography/TypewriterReveal.tsx +101 -0
  326. package/src/components/typography/WordReveal.tsx +143 -0
  327. package/src/data/modules.ts +378 -0
  328. package/src/data/subsystems.ts +101 -0
  329. package/src/index.ts +587 -0
  330. package/src/lib/base-path.ts +60 -0
  331. package/src/lib/cx.ts +25 -0
  332. package/src/lib/gloomberb.ts +69 -0
  333. package/src/lib/record.ts +19 -0
  334. package/src/lib/stub-payload.ts +183 -0
  335. package/src/lib/useBodyScrollLock.ts +44 -0
  336. package/src/lib/utils.ts +6 -0
  337. package/src/motion/primitives.tsx +105 -0
  338. package/src/motion/scroll-glide.ts +62 -0
  339. package/src/motion/scrolly-core.ts +38 -0
  340. package/src/motion/scrolly.tsx +95 -0
  341. package/src/navigation/HashScrollManager.tsx +167 -0
  342. package/src/navigation/hashScroll.ts +78 -0
  343. package/src/replay/browser.ts +280 -0
  344. package/src/replay/chat-export-run.ts +121 -0
  345. package/src/replay/export-format.ts +25 -0
  346. package/src/replay/export-mount.ts +32 -0
  347. package/src/replay/filename.ts +54 -0
  348. package/src/replay/index.ts +21 -0
  349. package/src/replay/redact-dom.ts +178 -0
  350. package/src/replay/redact.ts +113 -0
  351. package/src/replay/snapshot.ts +659 -0
  352. package/src/styles/account-auth.css +339 -0
  353. package/src/styles/brand-wordmark.css +11 -0
  354. package/src/styles/card-rail.css +120 -0
  355. package/src/styles/chat-aui.css +1143 -0
  356. package/src/styles/chat-core.css +469 -0
  357. package/src/styles/chat-digichat.css +2531 -0
  358. package/src/styles/chat-math.css +70 -0
  359. package/src/styles/chat-skin-palette.css +138 -0
  360. package/src/styles/chat-stock-menu.css +37 -0
  361. package/src/styles/chat-widgets.css +190 -0
  362. package/src/styles/command-palette.css +372 -0
  363. package/src/styles/controls-core.css +2261 -0
  364. package/src/styles/controls-overlay.css +603 -0
  365. package/src/styles/data-layout.css +288 -0
  366. package/src/styles/deck.css +141 -0
  367. package/src/styles/diagrams.css +1860 -0
  368. package/src/styles/digichat-app-theme.css +250 -0
  369. package/src/styles/digichat-boot-loader.css +487 -0
  370. package/src/styles/digichat-launcher.css +377 -0
  371. package/src/styles/docs.css +165 -0
  372. package/src/styles/effects-chrome.css +888 -0
  373. package/src/styles/finance-composites.css +214 -0
  374. package/src/styles/finance-tearsheet-overlay.css +90 -0
  375. package/src/styles/finance-tearsheet.css +945 -0
  376. package/src/styles/marquee.css +168 -0
  377. package/src/styles/metrics.css +82 -0
  378. package/src/styles/nav-shell.css +551 -0
  379. package/src/styles/pricing.css +79 -0
  380. package/src/styles/repo-activity.css +575 -0
  381. package/src/styles/stages.css +69 -0
  382. package/src/styles/terminal-loaders.css +122 -0
  383. package/src/styles/terminal-manifest.css +228 -0
  384. package/src/styles/web-theme.css +1007 -0
  385. package/src/styles/word-reveal.css +204 -0
  386. package/src/theme/apply-theme.ts +214 -0
  387. package/src/theme/index.ts +27 -0
  388. package/src/theme/palette-registry.ts +855 -0
  389. package/src/theme/tui-prefs.ts +64 -0
  390. package/src/ui/alert.tsx +78 -0
  391. package/src/ui/avatar.tsx +104 -0
  392. package/src/ui/badge.tsx +75 -0
  393. package/src/ui/breadcrumb.tsx +61 -0
  394. package/src/ui/button.tsx +67 -0
  395. package/src/ui/card.tsx +139 -0
  396. package/src/ui/checkbox.tsx +27 -0
  397. package/src/ui/collapsible.tsx +32 -0
  398. package/src/ui/date-pager.tsx +325 -0
  399. package/src/ui/dialog.tsx +157 -0
  400. package/src/ui/dropdown-menu.tsx +267 -0
  401. package/src/ui/empty-state.tsx +165 -0
  402. package/src/ui/field.tsx +104 -0
  403. package/src/ui/form.tsx +36 -0
  404. package/src/ui/icon-button.tsx +36 -0
  405. package/src/ui/index.ts +34 -0
  406. package/src/ui/input.tsx +33 -0
  407. package/src/ui/kbd.tsx +30 -0
  408. package/src/ui/label.tsx +32 -0
  409. package/src/ui/pager.tsx +119 -0
  410. package/src/ui/pagination.tsx +126 -0
  411. package/src/ui/radio-group.tsx +51 -0
  412. package/src/ui/search-bar.tsx +85 -0
  413. package/src/ui/segmented-control.tsx +76 -0
  414. package/src/ui/select.tsx +257 -0
  415. package/src/ui/separator.tsx +24 -0
  416. package/src/ui/sheet.tsx +141 -0
  417. package/src/ui/skeleton.tsx +76 -0
  418. package/src/ui/slider.tsx +76 -0
  419. package/src/ui/switch.tsx +31 -0
  420. package/src/ui/table.tsx +179 -0
  421. package/src/ui/tabs.tsx +89 -0
  422. package/src/ui/tags-input.tsx +168 -0
  423. package/src/ui/textarea.tsx +17 -0
  424. package/src/ui/tooltip.tsx +71 -0
@@ -0,0 +1,1007 @@
1
+ /* ==========================================================================
2
+ @digithings-ai/ui — THE Tailwind v4 token bridge. One definition, every app.
3
+ Bridges the [data-theme] semantic tokens (from @digithings-ai/design/tokens.css)
4
+ to Tailwind color/font utilities. Import order in an app's globals.css:
5
+ @import "tailwindcss";
6
+ @import "@digithings-ai/design/tokens.css";
7
+ @import "@digithings-ai/ui/styles/web-theme.css";
8
+
9
+ `@theme inline` (not plain `@theme`) is load-bearing: utilities emit
10
+ `var(--token)` at the use site (`text-ink` → `color: var(--ink)`), so a
11
+ scoped livery (`.accent-digiquant { --accent: … }`) or a `data-theme` flip
12
+ re-resolves inside every utility. Plain `@theme` freezes the var at :root
13
+ and scoped overrides never reach the utility. Apps must not declare their
14
+ own bridge — extend this one.
15
+ ========================================================================== */
16
+
17
+ /* Overlay state variants — the kit's Collapsible/Dialog/Sheet/Menu emit
18
+ `data-open`/`data-closed` (Base UI attrs, Radix `data-state`). Declared on
19
+ the bridge so consumers need no local copy. */
20
+ @custom-variant data-open (&:where([data-state="open"], [data-open]:not([data-open="false"])));
21
+
22
+ @custom-variant data-closed (&:where([data-state="closed"], [data-closed]:not([data-closed="false"])));
23
+
24
+ /* Skeleton shimmer (#4306, batch K1) — the kit <Skeleton/> rides the stock
25
+ sk-* sweep; the keyframes live on the bridge so every consumer that imports
26
+ web-theme gets them, with no controls-core.css dependency. */
27
+ @keyframes sk-shimmer {
28
+ to {
29
+ transform: translateX(100%);
30
+ }
31
+ }
32
+
33
+ @theme inline {
34
+ --color-bg: var(--bg);
35
+ --color-surface: var(--surface);
36
+ --color-surface-2: var(--surface-2);
37
+ --color-hair: var(--hair);
38
+ --color-hair-2: var(--hair-2);
39
+
40
+ --color-ink: var(--ink);
41
+ --color-ink-soft: var(--ink-soft);
42
+ --color-ink-mute: var(--ink-mute);
43
+
44
+ --color-accent: var(--accent);
45
+ --color-accent-weak: var(--accent-weak);
46
+ --color-on-accent: var(--on-accent);
47
+
48
+ --color-up: var(--up);
49
+ --color-down: var(--down);
50
+ --color-warn: var(--warn);
51
+ --color-danger: var(--danger);
52
+ --color-heat-ink: var(--heat-ink);
53
+ --color-heat-ink-strong: var(--heat-ink-strong);
54
+
55
+ --color-term-bg: var(--term-bg);
56
+ --color-term-ink: var(--term-ink);
57
+ --color-term-hair: var(--term-hair);
58
+ --color-term-fill: var(--term-fill);
59
+ --color-term-mute: var(--term-mute);
60
+
61
+ --font-sans: var(--font-sans);
62
+ --font-mono: var(--font-mono);
63
+ --font-display: var(--font-display);
64
+
65
+ --ease-brand: cubic-bezier(0.22, 1, 0.36, 1);
66
+
67
+ /* shadcn/ui token contract — Base UI base, lyra preset.
68
+ See docs/superpowers/specs/2026-09-16-shadcn-migration-design.md §3.2 */
69
+ --color-background: var(--bg);
70
+ --color-foreground: var(--ink);
71
+ --color-card: var(--surface);
72
+ --color-card-foreground: var(--ink);
73
+ --color-popover: var(--surface-2);
74
+ --color-popover-foreground: var(--ink);
75
+ --color-primary: var(--ink);
76
+ --color-primary-foreground: var(--bg);
77
+ --color-secondary: var(--surface-2);
78
+ --color-secondary-foreground: var(--ink);
79
+ --color-muted: var(--surface);
80
+ --color-muted-foreground: var(--ink-soft);
81
+ /* Brand --color-accent is intentionally held (owned by the row at the top of
82
+ this block); shadcn's subtle accent role + brand-alias rename defer to the
83
+ Wave 2/3 alias sweep. */
84
+ --color-accent-foreground: var(--on-accent);
85
+ --color-destructive: var(--danger);
86
+ --color-border: var(--hair);
87
+ --color-input: var(--hair);
88
+ --color-ring: color-mix(in srgb, var(--accent) 40%, transparent);
89
+ --color-input-background: var(--surface);
90
+ --color-chart-1: var(--ink);
91
+ --color-chart-2: var(--accent);
92
+ --color-chart-3: var(--ink-soft);
93
+ --color-chart-4: var(--up);
94
+ --color-chart-5: var(--warn);
95
+ --radius-sm: 0;
96
+ --radius-md: 0;
97
+ --radius-lg: 0;
98
+ --radius-xl: 0;
99
+
100
+ /* Collapsible height animation. The Base UI panel exposes
101
+ --collapsible-panel-height (the kit's Collapsible); the Radix var stays
102
+ first for Radix consumers. Caveat: tw-animate-css declares the same
103
+ keyframes WITHOUT the Base UI var, so an app importing it after this
104
+ bridge (reference, digichat) still needs its local copy to win. */
105
+ @keyframes collapsible-down {
106
+ from { height: 0; }
107
+ to { height: var(--radix-collapsible-content-height, var(--collapsible-panel-height, auto)); }
108
+ }
109
+ @keyframes collapsible-up {
110
+ from { height: var(--radix-collapsible-content-height, var(--collapsible-panel-height, auto)); }
111
+ to { height: 0; }
112
+ }
113
+
114
+ /* Entrance choreography (D1, #4429) — the staged hero reveal. Declared in
115
+ THIS block rather than a second one: web-theme.test.ts pins exactly one
116
+ `@theme inline`, because a second is how a token bridge drifts. */
117
+ --animate-appear: kit-appear 0.6s forwards ease-out;
118
+ --animate-appear-zoom: kit-appear-zoom 0.6s forwards ease-out;
119
+ --line-width: 1px;
120
+
121
+ @keyframes kit-appear {
122
+ 0% { opacity: 0; transform: translateY(1rem); filter: blur(0.5rem); }
123
+ 50% { filter: blur(0); }
124
+ 100% { opacity: 1; transform: translateY(0); filter: blur(0); }
125
+ }
126
+
127
+ @keyframes kit-appear-zoom {
128
+ 0% { opacity: 0; transform: scale(0.5); }
129
+ 100% { opacity: 1; transform: scale(1); }
130
+ }
131
+ }
132
+
133
+ /* Base element defaults shared by the React apps.
134
+ Utilitarian v0.1: mono is the default voice (claims, body, chrome). */
135
+ body {
136
+ background: var(--bg);
137
+ color: var(--ink);
138
+ font-family: var(--font-mono);
139
+ line-height: 1.55;
140
+ letter-spacing: -0.015em;
141
+ -webkit-font-smoothing: antialiased;
142
+ text-rendering: optimizeLegibility;
143
+ /* Theme flip only — color/background, not layout properties. */
144
+ transition: background 0.4s var(--ease), color 0.4s var(--ease);
145
+ }
146
+ ::selection { background: var(--accent); color: var(--on-accent); }
147
+ :focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 0; }
148
+
149
+ /* ── stack logos / chips ─────────────────────────────────────────────── */
150
+ .stack-row { display: flex; flex-wrap: wrap; gap: 0.45rem; margin-top: 0.6rem; }
151
+ .stack-chip { display: inline-flex; align-items: center; gap: 0.4rem; border: 1px solid var(--hair); background: var(--surface); border-radius: 0; padding: 0.32rem 0.6rem; font-family: var(--font-mono); font-size: 0.74rem; color: var(--ink-soft); transition: border-color .18s var(--ease), color .18s var(--ease); }
152
+ .stack-chip:hover { border-color: var(--hair-2); color: var(--ink); }
153
+ .stack-ico { color: var(--ink-mute); transition: color .18s var(--ease); }
154
+ .stack-chip:hover .stack-ico { color: var(--brand, var(--accent)); }
155
+ .stack-mono { display: inline-grid; place-items: center; min-width: 0.9375rem; height: 0.9375rem; font-size: 0.6rem; font-weight: 600; color: var(--ink-mute); border: 1px solid var(--hair-2); border-radius: 0; padding: 0 0.125rem; }
156
+ .stack-row.compact .stack-name { display: none; }
157
+ .stack-row.compact .stack-chip { padding: 0.3rem; }
158
+
159
+ /* ── module cards ────────────────────────────────────────────────────── */
160
+ .mod-card { display: flex; flex-direction: column; gap: 0.3rem; background: var(--surface); border: 1px solid var(--hair); border-radius: var(--r-md); padding: 1.4rem 1.3rem; transition: transform .3s var(--ease), border-color .3s var(--ease); position: relative; overflow: hidden; }
161
+ .mod-card::before { content: ""; position: absolute; inset: 0 0 auto 0; height: 0.125rem; background: var(--accent); transform: scaleX(0); transform-origin: left; transition: transform .32s var(--ease); }
162
+ /* transform-origin has no logical keyword — fill from the inline-start edge */
163
+ [dir="rtl"] .mod-card::before { transform-origin: right; }
164
+ .mod-card:hover { transform: translateY(-4px); border-color: var(--hair-2); }
165
+ .mod-card:hover::before { transform: scaleX(1); }
166
+ .mod-card-top { display: flex; align-items: center; justify-content: space-between; margin-bottom: 0.3rem; }
167
+ .mod-card h3 { font-size: 1.14rem; font-weight: 600; letter-spacing: -0.02em; }
168
+ .mod-card .role { font-size: 0.8rem; color: var(--ink-soft); }
169
+ .dg-tier { font-family: var(--font-mono); font-size: 0.6rem; letter-spacing: 0.12em; text-transform: uppercase; color: var(--ink-mute); border: 1px solid var(--hair); border-radius: 0; padding: 0.16rem 0.5rem; }
170
+ /* .dg-tier.t-roadmap reads --warn as small text; the light --warn token was
171
+ raised to #8F6220 in tokens.css (canon-audit S2) so this needs no local
172
+ override — dark stays #E0B341. */
173
+ .dg-tier.t-core { color: var(--accent); border-color: var(--accent-weak); }
174
+ .dg-tier.t-roadmap { color: var(--warn); border-color: color-mix(in srgb, var(--warn) 40%, transparent); }
175
+
176
+ /* ── scrollytelling graph ────────────────────────────────────────────── */
177
+ .dg-track { position: relative; }
178
+ .dg-stage { position: sticky; top: 0; height: 100vh; display: flex; flex-direction: column; justify-content: center; }
179
+ /* minmax(0,...) locks both tracks: content (long stack rows, docker cmds)
180
+ can never expand a column, so the graph + panel never shift while scrolling. */
181
+ .dg-stage-grid { display: grid; grid-template-columns: minmax(0, 1.2fr) minmax(0, 0.78fr); gap: clamp(1.5rem, 4vw, 3.5rem); align-items: center; max-width: var(--wrap); margin-inline: auto; padding-inline: var(--gutter); width: 100%; }
182
+ .dg-graph { width: 100%; height: auto; max-height: 70vh; }
183
+ .dg-graph-wrap { min-width: 0; }
184
+ .dg-edge { stroke: var(--hair-2); stroke-width: 1.4; fill: none; opacity: 0.5; transition: stroke .25s var(--ease), opacity .25s var(--ease); }
185
+ .dg-edge.has-active { opacity: 0.12; }
186
+ .dg-edge.on { opacity: 0.95; stroke: var(--accent); stroke-width: 2; }
187
+ .dg-node { cursor: pointer; }
188
+ .dg-node .halo { fill: var(--accent); opacity: 0; transition: opacity .25s var(--ease); }
189
+ .dg-node .node { fill: color-mix(in srgb, var(--accent) 20%, var(--surface)); stroke: var(--accent); stroke-width: 1.6; transition: stroke-width .25s var(--ease); }
190
+ .dg-node.hub .node { fill: color-mix(in srgb, var(--accent) 30%, var(--surface)); stroke-width: 2; }
191
+ .dg-node .label { fill: var(--ink-soft); font-family: var(--font-mono); font-size: var(--text-sm); text-anchor: middle; font-weight: 500; }
192
+ .dg-node.hub .label { fill: var(--ink); font-size: 0.875rem; }
193
+ .dg-node.on .halo { opacity: 0.22; } .dg-node.on .node { stroke-width: 2.6; } .dg-node.on .label { fill: var(--ink); }
194
+ .dg-node.dim { opacity: 0.32; }
195
+ .dg-rail { --rail-dot: 0.375rem; --rail-dot-gap: var(--space-2); position: absolute; inset-inline-end: clamp(1rem, 3vw, 2rem); top: 50%; transform: translateY(-50%); display: flex; flex-direction: column; gap: var(--rail-dot-gap); }
196
+ .dg-tick { width: var(--rail-dot); height: var(--rail-dot); border-radius: 50%; background: var(--hair-2); transition: background .2s var(--ease), transform .2s var(--ease); }
197
+ .dg-tick.on { background: var(--accent); transform: scale(1.5); }
198
+
199
+ /* fixed min-height + top alignment so the box never grows/shrinks per module;
200
+ only the text inside changes. */
201
+ .dg-panel { background: var(--surface); border: 1px solid var(--hair); border-radius: var(--r-lg); padding: clamp(1.4rem, 2.5vw, 2rem); min-height: 26rem; min-width: 0; }
202
+ .dg-panel .dg-tag { min-height: 3.2em; }
203
+ .dg-panel-head { display: flex; align-items: center; gap: 0.9rem; }
204
+ .dg-panel-head h3 { font-family: var(--font-display); font-weight: 400; font-size: 1.9rem; letter-spacing: -0.01em; }
205
+ .dg-tag { color: var(--ink-soft); margin: 0.7rem 0 0.4rem; }
206
+ .dg-docker { font-family: var(--font-mono); font-size: 0.9rem; color: var(--term-ink); background: var(--term-bg); border: 1px solid var(--term-hair); border-radius: 0; padding: 0.5rem 0.7rem; margin-top: 0.8rem; overflow-x: auto; white-space: nowrap; }
207
+ .dg-docker .prompt { color: var(--accent); }
208
+ .dg-more { display: inline-block; margin-top: 0.9rem; font-family: var(--font-mono); font-size: 0.82rem; color: var(--accent); }
209
+
210
+ /* code block (module init snippet) */
211
+ .codeblock { background: var(--term-bg); border: 1px solid var(--term-hair); border-radius: 0; padding: 1.1rem 1.3rem; font-family: var(--font-mono); font-size: 0.82rem; line-height: 1.9; color: var(--term-ink); overflow-x: auto; white-space: pre-wrap; }
212
+
213
+ /* ── the flat module grid (ModuleGrid, v13 stage 4) ────────────────────────
214
+ The angular sibling of the SVG graph: every module is a square-ish cell in
215
+ one grid for the whole run, the focused cell is the one the scroll selects,
216
+ and the detail sits in a flat panel beside it. No radius anywhere — this is
217
+ the canon's flat-corner grammar, where the graph above is the rounded one.
218
+ The grid is sized so all modules fit one viewport without an internal
219
+ scrollbar (12 cells at 4 columns = 3 rows). */
220
+ /* ── mosaic v4: a fixed order that only resizes ────────────────────────────
221
+ No rail, no side panel, no per-cell logo or tier colour. Three fixed rows of
222
+ flat tiles; the focused tile grows in place and reveals its detail, so the
223
+ whole story stays in a single view.
224
+
225
+ The order is decided at module scope (`ROW_SIZES`), never by the layout
226
+ engine, because the owner's rule is that the tiles keep their places and only
227
+ the sizes move. v3 packed them widest-first to make the rows flush, which
228
+ silently made reading order a function of tile size — and it re-solved on
229
+ every focus step, so the tiles swapped places as the scroll advanced.
230
+
231
+ Both levels resize with `flex-grow`, which interpolates natively; a grid
232
+ `span` does not, so v3 could only jump. The rows are flex items of a column
233
+ container and the tiles are flex items of their row, so a row's width is
234
+ always exactly its parent's and the rows are flush by construction — the
235
+ property v3 needed a backtracking partition to reach. */
236
+ .dg-mosaic--rows {
237
+ display: block;
238
+ position: relative;
239
+ /* Rail-bounded (#4429): the mosaic spans rail to rail — never the full
240
+ viewport — with one page-pad of air inside each rail, the same grammar
241
+ every section uses (Tailwind preflight is border-box, so the padding
242
+ insets inside the max-width). Tiles size against the frame width, not
243
+ the window.
244
+ Always-full (v16): the mosaic fills the stage at every scroll position —
245
+ landing, walk, and release. There is no height transition because height
246
+ no longer changes.
247
+ Packing box (v17): the tiles are absolutely positioned rects solved by
248
+ the squarified treemap in `ModuleGrid` (areas ∝ LOC, no fixed rows), so
249
+ the container is a plain relative box, not a flex column. Motion eases
250
+ each tile's translate/width/height between solves. On viewports taller
251
+ than the cap the grid stops growing instead of going cavernous (owner:
252
+ if the page gets too large the sections below should still come into
253
+ view). */
254
+ width: 100%;
255
+ max-width: calc(var(--frame-w) + 2 * var(--page-pad));
256
+ margin-inline: auto;
257
+ padding-inline: var(--page-pad);
258
+ height: 100%;
259
+ max-height: 64rem;
260
+ overflow: hidden;
261
+ }
262
+ /* Packed tiles are positioned by inline rects (left/top/width/height from the
263
+ treemap solve), so they leave normal flow here. Scoped to the mosaic face:
264
+ the stacked face keeps its flow tiles. The focus walk's size morph eases on
265
+ the brand curve — the treemap re-solves areas and CSS carries each tile to
266
+ its new rect. Opacity starts hidden until the first solve lands: the box is
267
+ measured one effect-tick after mount, and unpositioned tiles must not flash
268
+ piled at the corner (visibility, not opacity, so rest/focus/hover fading
269
+ stays untouched). */
270
+ .dg-mosaic--rows .dg-cell {
271
+ position: absolute;
272
+ inset-block-start: 0;
273
+ left: 0; /* physical: the tile's translate is the treemap's physical x */
274
+ transition:
275
+ transform 0.5s var(--ease),
276
+ width 0.5s var(--ease),
277
+ height 0.5s var(--ease),
278
+ opacity 0.25s var(--ease),
279
+ background 0.25s var(--ease),
280
+ box-shadow 0.25s var(--ease);
281
+ }
282
+ /* Load and resize are layout, not motion (ModuleGrid sets both hooks): the
283
+ mosaic is unseen until its box has settled once, then fades in whole, and a
284
+ box change places tiles without easing them. Opacity on the container,
285
+ because visibility on a tile does not hide descendants that set their own. */
286
+ .dg-mosaic--rows {
287
+ transition: opacity 0.4s var(--ease);
288
+ }
289
+ .dg-mosaic--rows:not([data-settled]) {
290
+ opacity: 0;
291
+ }
292
+ .dg-mosaic--rows:not([data-settled]) .dg-cell,
293
+ .dg-mosaic--rows[data-resizing] .dg-cell {
294
+ transition:
295
+ opacity 0.25s var(--ease),
296
+ background 0.25s var(--ease),
297
+ box-shadow 0.25s var(--ease);
298
+ }
299
+ /* The mosaic's own stage sits below the pinned top bar — never behind it —
300
+ so the walk plays in the remaining viewport, vertically centred. Scoped
301
+ to the mosaic; the tour keeps the fullscreen stage. */
302
+ .dg-stage--mosaic {
303
+ top: var(--dq-nav-h, 3.875rem);
304
+ height: calc(100dvh - var(--dq-nav-h, 3.875rem));
305
+ /* Dock padding (owner choreography): the grid breathes below the bar and
306
+ above the viewport bottom instead of sticking to either edge. Scoped to
307
+ the mosaic stage only; box-sizing keeps the padding inside the height. */
308
+ padding-block: clamp(1rem, 3vh, 2.5rem);
309
+ box-sizing: border-box;
310
+ /* Centred: the mosaic is full-height in every state that matters, so this
311
+ only binds on viewports taller than its cap — the grid sits mid-stage
312
+ instead of sticking to the hero. */
313
+ justify-content: center;
314
+ }
315
+
316
+ /* Extreme zoom-out (very tall viewport): once the mosaic hits its height cap,
317
+ centring leaves cavernous gaps above and below. Top-align it instead so it
318
+ enters the view with the section that follows, rather than floating in the
319
+ middle of an empty screen. */
320
+ @media (min-height: 1400px) {
321
+ .dg-stage--mosaic {
322
+ justify-content: flex-start;
323
+ }
324
+ }
325
+
326
+ /* The stacked face — the same boxes on a phone, one per row.
327
+ There is no pinned track here, so the column is in normal flow and grows with
328
+ its content. Every tile is open: the tile under the reader's eye only lights
329
+ (`.dg-cell.on`, paint only), because opening one in flow would push the rest
330
+ of the page down under a moving thumb. The wrap carries the page's own
331
+ gutter because there is no stage to centre it. */
332
+ .dg-stack-wrap {
333
+ padding-inline: var(--page-pad);
334
+ /* Breathing room below the section's top separator: the first card must sit
335
+ a step down from the line-t rule, not touch it. */
336
+ padding-block: clamp(1rem, 3vh, 2rem);
337
+ }
338
+ .dg-mosaic--stack { height: auto; gap: var(--space-2); }
339
+ .dg-mosaic--stack .dg-mosaic-row { flex: 0 0 auto; transition: none; }
340
+ /* The slide-up entrance wrapper (kit Reveal) is the row's flex child, not the
341
+ tile — so it carries the tile's flex share and stretches the tile to fill
342
+ it. Without this the wrapper would size to content and the tile would never
343
+ fill the row width. */
344
+ .dg-mosaic--stack .dg-stack-reveal { flex: 1 1 auto; min-width: 0; display: flex; }
345
+ .dg-mosaic--stack .dg-stack-reveal > .dg-cell { flex: 1 1 auto; }
346
+ /* One tile per row, so the tile fills the row's width. `flex: 0 0 auto` here
347
+ would size the tile to its own max-content — which measured 760px on a 420px
348
+ viewport and pushed the whole page sideways. Height is content-driven because
349
+ the row is `flex: 0 0 auto` inside the column. */
350
+ .dg-mosaic--stack .dg-cell { flex: 1 1 auto; min-width: 0; opacity: 0.72; transition: opacity .25s var(--ease), background .25s var(--ease), box-shadow .25s var(--ease); }
351
+ .dg-mosaic--stack .dg-cell.on { opacity: 1; }
352
+ .dg-mosaic-row { display: flex; flex: 1 1 0; min-height: 0; gap: var(--space-2); /* Stacked face only: the mosaic face has no rows since v17 (treemap). No size transition here in either face — the mosaic morph is motion `animate` on the tiles, the stack is content-driven. CSS keeps only the paint transitions below. */ }
353
+
354
+ .dg-cell { --dg-cell-pad-block: 0.85rem; --dg-cell-pad-inline: 0.95rem; --dg-cell-gap: var(--space-2); position: relative; display: flex; flex-direction: column; gap: var(--dg-cell-gap); flex: 1 1 0; min-width: 0; min-height: 0; padding: var(--dg-cell-pad-block) var(--dg-cell-pad-inline); margin: 0; background: var(--surface); border: 1px solid var(--hair); border-radius: 0; text-align: left; cursor: pointer; overflow: hidden; opacity: 0.55; transition: opacity .25s var(--ease), background .25s var(--ease), box-shadow .25s var(--ease); }
355
+ .dg-cell:hover { opacity: 0.85; }
356
+ .dg-cell.on { opacity: 1; background: var(--surface-2, var(--surface)); box-shadow: inset 0 0 0 1px var(--accent); cursor: default; overflow: hidden; scrollbar-width: none; }
357
+ .dg-cell, .dg-mosaic, .dg-mosaic-stack { scrollbar-width: none; }
358
+ .dg-cell::-webkit-scrollbar, .dg-mosaic::-webkit-scrollbar, .dg-mosaic-stack::-webkit-scrollbar { display: none; width: 0; height: 0; }
359
+
360
+ /* Name left, version right: the tile's identity line. The version sits in the
361
+ top-right corner where a reader scanning the mosaic can compare releases down
362
+ a column. The head wraps, and the version carries `margin-left: auto` so it
363
+ keeps its right edge on the line it lands on — both halves are `nowrap`, so
364
+ without the wrap a narrow tile had its name run underneath it. */
365
+ .dg-mosaic-head { display: flex; flex-wrap: wrap; align-items: flex-start; justify-content: space-between; gap: 0.25rem 0.6rem; min-width: 0; }
366
+ .dg-mosaic-version { margin-left: auto; text-align: right; }
367
+ .dg-mosaic-link {
368
+ margin-left: 0.35rem;
369
+ flex: 0 0 auto;
370
+ font-family: var(--font-mono);
371
+ font-size: 0.95rem;
372
+ line-height: 1;
373
+ color: var(--ink-mute);
374
+ text-decoration: none;
375
+ transition: color 0.2s var(--ease);
376
+ }
377
+ .dg-mosaic-link:hover,
378
+ .dg-mosaic-link:focus-visible { color: var(--ink); }
379
+ .dg-mosaic-name { font-family: var(--font-mono); font-size: 1.35rem; font-weight: 500; letter-spacing: -0.01em; color: var(--ink); min-width: 0; overflow-wrap: anywhere; }
380
+ /* Mini tiles step the name down a notch instead of clipping it: the full
381
+ name still shows (never an abbreviation), just set smaller next to the
382
+ version, so the head fits ~135px with padding. */
383
+ .dg-cell[data-tier="mini"] .dg-mosaic-name { font-size: 1.1rem; }
384
+ /* A hero-banner mark in place of the text name (10 cells tall); tiles narrow it by tier. */
385
+ .dg-mosaic-mark { display: block; flex: 0 1 auto; width: min(100%, 11rem); }
386
+ .dg-cell[data-tier="mini"] .dg-mosaic-mark { width: min(100%, 8.5rem); }
387
+
388
+ /* The whole tile is the focus target, but the focused tile also owns real
389
+ controls (copy the compose command, ask digichat). A <button> cannot contain
390
+ another button, so the focus action is a transparent overlay *under* the body
391
+ rather than the body's parent; the body re-enables pointer events only on its
392
+ own controls, so every other click falls through to the overlay. The overlay
393
+ lives inside an `overflow: hidden` tile, so the global `:focus-visible`
394
+ outline (drawn outside the box) would be clipped — the ring is drawn just
395
+ inside the edge instead. */
396
+ .dg-cell-focus { position: absolute; inset: 0; z-index: 0; width: 100%; height: 100%; margin: 0; padding: 0; border: 0; background: transparent; cursor: pointer; }
397
+ .dg-cell.on .dg-cell-focus { cursor: default; }
398
+ .dg-cell-focus:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
399
+ .dg-cell-body { position: relative; z-index: 1; display: flex; flex-direction: column; gap: var(--dg-cell-gap); flex: 1 1 auto; min-height: 0; pointer-events: none; }
400
+ .dg-cell-body button { pointer-events: auto; }
401
+
402
+ /* The three stated facts — lines of code, endpoints, MCP tools — stacked under
403
+ the name in every tile, focused or resting, so the box is a constant the
404
+ reader learns once. A label/value row each in the same `.dg-loc` mono
405
+ micro-type the corner figures used; the value is right-aligned with
406
+ `tabular-nums` so the column compares cleanly down the mosaic. */
407
+ .dg-mosaic-facts { font-family: var(--font-mono); font-size: 0.86rem; line-height: 1.5; letter-spacing: 0.01em; color: var(--ink-soft); font-variant-numeric: tabular-nums; }
408
+ /* The packages sit at the foot of every tile, expanded or resting. `margin-top:
409
+ auto` pins them to the bottom so the boxes line up whether or not the tile is
410
+ open; the chips print their names, because the owner asked for the packages to
411
+ be readable "in the non-expanded view". On the focused tile they pack to the
412
+ right, directly above the compose line. */
413
+ .dg-mosaic-stack { display: block; margin-top: auto; min-width: 0; }
414
+ .dg-mosaic-stack .stack-row { margin-top: 0; }
415
+ /* Focused tiles: the chips shrink to their own width and sit in the bottom-right,
416
+ just above the compose line. The cap wraps a long row into that corner
417
+ instead of stretching it across the card. */
418
+ .dg-cell.on .dg-mosaic-stack { align-self: stretch; width: 100%; max-width: 100%; }
419
+ .dg-cell.on .dg-mosaic-stack .stack-row { justify-content: flex-start; }
420
+ /* Package chips wrap inside the tile. A single nowrap row used to scroll
421
+ out of the cell while the mosaic was resizing. */
422
+ .dg-mosaic-stack .stack-row,
423
+ .dg-mosaic-stack .stack-row.compact {
424
+ flex-wrap: wrap;
425
+ overflow: hidden;
426
+ max-width: 100%;
427
+ }
428
+ .dg-mosaic-stack .stack-chip { min-width: 0; max-width: 100%; }
429
+ .dg-mosaic-role { font-family: var(--font-mono); font-size: 0.9rem; line-height: 1.5; color: var(--ink-mute); }
430
+ .dg-cell.on .dg-mosaic-role { color: var(--ink-soft); }
431
+ .dg-mosaic-detail { display: flex; flex-direction: column; gap: 0.6rem; margin-top: 0.4rem; min-width: 0; }
432
+ /* The one part of the focused tile allowed to shrink. The tile is one row tall,
433
+ and the earlier version let the copy take as much as it wanted — which pushed
434
+ the compose command and the ask control out of the box on the longest modules
435
+ (digigraph, digivault). The copy shrinks, the stack row and the foot below are
436
+ pinned, and the lead paragraph is clamped, so the controls are always visible. */
437
+
438
+ /* The WHY drawer's typewriter fill. Each owned word carries its index (`--i`)
439
+ and the word count (`--n`), and the drawer writes a single `--seam-fill`
440
+ (0..1) on the grid; the clamp below turns that into a per-word opacity, so
441
+ the sentence types itself out as the panel slides right. `--seam-fill`
442
+ defaults to 1, so SSR, no-JS and prefers-reduced-motion all render the full
443
+ line with no reveal. */
444
+ .dg-seam-word {
445
+ opacity: clamp(0, calc(var(--seam-fill, 1) * var(--n) - var(--i)), 1);
446
+ }
447
+ /* The compose command and the CTA share the last line. The command wraps
448
+ on the focused tile so a zoomed page lengthens the card instead of
449
+ cutting the line off. */
450
+ .dg-mosaic-foot { display: flex; align-items: center; flex-wrap: wrap; gap: var(--dg-cell-gap) 0.9rem; margin-top: 0; min-width: 0; flex: 0 0 auto; }
451
+ .dg-mosaic-foot .dg-docker { margin-top: 0; min-width: 0; }
452
+ .dg-mosaic-ask { margin-left: auto; white-space: nowrap; font-family: var(--font-mono); font-size: 0.9rem; color: var(--ink-mute); }
453
+ button.dg-mosaic-ask { background: none; border: 0; padding: 0; cursor: pointer; }
454
+ button.dg-mosaic-ask:hover { color: var(--ink); }
455
+
456
+ /* The compose command is a control in the focused tile: a click copies it. The
457
+ `.dg-docker` skin is shared with the graph panel, where the command is plain
458
+ text, so the pointer affordance is scoped to the button form. */
459
+ button.dg-docker { cursor: pointer; text-align: left; }
460
+ button.dg-docker:hover { border-color: var(--hair-2); }
461
+ button.dg-docker.is-copied { color: var(--accent); }
462
+
463
+ /* Resting tiles clip detail that does not fit their share of the mosaic.
464
+ The focused tile unclamps its copy and grows inside the fixed grid.
465
+ Text that still exceeds that grid scrolls inside the card. */
466
+ .dg-cell .dg-mosaic-detail { overflow: hidden; flex: 1 1 auto; min-height: 0; }
467
+ .dg-cell.on .dg-mosaic-detail { overflow: visible; flex: 0 1 auto; min-height: auto; }
468
+ .dg-cell.on .dg-docker { margin-top: 0; overflow: visible; white-space: normal; }
469
+ .dg-cell.on .dg-mosaic-serves {
470
+ display: block;
471
+ -webkit-line-clamp: unset;
472
+ line-clamp: unset;
473
+ overflow: visible;
474
+ }
475
+
476
+ /* The deeper description paragraphs: what the module is for, in its own words.
477
+ Only on the focused tile — a resting tile is a fraction of a row tall, so it
478
+ carries the name, version, stats box and role and nothing else. */
479
+ .dg-mosaic-serves { color: var(--ink-mute); font-family: var(--font-mono); font-size: 0.94rem; line-height: 1.5; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 5; line-clamp: 5; overflow: hidden; }
480
+
481
+ /* Card density tiers (owner: a tile shows whatever fits, never clipped).
482
+ The tile's tier is set from its measured width and height (see
483
+ ModuleGrid): full shows everything; medium keeps the head plus the
484
+ logo-only package row and drops the role prose (the collapsed tile keeps
485
+ the owner's logo packages); mini keeps name + version only. Tiers
486
+ CROSS-FADE (opacity + visibility) instead of flipping display: tiles clip
487
+ (overflow hidden on .dg-cell) and the hidden content keeps its layout, so
488
+ a tier change is paint-only — no pop, no reflow. */
489
+ .dg-cell .dg-mosaic-stack,
490
+ .dg-cell .dg-mosaic-role,
491
+ .dg-cell .dg-mosaic-detail {
492
+ opacity: 1;
493
+ visibility: visible;
494
+ transition: opacity 0.3s var(--ease), visibility 0s;
495
+ }
496
+ .dg-cell[data-tier="medium"] .dg-mosaic-role,
497
+ .dg-cell[data-tier="medium"] .dg-mosaic-detail,
498
+ .dg-cell[data-tier="mini"] .dg-mosaic-role,
499
+ .dg-cell[data-tier="mini"] .dg-mosaic-stack,
500
+ .dg-cell[data-tier="mini"] .dg-mosaic-detail {
501
+ opacity: 0;
502
+ visibility: hidden;
503
+ transition: opacity 0.3s var(--ease), visibility 0s 0.3s;
504
+ }
505
+ /* The name stays put. Focus detail enters as the tile grows and leaves
506
+ ahead of the shrink, so a step never blanks the grid or squishes the copy.
507
+ Resting tiles take the detail out of flow (no gap, no extra height) and
508
+ fade it; the focused tile brings it back in flow once the box has started
509
+ to open. Transform/opacity only, per the kit's motion vocabulary. */
510
+ .dg-cell-body { animation: dg-cell-in 0.3s var(--ease) 0.12s both; }
511
+ .dg-mosaic--rows .dg-cell-body { animation: none; }
512
+ @keyframes dg-cell-in {
513
+ from { opacity: 0; transform: translateY(6px); }
514
+ }
515
+ .dg-mosaic--rows .dg-cell:not(.on) .dg-mosaic-enter {
516
+ position: absolute;
517
+ z-index: 1;
518
+ inset-inline: var(--dg-cell-pad-inline);
519
+ margin-top: 0;
520
+ opacity: 0;
521
+ transform: translateY(6px);
522
+ pointer-events: none;
523
+ visibility: hidden;
524
+ transition:
525
+ opacity 0.18s var(--ease),
526
+ transform 0.18s var(--ease),
527
+ visibility 0s linear 0.18s;
528
+ }
529
+ .dg-mosaic--rows .dg-cell.on .dg-mosaic-enter {
530
+ position: relative;
531
+ opacity: 1;
532
+ transform: none;
533
+ pointer-events: auto;
534
+ visibility: visible;
535
+ transition:
536
+ opacity 0.34s var(--ease),
537
+ transform 0.42s var(--ease) 0.14s,
538
+ visibility 0s linear 0s;
539
+ }
540
+ /* Reduced motion: every mosaic transition above goes instant — dock,
541
+ growth, tier fades, body crossfade. State changes still apply, with no animation. */
542
+ @media (prefers-reduced-motion: reduce) {
543
+ .dg-mosaic--rows,
544
+ .dg-mosaic-row,
545
+ .dg-cell,
546
+ .dg-cell .dg-mosaic-stack,
547
+ .dg-cell .dg-mosaic-role,
548
+ .dg-cell .dg-mosaic-detail,
549
+ .dg-mosaic--rows .dg-cell:not(.on) .dg-mosaic-enter,
550
+ .dg-mosaic--rows .dg-cell.on .dg-mosaic-enter {
551
+ transition: none;
552
+ }
553
+ .dg-cell-body { animation: none; }
554
+ .dg-mosaic--rows .dg-cell:not(.on) .dg-mosaic-enter { transform: none; }
555
+ }
556
+
557
+ /* The stated figures. They are printed on the cell rather than implied by its
558
+ size, so the sizing is readable rather than felt — a reader can check that
559
+ digiquant's box is bigger because its figure is bigger. Right-aligned inside
560
+ `.dg-stats`, because that is the column the reader compares down. */
561
+ .dg-loc {
562
+ font-family: var(--font-mono);
563
+ font-size: 0.82rem;
564
+ letter-spacing: 0.06em;
565
+ color: var(--ink-mute);
566
+ font-variant-numeric: tabular-nums;
567
+ white-space: nowrap;
568
+ }
569
+ .dg-cell.on .dg-loc { color: var(--ink-soft); }
570
+
571
+ /* The focus morph is the whole point of the mosaic, so the one place it
572
+ must not run is where motion is unwanted: focus then lands on the final
573
+ layout with no travel. The mosaic-face selector keeps the higher
574
+ specificity of the transition it cancels. */
575
+ @media (prefers-reduced-motion: reduce) {
576
+ .dg-mosaic-row,
577
+ .dg-cell,
578
+ .dg-mosaic--rows .dg-cell,
579
+ .dg-mosaic--rows .dg-cell:not(.on) .dg-mosaic-enter,
580
+ .dg-mosaic--rows .dg-cell.on .dg-mosaic-enter {
581
+ transition: none;
582
+ }
583
+ }
584
+
585
+
586
+
587
+ /* Terminal registers (v15, #4429): the boot playback is a fixed box on the
588
+ landing page, so it must not grow as lines type in. `fill` stretches .term
589
+ to the wrapper's height; `compact` drops the body type a step. Both are
590
+ additive modifiers over the shared .term base in site.css. */
591
+ .term--fill { display: flex; flex-direction: column; height: 100%; }
592
+ .term--fill .term-body { flex: 1 1 auto; min-height: 0; overflow: hidden; }
593
+ .term--compact .term-bar { padding: 0.55rem 0.9rem; }
594
+ .term--compact .term-body { font-size: 0.72rem; line-height: 1.72; padding: 0.9rem 1rem 1rem; min-height: 0; }
595
+ .term--compact { box-shadow: 0 28px 64px -40px rgba(0, 0, 0, 0.5); }
596
+
597
+ /* mobile / reduced-motion stepper */
598
+ .dg-stepper { display: flex; flex-direction: column; gap: var(--space-5); max-width: var(--wrap); margin-inline: auto; padding-inline: var(--gutter); }
599
+ .dg-step { display: grid; gap: var(--space-3); }
600
+ .dg-graph-mini { max-width: 22.5rem; margin-inline: auto; }
601
+ @media (min-width: 720px) { .dg-step { grid-template-columns: 1fr 1.2fr; align-items: center; } }
602
+ @media (max-width: 900px) { .dg-stage-grid { grid-template-columns: 1fr; } .dg-rail { display: none; } }
603
+
604
+ /* ── disclosure rows (native <details>/<summary>) ───────────────────────────
605
+ A summary that opens something must read as clickable, and a bare <summary>
606
+ computes `cursor: auto` — so the affordance is owned here rather than
607
+ hand-rolled as an app-local `cursor-pointer` on every FAQ (canon: pointer
608
+ cursors are kit-level; apps/ utilities are refused). `list-style: none` plus
609
+ the webkit marker rule drop the default triangle so the caller can draw its
610
+ own indicator. ─────────────────────────────────────────────────────────── */
611
+ .disclosure-row > summary { cursor: pointer; list-style: none; }
612
+ .disclosure-row > summary::-webkit-details-marker { display: none; }
613
+
614
+ /* ── ScrollyFeatures (useScrollyFeatures reference skin) ────────────────────
615
+ Pinned section + progress rail (Graphite pattern). The hook owns state; this
616
+ is the reference CSS. Track height comes from scrollyTrackHeightVh() inline.
617
+ Small-viewport/reduced-motion consumers render .scrolly-stepper instead (all
618
+ slides in flow — the hook signals this via `stepper`). ────────────────── */
619
+ .scrolly-track { position: relative; }
620
+ .scrolly-pin { position: sticky; top: 0; min-height: 100svh; display: flex; flex-direction: column; justify-content: center; }
621
+ .scrolly-rail { --rail-dot: 0.375rem; --rail-dot-gap: var(--space-2); position: absolute; inset-inline-end: clamp(1rem, 3vw, 2rem); top: 50%; transform: translateY(-50%); display: flex; flex-direction: column; gap: var(--rail-dot-gap); }
622
+ .scrolly-tick { width: var(--rail-dot); height: var(--rail-dot); border-radius: 50%; background: var(--hair-2); transition: background .2s var(--ease), transform .2s var(--ease); }
623
+ .scrolly-tick.on { background: var(--accent); transform: scale(1.5); }
624
+ .scrolly-stepper { display: flex; flex-direction: column; gap: var(--space-5); max-width: var(--wrap); margin-inline: auto; padding-inline: var(--gutter); }
625
+
626
+ @media (prefers-reduced-motion: reduce) {
627
+ /* Belt-and-suspenders: even if a consumer keeps the pinned track mounted,
628
+ drop the sticky pin so slides read as a static stack. The hook's `stepper`
629
+ flag is the primary path (renders .scrolly-stepper); this is the fallback. */
630
+ .scrolly-pin { position: static; min-height: 0; }
631
+ .scrolly-tick { transition: none; }
632
+ }
633
+
634
+ /* ── DatePager calendar (kit, #4306 batch K2) ──────────────────────────────
635
+ Ported verbatim from styles/controls-overlay.css (`.nb-cal*`) so the kit
636
+ <DatePager/> no longer depends on the controls sheet. The enter/exit travel
637
+ is the kit's own `kit-pop` (same 0.15s fade + 0.5rem side slide as the
638
+ controls `.ctl-pop`), declared here rather than in a family sheet. */
639
+ .kit-pop {
640
+ --kit-pop-dur: 0.15s;
641
+ --kit-slide-x: 0rem;
642
+ --kit-slide-y: 0rem;
643
+ }
644
+ .kit-pop[data-side="bottom"] { --kit-slide-y: -0.5rem; }
645
+ .kit-pop[data-side="top"] { --kit-slide-y: 0.5rem; }
646
+ .kit-pop[data-side="left"],
647
+ .kit-pop[data-side="inline-start"] { --kit-slide-x: 0.5rem; }
648
+ .kit-pop[data-side="right"],
649
+ .kit-pop[data-side="inline-end"] { --kit-slide-x: -0.5rem; }
650
+ .kit-pop[data-open] { animation: kit-pop-in var(--kit-pop-dur) ease; }
651
+ .kit-pop[data-closed] { animation: kit-pop-out var(--kit-pop-dur) ease forwards; }
652
+
653
+ @keyframes kit-pop-in {
654
+ from { opacity: 0; transform: translate(var(--kit-slide-x), var(--kit-slide-y)) scale(0.95); }
655
+ }
656
+ @keyframes kit-pop-out {
657
+ to { opacity: 0; transform: translate(var(--kit-slide-x), var(--kit-slide-y)) scale(0.95); }
658
+ }
659
+
660
+ @layer components {
661
+ .nb-cal {
662
+ width: 17.5rem;
663
+ padding: 0.75rem;
664
+ border: 1px solid var(--hair);
665
+ border-radius: 0;
666
+ background: color-mix(in oklab, var(--term-bg) 92%, var(--surface));
667
+ box-shadow: 0 18px 40px -18px rgba(0, 0, 0, 0.55);
668
+ font-family: var(--font-mono);
669
+ color: var(--ink);
670
+ }
671
+
672
+ .nb-cal-head {
673
+ display: grid;
674
+ grid-template-columns: 1.75rem 1fr 1.75rem;
675
+ align-items: center;
676
+ gap: 0.35rem;
677
+ margin-bottom: 0.65rem;
678
+ }
679
+
680
+ .nb-cal-title {
681
+ text-align: center;
682
+ font-size: 0.75rem;
683
+ font-weight: 500;
684
+ color: var(--ink);
685
+ letter-spacing: 0.01em;
686
+ }
687
+
688
+ .nb-cal-nav {
689
+ display: inline-flex;
690
+ align-items: center;
691
+ justify-content: center;
692
+ width: 1.75rem;
693
+ height: 1.75rem;
694
+ margin: 0;
695
+ padding: 0;
696
+ border: none;
697
+ border-radius: 0;
698
+ background: transparent;
699
+ color: var(--ink-mute);
700
+ cursor: pointer;
701
+ transition:
702
+ color 0.15s cubic-bezier(0.4, 0, 0.2, 1),
703
+ background-color 0.15s cubic-bezier(0.4, 0, 0.2, 1);
704
+ }
705
+
706
+ .nb-cal-nav:hover:not(:disabled) {
707
+ color: var(--ink);
708
+ background: color-mix(in oklab, var(--ink) 6%, transparent);
709
+ }
710
+
711
+ .nb-cal-nav:disabled {
712
+ opacity: 0.3;
713
+ cursor: not-allowed;
714
+ }
715
+
716
+ .nb-cal-weekdays,
717
+ .nb-cal-row {
718
+ display: grid;
719
+ grid-template-columns: repeat(7, 1fr);
720
+ gap: 0.15rem;
721
+ }
722
+
723
+ .nb-cal-grid {
724
+ display: flex;
725
+ flex-direction: column;
726
+ gap: 0.15rem;
727
+ }
728
+
729
+ .nb-cal-weekday {
730
+ display: flex;
731
+ align-items: center;
732
+ justify-content: center;
733
+ height: 1.35rem;
734
+ font-size: 0.62rem;
735
+ color: var(--ink-mute);
736
+ text-transform: uppercase;
737
+ letter-spacing: 0.04em;
738
+ }
739
+
740
+ .nb-cal-day {
741
+ display: inline-flex;
742
+ align-items: center;
743
+ justify-content: center;
744
+ width: 100%;
745
+ aspect-ratio: 1;
746
+ margin: 0;
747
+ padding: 0;
748
+ border: 1px solid transparent;
749
+ border-radius: 0;
750
+ background: transparent;
751
+ color: var(--ink-soft);
752
+ font: inherit;
753
+ font-size: 0.72rem;
754
+ font-variant-numeric: tabular-nums;
755
+ cursor: pointer;
756
+ transition:
757
+ color 0.15s cubic-bezier(0.4, 0, 0.2, 1),
758
+ background-color 0.15s cubic-bezier(0.4, 0, 0.2, 1),
759
+ border-color 0.15s cubic-bezier(0.4, 0, 0.2, 1);
760
+ }
761
+
762
+ .nb-cal-day:hover:not(:disabled):not(.nb-cal-day--selected) {
763
+ color: var(--ink);
764
+ background: color-mix(in oklab, var(--ink) 6%, transparent);
765
+ }
766
+
767
+ .nb-cal-day--selected {
768
+ color: var(--accent);
769
+ background: color-mix(in oklab, var(--accent) 20%, transparent);
770
+ border-color: color-mix(in oklab, var(--accent) 35%, transparent);
771
+ }
772
+
773
+ .nb-cal-day--muted,
774
+ .nb-cal-day:disabled {
775
+ color: var(--ink-mute);
776
+ opacity: 0.35;
777
+ cursor: not-allowed;
778
+ }
779
+
780
+ .nb-cal-day--empty {
781
+ pointer-events: none;
782
+ visibility: hidden;
783
+ }
784
+ }
785
+
786
+ @media (prefers-reduced-motion: reduce) {
787
+ .kit-pop[data-open],
788
+ .kit-pop[data-closed] {
789
+ animation: none;
790
+ }
791
+ }
792
+
793
+ /* ==========================================================================
794
+ PAGE GEOMETRY + ENTRANCE CHOREOGRAPHY (D1, #4429 — ported techniques)
795
+ --------------------------------------------------------------------------
796
+ Two things this kit was missing, both read out of launch-ui's source (MIT)
797
+ rather than invented:
798
+
799
+ 1. `line-*` / `fade-*` / `glass-*` — a named utility vocabulary so sections
800
+ COMPOSE instead of hand-rolling a border or a mask each time. That
801
+ hand-rolling is exactly why the first pass of pages read as ad hoc.
802
+
803
+ 2. `animate-appear` — the staged entrance. Every hero element carries it
804
+ with a `delay-*` class, starting from `opacity-0`; `forwards` means the
805
+ resting state after the run is the visible one, so nothing flashes.
806
+
807
+ Both are token-backed. NOTE for the canon guard: `line-*` deliberately
808
+ duplicates @digithings-ai/design/site/site.css's legacy `.line-*` names — that
809
+ sheet is imported by apps that do NOT import this bridge, so neither can
810
+ own it exclusively. Declared here as `@utility` so Tailwind generates them
811
+ alongside its own.
812
+ ========================================================================== */
813
+
814
+ /* ---- Layout rules: the persistent page geometry -------------------------
815
+ A section that runs the full container height between two vertical hairlines
816
+ reads as one built object rather than boxes in a column. `line-y` draws only
817
+ the inline edges (0 top/bottom), `line-x` only the block edges.
818
+
819
+ These two are the *rails*, and they draw at `--hair-2` rather than `--hair`.
820
+ At `--hair` (9% white) the rail was invisible against the page ground, which
821
+ defeats the technique: a guide nobody can see is not geometry. One step up
822
+ (15%) registers at a glance while staying below any content edge, so the rail
823
+ reads as structure rather than as a border.
824
+
825
+ `line-t` / `line-b` deliberately stay at `--hair`. They are section
826
+ *dividers* — a different role, and there are dozens of them; raising those
827
+ would change the rhythm of every page rather than the frame around it. The
828
+ weight classes are: content edge > rail > divider > ground. */
829
+ /* Plain CSS, not @apply and not a var()-based shorthand: Tailwind's utility
830
+ generator resolves these at build time and a `var(--line-width, 1px)` inside
831
+ a shorthand did not survive (the border rendered 0px). A literal `1px` is
832
+ correct here — the hairline width is a canon constant, not a themed value. */
833
+ @utility line-y {
834
+ border-left-width: 1px;
835
+ border-right-width: 1px;
836
+ border-style: solid;
837
+ border-color: var(--hair-2);
838
+ }
839
+
840
+ @utility line-x {
841
+ border-top-width: 1px;
842
+ border-bottom-width: 1px;
843
+ border-style: solid;
844
+ border-color: var(--hair-2);
845
+ }
846
+
847
+ @utility line-t {
848
+ border-top-width: 1px;
849
+ border-style: solid;
850
+ border-color: var(--hair);
851
+ }
852
+
853
+ @utility line-b {
854
+ border-bottom-width: 1px;
855
+ border-style: solid;
856
+ border-color: var(--hair);
857
+ }
858
+
859
+ /* Must come AFTER the line-* rules in source order for equal specificity to
860
+ win, which is why it is declared here rather than with the others. */
861
+ @utility line-dashed {
862
+ border-style: dashed;
863
+ }
864
+
865
+ /* ---- Fade masks: bleed an artefact into the page instead of cutting it ---
866
+ (`fade-bottom` on a hero section is what stops a product shot ending on a
867
+ hard edge.) */
868
+ @utility fade-x {
869
+ mask-image: linear-gradient(to right, transparent 0%, black 25%, black 75%, transparent 100%);
870
+ }
871
+
872
+ @utility fade-y {
873
+ mask-image: linear-gradient(to top, transparent 0%, black 25%, black 75%, transparent 100%);
874
+ }
875
+
876
+ @utility fade-top {
877
+ mask-image: linear-gradient(to bottom, transparent 0%, black 35%);
878
+ }
879
+
880
+ @utility fade-bottom {
881
+ mask-image: linear-gradient(to top, transparent 0%, black 35%);
882
+ }
883
+
884
+ @utility fade-top-lg {
885
+ mask-image: linear-gradient(to bottom, transparent 15%, black 100%);
886
+ }
887
+
888
+ @utility fade-bottom-lg {
889
+ mask-image: linear-gradient(to top, transparent 15%, black 100%);
890
+ }
891
+
892
+ @utility fade-left {
893
+ mask-image: linear-gradient(to right, transparent 0%, black 35%);
894
+ }
895
+
896
+ @utility fade-right {
897
+ mask-image: linear-gradient(to left, transparent 0%, black 35%);
898
+ }
899
+
900
+ /* ---- Glass: layered gradient surfaces over the flat ground -------------
901
+ Five depths. `hair` is this system's border token, so the edges stay in the
902
+ canon rather than reaching for a Tailwind palette. */
903
+ @utility glass-1 {
904
+ border: 1px solid var(--hair);
905
+ background-image: linear-gradient(to bottom, color-mix(in srgb, var(--surface) 80%, transparent), color-mix(in srgb, var(--surface) 40%, transparent));
906
+ }
907
+
908
+ @utility glass-2 {
909
+ border: 1px solid var(--hair);
910
+ background-image: linear-gradient(to bottom, var(--surface), color-mix(in srgb, var(--surface) 80%, transparent));
911
+ }
912
+
913
+ @utility glass-3 {
914
+ border: 1px solid var(--hair);
915
+ background-image: linear-gradient(to bottom, color-mix(in srgb, var(--surface) 30%, transparent), color-mix(in srgb, var(--surface) 20%, transparent));
916
+ }
917
+
918
+ @utility glass-4 {
919
+ border: 1px solid var(--hair);
920
+ background-image: linear-gradient(to bottom, color-mix(in srgb, var(--surface) 60%, transparent), color-mix(in srgb, var(--surface) 20%, transparent));
921
+ }
922
+
923
+ @utility glass-5 {
924
+ border: 1px solid var(--hair);
925
+ background-image: linear-gradient(to bottom, var(--surface), color-mix(in srgb, var(--surface) 20%, transparent));
926
+ }
927
+
928
+ /* ---- The entrance choreography: keyframes + tokens live in the one
929
+ @theme inline block above, per web-theme.test.ts. ---- */
930
+
931
+ /* Reduced motion: the choreography parks elements at their END state rather
932
+ than their start state, so a reduced-motion visitor sees the finished page
933
+ immediately instead of a page of invisible elements. */
934
+ @media (prefers-reduced-motion: reduce) {
935
+ .animate-appear,
936
+ .animate-appear-zoom {
937
+ animation: none;
938
+ opacity: 1;
939
+ transform: none;
940
+ filter: none;
941
+ }
942
+ }
943
+
944
+ /* ── Squared scrollbars: one bar for the whole product surface ─────────────
945
+ Every box in this system is square, so the scrollbar must be too. macOS
946
+ draws a rounded overlay pill by default, and on a page of hairline edges it
947
+ is the only rounded shape left.
948
+
949
+ Two renderers, two mechanisms, and they must not meet:
950
+
951
+ · Blink/WebKit expose a bar only through ::-webkit-scrollbar-*. A styled
952
+ pseudo is a classic bar — it reserves its own track and never becomes an
953
+ overlay — and `border-radius: 0` on the thumb is what squares it. This is
954
+ the same treatment as digichat's `data-scrollbars="always"` skin
955
+ (chat-digichat.css), so the chat surface and the site read identically.
956
+ · Firefox has no pseudo-elements, so it takes the standard
957
+ `scrollbar-width` / `scrollbar-color` pair — guarded to Gecko. That guard
958
+ is load-bearing, not tidiness: declaring either property in Blink switches
959
+ the element to the native bar renderer and silently disables the
960
+ ::-webkit-scrollbar rules above, so an unguarded `*` rule would un-square
961
+ every Chromium surface while still looking right in the source.
962
+
963
+ The fill stays under :where(*) and therefore carries zero specificity. A
964
+ surface that hides its bar — `.dg-cell`, `.cr-track`, `.h-scroll__track`,
965
+ Tailwind's `[&::-webkit-scrollbar]:hidden` — keeps winning with its own
966
+ class-level `display: none` / `scrollbar-width: none`, and a surface that
967
+ paints its own thumb (digichat's skin, digiquant-web's `McpCli`) keeps its
968
+ colour. Do not raise these selectors to "fix" a local case. */
969
+ :where(*)::-webkit-scrollbar {
970
+ width: 0.625rem;
971
+ height: 0.625rem;
972
+ }
973
+
974
+ /* Transparent, so the bar lies on whatever surface it crosses — page, panel,
975
+ popover, terminal — instead of painting a stripe across it. */
976
+ :where(*)::-webkit-scrollbar-track,
977
+ :where(*)::-webkit-scrollbar-corner {
978
+ background: transparent;
979
+ }
980
+
981
+ /* Shape is the one part that is not negotiable, and it is the one part an
982
+ app-local stylesheet is most likely to have already answered: a bare
983
+ `::-webkit-scrollbar-thumb { border-radius: 3px }` (apps/dashboard) ties
984
+ :where(*) on specificity and wins on order, because every app imports this
985
+ file before its own rules. The element-level `html` prefix lifts this rule
986
+ to (0,0,2) — above a bare pseudo (0,0,1) and still far below any
987
+ class-level `display: none` (0,1,x) — so the square holds everywhere. */
988
+ html::-webkit-scrollbar-thumb,
989
+ html :where(*)::-webkit-scrollbar-thumb {
990
+ border-radius: 0;
991
+ }
992
+
993
+ :where(*)::-webkit-scrollbar-thumb {
994
+ background: color-mix(in oklab, var(--ink-mute) 32%, transparent);
995
+ }
996
+
997
+ :where(*)::-webkit-scrollbar-thumb:hover,
998
+ :where(*)::-webkit-scrollbar-thumb:active {
999
+ background: color-mix(in oklab, var(--ink-mute) 55%, transparent);
1000
+ }
1001
+
1002
+ @supports (-moz-appearance: none) {
1003
+ :where(*) {
1004
+ scrollbar-width: thin;
1005
+ scrollbar-color: color-mix(in oklab, var(--ink-mute) 38%, transparent) transparent;
1006
+ }
1007
+ }