@hraness/design-kit 0.31.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (345) hide show
  1. package/ARTICLE_COPY.md +208 -0
  2. package/HERO_FIELDS.md +55 -0
  3. package/ICONS.md +56 -0
  4. package/LANTERN_MATERIAL.md +60 -0
  5. package/LICENSE +21 -0
  6. package/MARKETING_COPY.md +49 -0
  7. package/MARKETING_PRESET.md +108 -0
  8. package/PALETTES.md +90 -0
  9. package/PAPER_THEME.md +128 -0
  10. package/README.md +745 -0
  11. package/STATUS_PAGES.md +67 -0
  12. package/STYLE.md +245 -0
  13. package/WRITING.md +72 -0
  14. package/dist/browser/index.js +2345 -0
  15. package/dist/chunk-44djxk16.js +6872 -0
  16. package/dist/chunk-5gtx3pza.js +9 -0
  17. package/dist/chunk-77391vmq.js +454 -0
  18. package/dist/chunk-7b1586eb.js +1209 -0
  19. package/dist/chunk-8834fh4n.js +6 -0
  20. package/dist/chunk-9t8xyqte.js +870 -0
  21. package/dist/chunk-cejpzyfh.js +446 -0
  22. package/dist/chunk-eh71jz57.js +429 -0
  23. package/dist/chunk-evb02bc1.js +516 -0
  24. package/dist/chunk-h4k7yv6x.js +368 -0
  25. package/dist/chunk-he8eznb1.js +760 -0
  26. package/dist/chunk-mr1vdcjq.js +404 -0
  27. package/dist/chunk-wzvdn8ey.js +116 -0
  28. package/dist/fonts/nebula-sans/social-fonts.generated.js +53 -0
  29. package/dist/icons.js +48 -0
  30. package/dist/index.js +237 -0
  31. package/dist/launch.js +41 -0
  32. package/dist/mockups/client.js +422 -0
  33. package/dist/mockups/index.js +1850 -0
  34. package/dist/portfolio.js +1549 -0
  35. package/dist/provider-marks.js +19 -0
  36. package/dist/react/charts.js +11 -0
  37. package/dist/react/hero-backdrop.js +8 -0
  38. package/dist/react/index.js +7252 -0
  39. package/dist/react/platform-install.js +10 -0
  40. package/dist/react/server.js +137 -0
  41. package/dist/stylex-manifest.json +1 -0
  42. package/dist/stylex.css +5392 -0
  43. package/dist/syntax-highlighting.js +15 -0
  44. package/dist/testing.js +250 -0
  45. package/package.json +377 -0
  46. package/portfolio-inventory.json +31 -0
  47. package/scripts/check-lantern-material-snapshot.d.mts +26 -0
  48. package/scripts/check-lantern-material-snapshot.mjs +126 -0
  49. package/scripts/check-marketing-snapshot.d.mts +12 -0
  50. package/scripts/check-marketing-snapshot.mjs +58 -0
  51. package/scripts/lantern-material-snapshot.ts +101 -0
  52. package/scripts/marketing-textures.ts +48 -0
  53. package/scripts/paper-theme-snapshot.ts +97 -0
  54. package/scripts/product-marketing-snapshot.ts +149 -0
  55. package/src/appearance-menu.css +266 -0
  56. package/src/appearance.ts +32 -0
  57. package/src/article-html.ts +269 -0
  58. package/src/article.ts +686 -0
  59. package/src/browser/appearance-menu.ts +604 -0
  60. package/src/browser/artifact-share.ts +164 -0
  61. package/src/browser/design-palette.ts +197 -0
  62. package/src/browser/foil.ts +124 -0
  63. package/src/browser/hero-light.ts +11 -0
  64. package/src/browser/index.ts +29 -0
  65. package/src/browser/status-field.ts +432 -0
  66. package/src/browser/status-page.ts +100 -0
  67. package/src/browser/sticky-offset.ts +82 -0
  68. package/src/browser/theme-color-sync.ts +213 -0
  69. package/src/charts.css +7 -0
  70. package/src/compiler-components.css +34 -0
  71. package/src/compiler-foundation.css +18 -0
  72. package/src/compiler-palettes.css +7 -0
  73. package/src/compiler-tokens.css +342 -0
  74. package/src/components.css +3 -0
  75. package/src/design-gallery.css +431 -0
  76. package/src/effects.css +14 -0
  77. package/src/fonts/geist-mono/GeistMono[wght].woff2 +0 -0
  78. package/src/fonts/geist-mono/OFL.txt +93 -0
  79. package/src/fonts/geist-mono/PROVENANCE.md +12 -0
  80. package/src/fonts/instrument-serif/OFL.txt +93 -0
  81. package/src/fonts/instrument-serif/UPSTREAM.md +14 -0
  82. package/src/fonts/instrument-serif/instrument-serif-latin-400.woff2 +0 -0
  83. package/src/fonts/nebula-sans/LICENSE.txt +96 -0
  84. package/src/fonts/nebula-sans/NebulaSans-Black.woff2 +0 -0
  85. package/src/fonts/nebula-sans/NebulaSans-BlackItalic.woff2 +0 -0
  86. package/src/fonts/nebula-sans/NebulaSans-Bold.otf +0 -0
  87. package/src/fonts/nebula-sans/NebulaSans-Bold.woff2 +0 -0
  88. package/src/fonts/nebula-sans/NebulaSans-BoldItalic.woff2 +0 -0
  89. package/src/fonts/nebula-sans/NebulaSans-Book.otf +0 -0
  90. package/src/fonts/nebula-sans/NebulaSans-Book.woff2 +0 -0
  91. package/src/fonts/nebula-sans/NebulaSans-BookItalic.woff2 +0 -0
  92. package/src/fonts/nebula-sans/NebulaSans-Light.woff2 +0 -0
  93. package/src/fonts/nebula-sans/NebulaSans-LightItalic.woff2 +0 -0
  94. package/src/fonts/nebula-sans/NebulaSans-Medium.woff2 +0 -0
  95. package/src/fonts/nebula-sans/NebulaSans-MediumItalic.woff2 +0 -0
  96. package/src/fonts/nebula-sans/NebulaSans-Semibold.woff2 +0 -0
  97. package/src/fonts/nebula-sans/NebulaSans-SemiboldItalic.woff2 +0 -0
  98. package/src/fonts/nebula-sans/PROVENANCE.md +14 -0
  99. package/src/fonts/nebula-sans/social-fonts.generated.ts +3873 -0
  100. package/src/fonts.css +106 -0
  101. package/src/icons/act60/act60.svg +5 -0
  102. package/src/icons/act60/application.svg +9 -0
  103. package/src/icons/act60/chapter-compare.svg +3 -0
  104. package/src/icons/act60/decree-residence.svg +6 -0
  105. package/src/icons/act60/estimate.svg +7 -0
  106. package/src/icons/act60/export-services.svg +4 -0
  107. package/src/icons/act60/investor.svg +13 -0
  108. package/src/icons/act60/presence.svg +7 -0
  109. package/src/icons/aicharts/aicharts.svg +6 -0
  110. package/src/icons/aicharts/cost-compare.svg +4 -0
  111. package/src/icons/aicharts/task-audio.svg +7 -0
  112. package/src/icons/aicharts/task-coding.svg +6 -0
  113. package/src/icons/aicharts/task-images.svg +3 -0
  114. package/src/icons/aicharts/task-reasoning.svg +9 -0
  115. package/src/icons/aicharts/task-research.svg +5 -0
  116. package/src/icons/aicharts/task-video.svg +4 -0
  117. package/src/icons/manifest.json +1371 -0
  118. package/src/icons/platonik/conversation.svg +3 -0
  119. package/src/icons/platonik/creature.svg +7 -0
  120. package/src/icons/platonik/economy.svg +4 -0
  121. package/src/icons/platonik/keep-light.svg +6 -0
  122. package/src/icons/platonik/platonik.svg +7 -0
  123. package/src/icons/platonik/spark-route.svg +15 -0
  124. package/src/icons/receipts/act60-retry.receipt.json +888 -0
  125. package/src/icons/receipts/act60.receipt.json +1366 -0
  126. package/src/icons/receipts/aicharts.receipt.json +1546 -0
  127. package/src/icons/receipts/platonik.receipt.json +1011 -0
  128. package/src/icons/receipts/roughday.receipt.json +770 -0
  129. package/src/icons/receipts/shared.receipt.json +1296 -0
  130. package/src/icons/receipts/slopcamera.receipt.json +1495 -0
  131. package/src/icons/receipts/soundfish-retry.receipt.json +460 -0
  132. package/src/icons/receipts/soundfish.receipt.json +1709 -0
  133. package/src/icons/receipts/sponge.receipt.json +1743 -0
  134. package/src/icons/receipts/stripe-history-retry.receipt.json +145 -0
  135. package/src/icons/receipts/stripe-history.receipt.json +1202 -0
  136. package/src/icons/receipts/wordcell.receipt.json +1924 -0
  137. package/src/icons/roughday/finance.svg +7 -0
  138. package/src/icons/roughday/roughday.svg +6 -0
  139. package/src/icons/roughday/technology.svg +4 -0
  140. package/src/icons/roughday/world.svg +4 -0
  141. package/src/icons/sets/act60.json +63 -0
  142. package/src/icons/sets/aicharts.json +63 -0
  143. package/src/icons/sets/platonik.json +39 -0
  144. package/src/icons/sets/roughday.json +25 -0
  145. package/src/icons/sets/shared.json +12 -0
  146. package/src/icons/sets/slopcamera.json +47 -0
  147. package/src/icons/sets/soundfish.json +43 -0
  148. package/src/icons/sets/sponge.json +51 -0
  149. package/src/icons/sets/stripe-history.json +39 -0
  150. package/src/icons/sets/wordcell.json +51 -0
  151. package/src/icons/shared/agent-skill.svg +4 -0
  152. package/src/icons/shared/cli.svg +5 -0
  153. package/src/icons/shared/privacy.svg +3 -0
  154. package/src/icons/shared/research.svg +7 -0
  155. package/src/icons/shared/sdk.svg +3 -0
  156. package/src/icons/slopcamera/diagram.svg +3 -0
  157. package/src/icons/slopcamera/direct-scene.svg +5 -0
  158. package/src/icons/slopcamera/edit-video.svg +17 -0
  159. package/src/icons/slopcamera/mcp.svg +3 -0
  160. package/src/icons/slopcamera/native-world.svg +3 -0
  161. package/src/icons/slopcamera/slopcamera.svg +11 -0
  162. package/src/icons/soundfish/agent-ui.svg +7 -0
  163. package/src/icons/soundfish/collaborate.svg +5 -0
  164. package/src/icons/soundfish/compose.svg +3 -0
  165. package/src/icons/soundfish/hear.svg +6 -0
  166. package/src/icons/soundfish/human-ui.svg +6 -0
  167. package/src/icons/soundfish/share.svg +3 -0
  168. package/src/icons/soundfish/soundfish.svg +6 -0
  169. package/src/icons/sponge/api.svg +3 -0
  170. package/src/icons/sponge/documents.svg +3 -0
  171. package/src/icons/sponge/library.svg +3 -0
  172. package/src/icons/sponge/one-question.svg +5 -0
  173. package/src/icons/sponge/people-direct.svg +5 -0
  174. package/src/icons/sponge/read.svg +3 -0
  175. package/src/icons/sponge/save.svg +4 -0
  176. package/src/icons/sponge/sponge.svg +7 -0
  177. package/src/icons/stripe-history/company-history.svg +3 -0
  178. package/src/icons/stripe-history/evidence.svg +4 -0
  179. package/src/icons/stripe-history/independence.svg +7 -0
  180. package/src/icons/stripe-history/publications.svg +11 -0
  181. package/src/icons/stripe-history/sources.svg +6 -0
  182. package/src/icons/stripe-history/stripe-history.svg +3 -0
  183. package/src/icons/wordcell/backlinks.svg +6 -0
  184. package/src/icons/wordcell/capture.svg +6 -0
  185. package/src/icons/wordcell/git-provenance.svg +4 -0
  186. package/src/icons/wordcell/kb.svg +4 -0
  187. package/src/icons/wordcell/markdown.svg +4 -0
  188. package/src/icons/wordcell/scopes.svg +3 -0
  189. package/src/icons/wordcell/search.svg +7 -0
  190. package/src/icons.generated.ts +734 -0
  191. package/src/icons.ts +51 -0
  192. package/src/index.ts +275 -0
  193. package/src/lantern-material.css +213 -0
  194. package/src/launch.ts +624 -0
  195. package/src/marketing-assets/UPSTREAM.md +18 -0
  196. package/src/marketing-assets/cells.svg +1 -0
  197. package/src/marketing-assets/grain.svg +1 -0
  198. package/src/marketing-forced-colors.css +12 -0
  199. package/src/mockups/client.tsx +415 -0
  200. package/src/mockups/core.tsx +318 -0
  201. package/src/mockups/frames.tsx +368 -0
  202. package/src/mockups/index.ts +14 -0
  203. package/src/mockups/surfaces.tsx +681 -0
  204. package/src/mockups.css +2607 -0
  205. package/src/palette-appearance.ts +45 -0
  206. package/src/palette-bridge.css +124 -0
  207. package/src/palette-color.ts +41 -0
  208. package/src/palette-system.css +721 -0
  209. package/src/palette-themes.ts +23 -0
  210. package/src/palette-tokens.stylex.ts +519 -0
  211. package/src/palettes.css +3 -0
  212. package/src/palettes.ts +161 -0
  213. package/src/paper-theme.css +244 -0
  214. package/src/plain-publication.css +1241 -0
  215. package/src/plain-site.css +397 -0
  216. package/src/platforms.ts +151 -0
  217. package/src/portfolio.generated.json +1595 -0
  218. package/src/portfolio.generated.ts +1596 -0
  219. package/src/portfolio.ts +226 -0
  220. package/src/product-marketing-foundation.css +433 -0
  221. package/src/product-marketing-preset.css +261 -0
  222. package/src/product-marketing.css +2186 -0
  223. package/src/provider-marks.generated.ts +112 -0
  224. package/src/provider-marks.ts +143 -0
  225. package/src/react/animated-rail-stage.stylex.ts +15 -0
  226. package/src/react/animated-rail-stage.tsx +51 -0
  227. package/src/react/app-shell.stylex.ts +104 -0
  228. package/src/react/app-shell.tsx +121 -0
  229. package/src/react/article.tsx +602 -0
  230. package/src/react/aurora-dots-background.tsx +34 -0
  231. package/src/react/charts.stylex.ts +563 -0
  232. package/src/react/charts.tsx +610 -0
  233. package/src/react/chat.stylex.ts +34 -0
  234. package/src/react/chat.tsx +155 -0
  235. package/src/react/design-gallery.tsx +943 -0
  236. package/src/react/design-palette.stylex.ts +124 -0
  237. package/src/react/design-palette.tsx +196 -0
  238. package/src/react/design-theme-context.tsx +41 -0
  239. package/src/react/effects.stylex.ts +325 -0
  240. package/src/react/fader.stylex.ts +71 -0
  241. package/src/react/fader.tsx +151 -0
  242. package/src/react/foil-card-math.ts +100 -0
  243. package/src/react/foil-card-surface.stylex.ts +326 -0
  244. package/src/react/foil-card-surface.tsx +742 -0
  245. package/src/react/foil-mark.tsx +35 -0
  246. package/src/react/foil.stylex.ts +163 -0
  247. package/src/react/haptics.ts +228 -0
  248. package/src/react/hero-backdrop.tsx +22 -0
  249. package/src/react/index.ts +38 -0
  250. package/src/react/keyboard-shortcuts.ts +228 -0
  251. package/src/react/lantern-material-gallery.tsx +166 -0
  252. package/src/react/lantern-material.stylex.ts +39 -0
  253. package/src/react/launch-beats.tsx +73 -0
  254. package/src/react/navigation-rail.stylex.ts +130 -0
  255. package/src/react/navigation-rail.tsx +228 -0
  256. package/src/react/particle-halo.tsx +115 -0
  257. package/src/react/phaser-dots.tsx +501 -0
  258. package/src/react/platform-icons.tsx +94 -0
  259. package/src/react/platform-install.stylex.ts +385 -0
  260. package/src/react/platform-install.tsx +389 -0
  261. package/src/react/playback-transport.stylex.ts +14 -0
  262. package/src/react/playback-transport.tsx +112 -0
  263. package/src/react/procedural-backdrop.tsx +192 -0
  264. package/src/react/procedural-recipe.ts +323 -0
  265. package/src/react/product-marketing.stylex.ts +4469 -0
  266. package/src/react/product-marketing.tsx +1611 -0
  267. package/src/react/production-data-preview-notice.stylex.ts +40 -0
  268. package/src/react/production-data-preview-notice.tsx +34 -0
  269. package/src/react/provider-mark.stylex.ts +132 -0
  270. package/src/react/provider-mark.tsx +120 -0
  271. package/src/react/relative-time.tsx +117 -0
  272. package/src/react/route-state.stylex.ts +39 -0
  273. package/src/react/route-state.tsx +350 -0
  274. package/src/react/server.ts +11 -0
  275. package/src/react/social-kit-panel.tsx +103 -0
  276. package/src/react/sticky-offset.tsx +33 -0
  277. package/src/react/surfaces.stylex.ts +179 -0
  278. package/src/react/surfaces.tsx +330 -0
  279. package/src/react/syntax-code.tsx +33 -0
  280. package/src/react/theme-color-sync.ts +1 -0
  281. package/src/react/theme-resolution.ts +11 -0
  282. package/src/react/theme.stylex.ts +276 -0
  283. package/src/react/theme.tsx +402 -0
  284. package/src/reading.css +72 -0
  285. package/src/relative-time.ts +185 -0
  286. package/src/reset.css +2 -0
  287. package/src/site-shell.css +16 -0
  288. package/src/status-page-html.ts +63 -0
  289. package/src/status-page.css +276 -0
  290. package/src/status-page.ts +323 -0
  291. package/src/styles.css +21 -0
  292. package/src/syntax-code-lexers.ts +303 -0
  293. package/src/syntax-highlighting.css +78 -0
  294. package/src/syntax-highlighting.ts +477 -0
  295. package/src/syntax-tokens.ts +33 -0
  296. package/src/testing.ts +307 -0
  297. package/src/tokens.css +3 -0
  298. package/src/typography.css +86 -0
  299. package/vendor/evilcharts/LICENSE +21 -0
  300. package/vendor/evilcharts/UPSTREAM.md +14 -0
  301. package/vendor/platform-marks/LICENSE +30 -0
  302. package/vendor/platform-marks/UPSTREAM.md +15 -0
  303. package/vendor/platform-marks/apple.svg +1 -0
  304. package/vendor/platform-marks/linux.svg +1 -0
  305. package/vendor/provider-marks/CRUSH-LICENSE.md +134 -0
  306. package/vendor/provider-marks/LICENSE +21 -0
  307. package/vendor/provider-marks/UPSTREAM.md +27 -0
  308. package/vendor/provider-marks/aider.svg +3 -0
  309. package/vendor/provider-marks/crush-heartbit.svg +8 -0
  310. package/vendor/provider-marks/lobehub/alibabacloud-color.svg +1 -0
  311. package/vendor/provider-marks/lobehub/alibabacloud.svg +1 -0
  312. package/vendor/provider-marks/lobehub/amp-color.svg +1 -0
  313. package/vendor/provider-marks/lobehub/amp.svg +1 -0
  314. package/vendor/provider-marks/lobehub/anthropic.svg +1 -0
  315. package/vendor/provider-marks/lobehub/claude-color.svg +1 -0
  316. package/vendor/provider-marks/lobehub/claude.svg +1 -0
  317. package/vendor/provider-marks/lobehub/claudecode-color.svg +1 -0
  318. package/vendor/provider-marks/lobehub/claudecode.svg +1 -0
  319. package/vendor/provider-marks/lobehub/codex-color.svg +1 -0
  320. package/vendor/provider-marks/lobehub/codex.svg +1 -0
  321. package/vendor/provider-marks/lobehub/cursor.svg +1 -0
  322. package/vendor/provider-marks/lobehub/deepseek-color.svg +1 -0
  323. package/vendor/provider-marks/lobehub/deepseek.svg +1 -0
  324. package/vendor/provider-marks/lobehub/devin-color.svg +1 -0
  325. package/vendor/provider-marks/lobehub/devin.svg +1 -0
  326. package/vendor/provider-marks/lobehub/gemini-color.svg +1 -0
  327. package/vendor/provider-marks/lobehub/gemini.svg +1 -0
  328. package/vendor/provider-marks/lobehub/geminicli-color.svg +1 -0
  329. package/vendor/provider-marks/lobehub/geminicli.svg +1 -0
  330. package/vendor/provider-marks/lobehub/goose.svg +1 -0
  331. package/vendor/provider-marks/lobehub/meta-color.svg +1 -0
  332. package/vendor/provider-marks/lobehub/meta.svg +1 -0
  333. package/vendor/provider-marks/lobehub/mistral-color.svg +1 -0
  334. package/vendor/provider-marks/lobehub/mistral.svg +1 -0
  335. package/vendor/provider-marks/lobehub/moonshot.svg +1 -0
  336. package/vendor/provider-marks/lobehub/nvidia-color.svg +1 -0
  337. package/vendor/provider-marks/lobehub/nvidia.svg +1 -0
  338. package/vendor/provider-marks/lobehub/openai.svg +1 -0
  339. package/vendor/provider-marks/lobehub/opencode.svg +1 -0
  340. package/vendor/provider-marks/lobehub/perplexity-color.svg +1 -0
  341. package/vendor/provider-marks/lobehub/perplexity.svg +1 -0
  342. package/vendor/provider-marks/lobehub/qwen-color.svg +1 -0
  343. package/vendor/provider-marks/lobehub/qwen.svg +1 -0
  344. package/vendor/provider-marks/lobehub/xai.svg +1 -0
  345. package/vendor/provider-marks/lobehub/zai.svg +1 -0
@@ -0,0 +1,226 @@
1
+ /**
2
+ * Public Hraness portfolio facts: product names, one-liners, canonical URLs,
3
+ * status, and the registered relations between products.
4
+ *
5
+ * The data is a versioned snapshot generated by `scripts/sync-portfolio-facts.ts`
6
+ * from one exact commit of the Hraness portfolio registry, recorded in
7
+ * `portfolioProvenance`. This
8
+ * module holds data and pure helpers only. It renders nothing and imports no
9
+ * other part of the package.
10
+ */
11
+ import { portfolioSnapshot } from "./portfolio.generated.js";
12
+
13
+ export const portfolioRelationKinds = ["runtime", "development", "contract", "delivery"] as const;
14
+ export type PortfolioRelationKind = (typeof portfolioRelationKinds)[number];
15
+
16
+ /** `forward` reads source to target; `shared` is a symmetric contract both sides implement. */
17
+ export const portfolioRelationDirections = ["forward", "shared"] as const;
18
+ export type PortfolioRelationDirection = (typeof portfolioRelationDirections)[number];
19
+
20
+ /** Every product in the public portfolio contract is an active product. */
21
+ export const portfolioProductStatuses = ["active"] as const;
22
+ export type PortfolioProductStatus = (typeof portfolioProductStatuses)[number];
23
+
24
+ /** Whether the registry's structured marketing copy is reviewed (`authored`) or a draft (`proposed`). */
25
+ export const portfolioCopyStatuses = ["authored", "proposed"] as const;
26
+ export type PortfolioCopyStatus = (typeof portfolioCopyStatuses)[number];
27
+
28
+ export type PortfolioProductId = keyof typeof portfolioSnapshot.products;
29
+
30
+ /** The canonical messaging record for one product, verbatim from the public portfolio contract. */
31
+ export type PortfolioMessaging = Readonly<{
32
+ formatVersion: 1;
33
+ /** The canonical product slug, which can differ from the registry id. */
34
+ product: string;
35
+ names: Readonly<{
36
+ /** The prose name, used inside sentences. */
37
+ name: string;
38
+ /** The all-caps catalog name hraness.com cards display. */
39
+ catalog?: string;
40
+ command?: string;
41
+ /** Retired names that still route to the product. */
42
+ formerly?: readonly string[];
43
+ }>;
44
+ /** The product class, at most four words. */
45
+ category: string;
46
+ tagline: string;
47
+ /** The card line; sentence case without the product name or a final period. */
48
+ short: string;
49
+ /** The 110-to-160-character description for meta tags, GitHub About, and listings. */
50
+ meta: string;
51
+ /** Two-sentence copy that opens with the product name. */
52
+ medium: string;
53
+ long?: string;
54
+ headlines?: readonly string[];
55
+ hero?: Readonly<{
56
+ heading?: string;
57
+ summary?: string;
58
+ primaryAction?: string;
59
+ secondaryAction?: string;
60
+ }>;
61
+ channels?: Readonly<Record<string, unknown>>;
62
+ /** Per-tier authorship; tiers not listed take `default`. */
63
+ status: Readonly<{ default: PortfolioCopyStatus } & Partial<Record<string, PortfolioCopyStatus>>>;
64
+ reviewedOn?: string;
65
+ }>;
66
+
67
+ export type PortfolioProduct = Readonly<{
68
+ id: PortfolioProductId;
69
+ /** The name in its canonical casing, taken from the product's brand entry when it owns its host. */
70
+ name: string;
71
+ /** The product's `short` line in canonical casing. */
72
+ oneLiner: string;
73
+ /** The brand catalog's description, or null when it has none. It can differ from `oneLiner`. */
74
+ brandDescription: string | null;
75
+ canonicalUrl: string;
76
+ status: PortfolioProductStatus;
77
+ /** Whether the registry's default copy tier is reviewed (`authored`) or a draft (`proposed`). */
78
+ copyStatus: PortfolioCopyStatus;
79
+ /** Other names the registry uses for the product, such as an expanded, display, or retired name. */
80
+ aliases: readonly string[];
81
+ /** The product's portfolio mark as an inert `data:image/svg+xml` URL, the artwork hraness.com project cards show. */
82
+ mark: string;
83
+ /** The canonical tiered copy record: names, category, tagline, short, meta, medium, long, hero, and channels. */
84
+ messaging: PortfolioMessaging;
85
+ }>;
86
+
87
+ export type PortfolioRelation = Readonly<{
88
+ id: string;
89
+ source: PortfolioProductId;
90
+ target: PortfolioProductId;
91
+ kind: PortfolioRelationKind;
92
+ direction: PortfolioRelationDirection;
93
+ /** A short registry label such as "compacts sessions for". It is not reader-facing copy. */
94
+ label: string;
95
+ /** One reviewed sentence describing the integration, or null when none is registered. */
96
+ detail: string | null;
97
+ }>;
98
+
99
+ export type PortfolioProvenance = Readonly<{
100
+ /** The public home of the upstream facts, https://hraness.com/portfolio.json. */
101
+ registry: string;
102
+ /** The full 40-character registry commit the snapshot was generated from. */
103
+ commit: string;
104
+ /** The commit's date, YYYY-MM-DD. */
105
+ committedOn: string;
106
+ upstreamContract: string;
107
+ upstreamDigest: string;
108
+ files: readonly Readonly<{ path: string; sha256: string }>[];
109
+ }>;
110
+
111
+ export type PortfolioSnapshot = Readonly<{
112
+ contract: "hraness.design-kit-portfolio/v1";
113
+ formatVersion: 1;
114
+ provenance: PortfolioProvenance;
115
+ products: Readonly<Record<PortfolioProductId, PortfolioProduct>>;
116
+ relations: readonly PortfolioRelation[];
117
+ /** SHA-256 of the snapshot's compact JSON without this key. Pin it in a consumer test. */
118
+ digest: string;
119
+ }>;
120
+
121
+ /** A related-product card, shaped for `MarketingRelated` and `ArticleRelatedProducts` items. */
122
+ export type PortfolioRelatedItem = Readonly<{
123
+ href: string;
124
+ name: string;
125
+ /** The related product's one-liner, the line the card shows. */
126
+ role: string;
127
+ /** The related product's portfolio mark, a `data:image/svg+xml` URL. */
128
+ mark: string;
129
+ /** The relation's reviewed detail sentence. Cards no longer show it; articles can quote it. */
130
+ relationship: string;
131
+ productId: PortfolioProductId;
132
+ relationId: string;
133
+ }>;
134
+
135
+ /** A product pair joined by a relation with a reviewed detail, for "How X uses Y" posts. */
136
+ export type PortfolioUsesPair = Readonly<{
137
+ relation: PortfolioRelation & Readonly<{ detail: string }>;
138
+ source: PortfolioProduct;
139
+ target: PortfolioProduct;
140
+ }>;
141
+
142
+ export class PortfolioFactsError extends Error {
143
+ override readonly name = "PortfolioFactsError";
144
+ }
145
+
146
+ function deepFreeze<T>(value: T): T {
147
+ if (typeof value === "object" && value !== null && !Object.isFrozen(value)) {
148
+ for (const child of Object.values(value)) deepFreeze(child);
149
+ Object.freeze(value);
150
+ }
151
+ return value;
152
+ }
153
+
154
+ /** The whole snapshot, frozen so one consumer cannot change another's facts at runtime. */
155
+ export const portfolioFacts: PortfolioSnapshot = deepFreeze(portfolioSnapshot);
156
+ export const portfolioProducts: PortfolioSnapshot["products"] = portfolioFacts.products;
157
+ export const portfolioRelations: PortfolioSnapshot["relations"] = portfolioFacts.relations;
158
+ export const portfolioProvenance: PortfolioProvenance = portfolioFacts.provenance;
159
+ export const portfolioDigest: string = portfolioFacts.digest;
160
+
161
+ /** Product ids in registry order. */
162
+ export const portfolioProductIds: readonly PortfolioProductId[] = Object.freeze(
163
+ Object.keys(portfolioProducts) as PortfolioProductId[],
164
+ );
165
+
166
+ export function isPortfolioProductId(value: unknown): value is PortfolioProductId {
167
+ return typeof value === "string" && Object.hasOwn(portfolioProducts, value);
168
+ }
169
+
170
+ /** Returns one product, or throws `PortfolioFactsError` for an id that is not in the snapshot. */
171
+ export function product(id: PortfolioProductId): PortfolioProduct {
172
+ if (!isPortfolioProductId(id)) {
173
+ throw new PortfolioFactsError(`Unknown portfolio product: ${String(id)}.`);
174
+ }
175
+ return portfolioProducts[id];
176
+ }
177
+
178
+ function hasDetail(relation: PortfolioRelation): relation is PortfolioRelation & Readonly<{ detail: string }> {
179
+ return relation.detail !== null;
180
+ }
181
+
182
+ /**
183
+ * Products related to `id`, one card per related product, in relation order.
184
+ * Only relations with a reviewed detail sentence qualify, because the card
185
+ * shows that sentence. Pass `kinds` to narrow the relation kinds.
186
+ */
187
+ export function relatedFor(
188
+ id: PortfolioProductId,
189
+ options: Readonly<{ kinds?: readonly PortfolioRelationKind[] }> = {},
190
+ ): readonly PortfolioRelatedItem[] {
191
+ product(id);
192
+ const kinds = options.kinds ?? portfolioRelationKinds;
193
+ const seen = new Set<PortfolioProductId>();
194
+ const items: PortfolioRelatedItem[] = [];
195
+ for (const relation of portfolioRelations) {
196
+ if (!hasDetail(relation) || !kinds.includes(relation.kind)) continue;
197
+ const otherId = relation.source === id ? relation.target : relation.target === id ? relation.source : null;
198
+ if (otherId === null || otherId === id || seen.has(otherId)) continue;
199
+ seen.add(otherId);
200
+ const other = product(otherId);
201
+ items.push({
202
+ href: other.canonicalUrl,
203
+ name: other.name,
204
+ role: other.oneLiner,
205
+ mark: other.mark,
206
+ relationship: relation.detail,
207
+ productId: other.id,
208
+ relationId: relation.id,
209
+ });
210
+ }
211
+ return items;
212
+ }
213
+
214
+ /**
215
+ * Integration pairs with a reviewed detail sentence: the candidates for
216
+ * "How X uses Y" posts and provider hub pages. Delivery relations (one product
217
+ * served under another's route) are excluded. The relation does not say which
218
+ * side is the consumer; read `detail`.
219
+ */
220
+ export function usesPairs(): readonly PortfolioUsesPair[] {
221
+ return portfolioRelations.filter(hasDetail).filter((relation) => relation.kind !== "delivery").map((relation) => ({
222
+ relation,
223
+ source: product(relation.source),
224
+ target: product(relation.target),
225
+ }));
226
+ }
@@ -0,0 +1,433 @@
1
+ @import "./site-shell.css";
2
+
3
+ /* Compiler-adopter marketing foundation: public token bindings, arbitrary
4
+ consumer-content grammar, header capability/media tokens and direct-child layout relationships.
5
+ Owned React slots are compiled from product-marketing.stylex.ts.
6
+ The standalone product-marketing.css remains the raw HTML compatibility API. */
7
+ @layer components.hraness-design-kit.legacy {
8
+ .hraness-marketing-install {
9
+ --hraness-marketing-install-inline-size: initial;
10
+ }
11
+
12
+ :where( .hraness-marketing-page, .hraness-marketing-header, .hraness-marketing-footer, .hraness-marketing-hero, .hraness-marketing-pillars, .hraness-marketing-install, .hraness-marketing-section, .hraness-marketing-primitives, .hraness-marketing-stats, .hraness-marketing-interfaces, .hraness-marketing-trust, .hraness-marketing-quotes, .hraness-marketing-pricing, .hraness-marketing-questions, .hraness-marketing-maker, .hraness-marketing-related, .hraness-marketing-cta, .hraness-marketing-proof-frame ) {
13
+ --hraness-marketing-ink: var(--foreground, CanvasText);
14
+ --hraness-marketing-muted: var(--muted, GrayText);
15
+ --hraness-marketing-background: var(--background, Canvas);
16
+ --hraness-marketing-surface: var(--surface, Canvas);
17
+ --hraness-marketing-line: color-mix(in srgb, var(--hraness-marketing-ink) 10%, transparent);
18
+ --hraness-marketing-line-strong: color-mix(in srgb, var(--hraness-marketing-ink) 22%, transparent);
19
+ --hraness-marketing-inverse: var(--inverse, CanvasText);
20
+ --hraness-marketing-inverse-ink: var(--inverse-foreground, Canvas);
21
+ --hraness-marketing-accent: var(--hraness-site-accent, var(--primary, oklch(0.55 0.21 262)));
22
+ --hraness-marketing-accent-ink: var(--hraness-site-accent-ink, var(--primary-foreground, white));
23
+ --hraness-marketing-accent-soft: color-mix(in srgb, var(--hraness-marketing-accent) 10%, transparent);
24
+ --hraness-marketing-text-font: var(--font-text, ui-sans-serif, system-ui, sans-serif);
25
+ --hraness-marketing-heading-font: var(--font-heading, var(--hraness-marketing-text-font));
26
+ --hraness-marketing-mono-font: var(--font-mono, ui-monospace, monospace);
27
+ --hraness-marketing-measure: 72rem;
28
+ --hraness-marketing-copy-measure: 54ch;
29
+ --hraness-marketing-prose-measure: 62ch;
30
+ --hraness-marketing-gutter: clamp(1.25rem, 4vw, 3rem);
31
+ --hraness-marketing-radius: 0.75rem;
32
+ --hraness-marketing-frame-radius: 1.125rem;
33
+ --hraness-marketing-rule: 1px solid var(--hraness-marketing-line);
34
+ --hraness-marketing-surface-rule: 1px solid transparent;
35
+ --hraness-marketing-chrome-rule: 1px solid var(--hraness-marketing-line);
36
+ --hraness-marketing-surface-shadow: 0 0 0 1px var(--hraness-marketing-line);
37
+ --hraness-marketing-chrome-shadow: none;
38
+ --hraness-marketing-shadow: 0 0 0 1px var(--hraness-marketing-line);
39
+ --hraness-marketing-section-space: clamp(4rem, 9vw, 7.5rem);
40
+ --hraness-marketing-heading-weight: 550;
41
+ --hraness-marketing-heading-tracking: -0.03em;
42
+ --hraness-marketing-action-height: 2.75rem;
43
+ --hraness-foil-1: light-dark(oklch(0.89 0.065 337), oklch(0.56 0.16 340));
44
+ --hraness-foil-2: light-dark(oklch(0.875 0.05 277), oklch(0.52 0.15 285));
45
+ --hraness-foil-3: light-dark(oklch(0.92 0.05 170), oklch(0.66 0.14 175));
46
+ --hraness-foil-4: light-dark(oklch(0.95 0.045 96), oklch(0.72 0.13 100));
47
+ --hraness-foil-5: light-dark(oklch(0.9 0.05 55), oklch(0.55 0.15 45));
48
+ --hraness-foil-6: light-dark(oklch(0.875 0.06 305), oklch(0.62 0.16 305));
49
+ --hraness-foil-edge: light-dark(black, white);
50
+ }
51
+
52
+ /* The shared foil spectrum reads the deeper palette under explicit or
53
+ * media-resolved dark roots; the standalone stylesheet documents the contract. */
54
+ :where([data-theme="dark"], .dark):where(
55
+ .hraness-marketing-page, .hraness-marketing-header, .hraness-marketing-hero,
56
+ .hraness-marketing-pillars, .hraness-marketing-install, .hraness-marketing-section,
57
+ .hraness-marketing-primitives, .hraness-marketing-stats, .hraness-marketing-interfaces,
58
+ .hraness-marketing-trust, .hraness-marketing-quotes, .hraness-marketing-pricing,
59
+ .hraness-marketing-questions, .hraness-marketing-maker, .hraness-marketing-related,
60
+ .hraness-marketing-cta,
61
+ .hraness-marketing-proof-frame
62
+ ),
63
+ :where([data-theme="dark"], .dark) :where(
64
+ .hraness-marketing-page, .hraness-marketing-header, .hraness-marketing-hero,
65
+ .hraness-marketing-pillars, .hraness-marketing-install, .hraness-marketing-section,
66
+ .hraness-marketing-primitives, .hraness-marketing-stats, .hraness-marketing-interfaces,
67
+ .hraness-marketing-trust, .hraness-marketing-quotes, .hraness-marketing-pricing,
68
+ .hraness-marketing-questions, .hraness-marketing-maker, .hraness-marketing-related,
69
+ .hraness-marketing-cta,
70
+ .hraness-marketing-proof-frame
71
+ ) {
72
+ --hraness-foil-1: oklch(0.56 0.16 340);
73
+ --hraness-foil-2: oklch(0.52 0.15 285);
74
+ --hraness-foil-3: oklch(0.66 0.14 175);
75
+ --hraness-foil-4: oklch(0.72 0.13 100);
76
+ --hraness-foil-5: oklch(0.55 0.15 45);
77
+ --hraness-foil-6: oklch(0.62 0.16 305);
78
+ --hraness-foil-edge: white;
79
+ }
80
+
81
+ @media (prefers-color-scheme: dark) {
82
+ :where(:root:not([data-theme="light"]):not(.light)) :where(
83
+ .hraness-marketing-page, .hraness-marketing-header, .hraness-marketing-hero,
84
+ .hraness-marketing-pillars, .hraness-marketing-install, .hraness-marketing-section,
85
+ .hraness-marketing-primitives, .hraness-marketing-stats, .hraness-marketing-interfaces,
86
+ .hraness-marketing-trust, .hraness-marketing-quotes, .hraness-marketing-pricing,
87
+ .hraness-marketing-questions, .hraness-marketing-maker, .hraness-marketing-related,
88
+ .hraness-marketing-cta,
89
+ .hraness-marketing-proof-frame
90
+ ) {
91
+ --hraness-foil-1: oklch(0.56 0.16 340);
92
+ --hraness-foil-2: oklch(0.52 0.15 285);
93
+ --hraness-foil-3: oklch(0.66 0.14 175);
94
+ --hraness-foil-4: oklch(0.72 0.13 100);
95
+ --hraness-foil-5: oklch(0.55 0.15 45);
96
+ --hraness-foil-6: oklch(0.62 0.16 305);
97
+ --hraness-foil-edge: white;
98
+ }
99
+ }
100
+
101
+ .hraness-marketing-page :where(p, li, dd) {
102
+ text-wrap: pretty;
103
+ }
104
+
105
+ .hraness-marketing-page :where(a) {
106
+ color: inherit;
107
+ text-decoration-thickness: 1px;
108
+ text-underline-offset: 0.16em;
109
+ }
110
+
111
+ .hraness-marketing-page :where(code, kbd, pre) {
112
+ font-family: var(--hraness-marketing-mono-font);
113
+ }
114
+
115
+ .hraness-marketing-page :where(pre) {
116
+ max-inline-size: 100%;
117
+ margin-block: 0;
118
+ padding: 1rem 1.15rem;
119
+ overflow: auto;
120
+ border-radius: var(--hraness-marketing-radius);
121
+ background: var(--hraness-marketing-surface);
122
+ color: var(--hraness-marketing-ink);
123
+ font-size: 0.85rem;
124
+ line-height: 1.6;
125
+ tab-size: 2;
126
+ white-space: pre;
127
+ overflow-wrap: normal;
128
+ word-break: normal;
129
+ }
130
+
131
+ .hraness-marketing-page :where(:not(pre) > code) {
132
+ padding: 0.08em 0.38em;
133
+ border-radius: 0.3em;
134
+ background: color-mix(in srgb, var(--hraness-marketing-ink) 7%, transparent);
135
+ font-size: 0.9em;
136
+ }
137
+
138
+ .hraness-marketing-page > :where( .hraness-marketing-hero, .hraness-marketing-pillars, .hraness-marketing-install, .hraness-marketing-section, .hraness-marketing-primitives, .hraness-marketing-stats, .hraness-marketing-interfaces, .hraness-marketing-trust, .hraness-marketing-quotes, .hraness-marketing-pricing, .hraness-marketing-questions, .hraness-marketing-maker, .hraness-marketing-related, .hraness-marketing-cta, .hraness-marketing-data-table ) {
139
+ padding-inline: var(--hraness-marketing-gutter);
140
+ }
141
+
142
+ /* Direct children outside the grammar still honor the measure, gutter, and
143
+ block rhythm: a product-owned block cannot escape the column or touch the
144
+ viewport edge. */
145
+ .hraness-marketing-page > :where( :not([data-hraness-marketing], script, style, link, meta, template) ) {
146
+ inline-size: min(100%, var(--hraness-marketing-measure));
147
+ min-inline-size: 0;
148
+ margin-block: clamp(2rem, 5vw, 3.5rem);
149
+ margin-inline: auto;
150
+ padding-inline: var(--hraness-marketing-gutter);
151
+ }
152
+
153
+ /* Evidence-table cells align like prose by default; the numeric and tone
154
+ consumer hooks adjust presentation without inventing new classes. */
155
+ .hraness-marketing-data-table__table :where( th, td ) {
156
+ text-align: start;
157
+ }
158
+
159
+ .hraness-marketing-data-table__table :where( [data-numeric] ) {
160
+ text-align: end;
161
+ white-space: nowrap;
162
+ }
163
+
164
+ .hraness-marketing-data-table__table :where( td[data-tone="positive"] ) {
165
+ color: var(--success, var(--hraness-marketing-accent));
166
+ }
167
+
168
+ .hraness-marketing-data-table__table :where( td[data-tone="negative"] ) {
169
+ color: var(--danger, var(--hraness-marketing-muted));
170
+ }
171
+
172
+ @media (forced-colors: active) {
173
+ .hraness-marketing-data-table__scroll,
174
+ .hraness-marketing-data-table__table tbody :where( th, td ) {
175
+ border-color: CanvasText;
176
+ }
177
+ .hraness-marketing-data-table__table thead :where( th ),
178
+ .hraness-marketing-data-table__table :where( [data-tone] ),
179
+ .hraness-marketing-data-table__meta,
180
+ .hraness-marketing-data-table__note {
181
+ color: CanvasText;
182
+ }
183
+ }
184
+
185
+ /* Section separators are a sibling relationship, not an owned atom: a ruled
186
+ section draws one boundary above itself, except directly after a framed
187
+ component whose own border already closes the block. */
188
+ .hraness-marketing-section,
189
+ .hraness-marketing-primitives,
190
+ .hraness-marketing-interfaces,
191
+ .hraness-marketing-trust,
192
+ .hraness-marketing-quotes,
193
+ .hraness-marketing-pricing,
194
+ .hraness-marketing-questions,
195
+ .hraness-marketing-maker,
196
+ .hraness-marketing-related,
197
+ .hraness-marketing-cta {
198
+ border-block-start: var(--hraness-marketing-rule);
199
+ }
200
+
201
+ :is(
202
+ .hraness-marketing-install,
203
+ .hraness-marketing-proof,
204
+ .hraness-marketing-proof-frame
205
+ ) + :is(
206
+ .hraness-marketing-section,
207
+ .hraness-marketing-primitives,
208
+ .hraness-marketing-interfaces,
209
+ .hraness-marketing-trust,
210
+ .hraness-marketing-quotes,
211
+ .hraness-marketing-pricing,
212
+ .hraness-marketing-questions,
213
+ .hraness-marketing-maker,
214
+ .hraness-marketing-related,
215
+ .hraness-marketing-cta
216
+ ) {
217
+ border-block-start: 0;
218
+ }
219
+
220
+ .hraness-marketing-header__brand :where(img, svg) {
221
+ block-size: 1.25rem;
222
+ inline-size: auto;
223
+ }
224
+
225
+ .hraness-marketing-footer__brand :where(img, svg) {
226
+ block-size: 1.25rem;
227
+ inline-size: auto;
228
+ }
229
+
230
+ .hraness-marketing-footer__inner > :where(p) {
231
+ margin: 0;
232
+ max-inline-size: 48ch;
233
+ }
234
+
235
+ .hraness-marketing-hero[data-tone="accent"] {
236
+ --hraness-marketing-ink: var(--hraness-marketing-accent-ink);
237
+ --hraness-marketing-muted: color-mix(in srgb, var(--hraness-marketing-accent-ink) 78%, transparent);
238
+ --hraness-marketing-line: color-mix(in srgb, var(--hraness-marketing-accent-ink) 22%, transparent);
239
+ --hraness-marketing-line-strong: color-mix(in srgb, var(--hraness-marketing-accent-ink) 40%, transparent);
240
+ --hraness-marketing-surface: color-mix(in srgb, var(--hraness-marketing-accent-ink) 12%, transparent);
241
+ }
242
+
243
+ .hraness-marketing-proof-frame__content > :where(img, video) {
244
+ display: block;
245
+ inline-size: 100%;
246
+ block-size: auto;
247
+ }
248
+
249
+ .hraness-marketing-proof-frame__content > :where(pre) {
250
+ border-radius: 0;
251
+ }
252
+
253
+ .hraness-marketing-page > .hraness-marketing-install {
254
+ --hraness-marketing-install-inline-size: min(100% - 2 * var(--hraness-marketing-gutter), var(--hraness-marketing-measure));
255
+ padding-inline: clamp(1.5rem, 4vw, 2.5rem);
256
+ }
257
+
258
+ .hraness-marketing-section__body > :where(p) {
259
+ max-inline-size: var(--hraness-marketing-prose-measure);
260
+ margin: 0;
261
+ }
262
+
263
+ .hraness-marketing-section__body > :first-child {
264
+ margin-block-start: 0;
265
+ }
266
+
267
+ .hraness-marketing-section__body > :last-child {
268
+ margin-block-end: 0;
269
+ }
270
+
271
+ .hraness-marketing-primitive p:not(:where(.hraness-marketing-primitive__summary)) {
272
+ margin: 0;
273
+ color: var(--hraness-marketing-muted);
274
+ font-size: 0.95rem;
275
+ line-height: 1.5;
276
+ }
277
+
278
+ .hraness-marketing-primitive > :where(pre, figure) {
279
+ margin-block-start: 0.5rem;
280
+ }
281
+
282
+ .hraness-marketing-interface p:not(:where(.hraness-marketing-interface__summary)) {
283
+ margin: 0;
284
+ }
285
+
286
+ .hraness-marketing-interface p:not(:where(.hraness-marketing-interface__summary)) {
287
+ color: var(--hraness-marketing-muted);
288
+ font-size: 0.95rem;
289
+ line-height: 1.5;
290
+ }
291
+
292
+ .hraness-marketing-interface pre {
293
+ margin-block-start: 0.5rem;
294
+ }
295
+
296
+ .hraness-marketing-question__answer > :first-child {
297
+ margin-block-start: 0;
298
+ }
299
+
300
+ .hraness-marketing-question__answer > :last-child {
301
+ margin-block-end: 0;
302
+ }
303
+
304
+ .hraness-marketing-maker__portrait :where(img, svg) {
305
+ display: block;
306
+ inline-size: 100%;
307
+ block-size: 100%;
308
+ object-fit: cover;
309
+ }
310
+
311
+ .hraness-marketing-maker__body > p {
312
+ max-inline-size: var(--hraness-marketing-prose-measure);
313
+ margin: 0;
314
+ color: var(--hraness-marketing-muted);
315
+ font-size: 1.05rem;
316
+ line-height: 1.55;
317
+ }
318
+
319
+ .hraness-marketing-cta[data-tone="accent"] {
320
+ --hraness-marketing-ink: var(--hraness-marketing-accent-ink);
321
+ --hraness-marketing-muted: color-mix(in srgb, var(--hraness-marketing-accent-ink) 78%, transparent);
322
+ --hraness-marketing-line-strong: color-mix(in srgb, var(--hraness-marketing-accent-ink) 40%, transparent);
323
+ --hraness-marketing-surface: transparent;
324
+ }
325
+
326
+ .hraness-marketing-stats__source :where(strong, span) {
327
+ display: block;
328
+ }
329
+ .hraness-marketing-stats__source strong {
330
+ font-family: var(--hraness-marketing-heading-font);
331
+ font-size: clamp(1.75rem, 3vw, 2.25rem);
332
+ font-weight: 500;
333
+ letter-spacing: -0.02em;
334
+ line-height: 1.1;
335
+ font-variant-numeric: tabular-nums;
336
+ }
337
+ .hraness-marketing-stats__source span {
338
+ margin-block-start: 0.3rem;
339
+ color: var(--hraness-marketing-muted);
340
+ font-size: 0.85rem;
341
+ line-height: 1.45;
342
+ }
343
+
344
+ /* Shared header capability/media tokens; paint stays in the owned atoms. */
345
+ /* Keep the browser aliases in separate feature queries: CSS optimizers can
346
+ * collapse adjacent aliases to the prefixed property and lose Chromium blur. */
347
+ .hraness-marketing-header {
348
+ --hraness-marketing-header-background: var(--hraness-marketing-background);
349
+ --hraness-marketing-header-backdrop: none;
350
+ }
351
+
352
+ @supports (-webkit-backdrop-filter: blur(1px)) {
353
+ .hraness-marketing-header {
354
+ --hraness-marketing-header-background: color-mix(in srgb, var(--hraness-marketing-background) 82%, transparent);
355
+ --hraness-marketing-header-backdrop: blur(14px) saturate(1.4);
356
+ }
357
+ }
358
+
359
+ @supports (backdrop-filter: blur(1px)) {
360
+ .hraness-marketing-header {
361
+ --hraness-marketing-header-background: color-mix(in srgb, var(--hraness-marketing-background) 82%, transparent);
362
+ --hraness-marketing-header-backdrop: blur(14px) saturate(1.4);
363
+ }
364
+ }
365
+
366
+ @media (prefers-reduced-transparency: reduce), (forced-colors: active) {
367
+ .hraness-marketing-header {
368
+ --hraness-marketing-header-background: var(--hraness-marketing-background);
369
+ --hraness-marketing-header-backdrop: none;
370
+ }
371
+ }
372
+
373
+ .hraness-marketing-header:not([data-position="static"]) {
374
+ --hraness-sticky-offset: calc(var(--hraness-marketing-header-height, 2.75rem) + 1px);
375
+ }
376
+
377
+ html:has(:is(.hraness-marketing-header, .hraness-marketing-header-surface):not([data-position="static"])),
378
+ .hraness-marketing-page:has(> :is(.hraness-marketing-header, .hraness-marketing-header-surface):not([data-position="static"])) {
379
+ --hraness-sticky-offset: calc(var(--hraness-marketing-header-height, 2.75rem) + 1px);
380
+ }
381
+
382
+ html:has(:is(.hraness-marketing-header, .hraness-marketing-header-surface):not([data-position="static"])) {
383
+ scroll-padding-block-start: var(--hraness-sticky-offset);
384
+ }
385
+
386
+ .hraness-marketing-main,
387
+ .hraness-marketing-header + :is(main, .hraness-marketing-main) {
388
+ scroll-margin-block-start: var(--hraness-sticky-offset);
389
+ }
390
+
391
+ .hraness-marketing-main[data-hraness-clearance="pad"],
392
+ .hraness-marketing-header[data-position="fixed"] + :is(main, .hraness-marketing-main) {
393
+ padding-block-start: var(--hraness-sticky-offset);
394
+ }
395
+
396
+ .hraness-sticky-below-chrome,
397
+ [data-hraness-sticky] {
398
+ position: sticky;
399
+ inset-block-start: var(--hraness-sticky-offset);
400
+ z-index: 30;
401
+ }
402
+
403
+ .hraness-marketing-card-row,
404
+ .hraness-marketing-interface-grid,
405
+ .hraness-marketing-trust-grid,
406
+ .hraness-marketing-quote-grid,
407
+ .hraness-marketing-plan-grid,
408
+ .hraness-marketing-primitives__list {
409
+ align-items: stretch;
410
+ }
411
+
412
+ .hraness-marketing-card-row > *,
413
+ .hraness-marketing-interface-grid > *,
414
+ .hraness-marketing-trust-grid > *,
415
+ .hraness-marketing-quote-grid > *,
416
+ .hraness-marketing-plan-grid > *,
417
+ .hraness-marketing-primitives__list > * {
418
+ min-block-size: 100%;
419
+ align-self: stretch;
420
+ }
421
+ /* Hero backdrops are retired. Hand-authored markup that still ships the old
422
+ * backdrop hooks paints nothing and takes no space. */
423
+ .hraness-marketing-hero-backdrop {
424
+ display: none;
425
+ }
426
+
427
+ /* Keep all meaningful hero content above the inert decorative backdrop. */
428
+ .hraness-marketing-hero > :where(.hraness-marketing-hero__copy, .hraness-marketing-hero__frame, .hraness-marketing-proof, .hraness-marketing-facts) {
429
+ position: relative;
430
+ z-index: 1;
431
+ }
432
+
433
+ }