@deriv-com/dds-marketing 1.0.2 → 1.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 (315) hide show
  1. package/.llm/components/awards.md +209 -0
  2. package/.llm/components/background.md +89 -40
  3. package/.llm/components/bottomsheet.md +5 -0
  4. package/.llm/components/comparisoncards.md +257 -0
  5. package/.llm/components/comparisontable.md +231 -0
  6. package/.llm/components/ctabanner.md +9 -12
  7. package/.llm/components/ctabannerslider.md +15 -2
  8. package/.llm/components/datatable.md +273 -0
  9. package/.llm/components/downloadbanner.md +283 -0
  10. package/.llm/components/featurecards.md +41 -3
  11. package/.llm/components/featurecardscarousel.md +311 -0
  12. package/.llm/components/featuresplit.md +5 -0
  13. package/.llm/components/featurestack.md +227 -0
  14. package/.llm/components/illustrationcard.md +2 -2
  15. package/.llm/components/illustrationscene.md +6 -10
  16. package/.llm/components/imagebanner.md +278 -0
  17. package/.llm/components/navbar.md +1 -1
  18. package/.llm/components/navbaritem.md +4 -0
  19. package/.llm/components/pageheader.md +4 -3
  20. package/.llm/components/paginationdots.md +6 -1
  21. package/.llm/components/producthero.md +35 -11
  22. package/.llm/components/resourcebanner.md +247 -0
  23. package/.llm/components/scenescroll.md +222 -0
  24. package/.llm/components/select.md +17 -0
  25. package/.llm/components/stats.md +219 -0
  26. package/.llm/components/steps.md +286 -0
  27. package/.llm/components/stickycards.md +251 -0
  28. package/.llm/components/tableheadercell.md +19 -0
  29. package/.llm/components/teamgrid.md +243 -0
  30. package/.llm/components/testimonialrow.md +5 -0
  31. package/.llm/components/testimonialslider.md +14 -1
  32. package/.llm/components/textarea.md +13 -0
  33. package/.llm/components/textinput.md +13 -0
  34. package/.llm/components/textscroll.md +187 -0
  35. package/.llm/components/tiers.md +227 -0
  36. package/.llm/digest/awards.md +45 -0
  37. package/.llm/digest/background.md +11 -10
  38. package/.llm/digest/bottomsheet.md +2 -0
  39. package/.llm/digest/comparisoncards.md +51 -0
  40. package/.llm/digest/comparisontable.md +53 -0
  41. package/.llm/digest/ctabanner.md +4 -4
  42. package/.llm/digest/ctabannerslider.md +4 -0
  43. package/.llm/digest/datatable.md +65 -0
  44. package/.llm/digest/downloadbanner.md +65 -0
  45. package/.llm/digest/featurecards.md +1 -1
  46. package/.llm/digest/featurecardscarousel.md +54 -0
  47. package/.llm/digest/featuresplit.md +1 -0
  48. package/.llm/digest/featurestack.md +48 -0
  49. package/.llm/digest/illustrationscene.md +2 -3
  50. package/.llm/digest/imagebanner.md +64 -0
  51. package/.llm/digest/navbaritem.md +1 -0
  52. package/.llm/digest/pageheader.md +2 -1
  53. package/.llm/digest/paginationdots.md +3 -1
  54. package/.llm/digest/producthero.md +4 -2
  55. package/.llm/digest/resourcebanner.md +53 -0
  56. package/.llm/digest/scenescroll.md +49 -0
  57. package/.llm/digest/select.md +5 -0
  58. package/.llm/digest/stats.md +46 -0
  59. package/.llm/digest/steps.md +55 -0
  60. package/.llm/digest/stickycards.md +52 -0
  61. package/.llm/digest/tableheadercell.md +4 -0
  62. package/.llm/digest/teamgrid.md +53 -0
  63. package/.llm/digest/testimonialrow.md +2 -0
  64. package/.llm/digest/testimonialslider.md +4 -0
  65. package/.llm/digest/textarea.md +4 -0
  66. package/.llm/digest/textinput.md +4 -0
  67. package/.llm/digest/textscroll.md +43 -0
  68. package/.llm/digest/tiers.md +50 -0
  69. package/.llm/index.md +19 -3
  70. package/.llm/tokens.md +26 -1
  71. package/AGENTS.md +17 -1
  72. package/dist/components/awards-data.d.ts +24 -0
  73. package/dist/components/awards-data.js +11 -0
  74. package/dist/components/block-image-data.d.ts +24 -0
  75. package/dist/components/block-image-data.js +9 -0
  76. package/dist/components/blocks/awards/Awards.css +134 -0
  77. package/dist/components/blocks/awards/Awards.d.ts +24 -0
  78. package/dist/components/blocks/awards/Awards.js +114 -0
  79. package/dist/components/blocks/awards/Awards.types.d.ts +31 -0
  80. package/dist/components/blocks/awards/Awards.types.js +16 -0
  81. package/dist/components/blocks/awards/awards-impl.css +222 -0
  82. package/dist/components/blocks/cards-of.d.ts +16 -0
  83. package/dist/components/blocks/cards-of.js +23 -0
  84. package/dist/components/blocks/comparison-cards/ComparisonCards.css +195 -0
  85. package/dist/components/blocks/comparison-cards/ComparisonCards.d.ts +24 -0
  86. package/dist/components/blocks/comparison-cards/ComparisonCards.js +75 -0
  87. package/dist/components/blocks/comparison-cards/ComparisonCards.types.d.ts +49 -0
  88. package/dist/components/blocks/comparison-cards/ComparisonCards.types.js +26 -0
  89. package/dist/components/blocks/comparison-cards/comparison-cards-impl.css +262 -0
  90. package/dist/components/blocks/comparison-table/ComparisonTable.css +111 -0
  91. package/dist/components/blocks/comparison-table/ComparisonTable.d.ts +25 -0
  92. package/dist/components/blocks/comparison-table/ComparisonTable.js +100 -0
  93. package/dist/components/blocks/comparison-table/ComparisonTable.types.d.ts +36 -0
  94. package/dist/components/blocks/comparison-table/ComparisonTable.types.js +15 -0
  95. package/dist/components/blocks/comparison-table/comparison-table-impl.css +143 -0
  96. package/dist/components/blocks/cta-banner/CtaBanner.css +1 -0
  97. package/dist/components/blocks/cta-banner/CtaBanner.js +2 -2
  98. package/dist/components/blocks/cta-banner/CtaBanner.types.d.ts +4 -3
  99. package/dist/components/blocks/cta-banner/CtaBanner.types.js +1 -1
  100. package/dist/components/blocks/cta-banner-slider/CtaBannerSlider.css +1 -0
  101. package/dist/components/blocks/cta-banner-slider/CtaBannerSlider.d.ts +1 -1
  102. package/dist/components/blocks/cta-banner-slider/CtaBannerSlider.js +6 -2
  103. package/dist/components/blocks/cta-banner-slider/CtaBannerSlider.types.d.ts +8 -0
  104. package/dist/components/blocks/cta-banner-slider/CtaBannerSlider.types.js +1 -1
  105. package/dist/components/blocks/data-table/DataTable.css +170 -0
  106. package/dist/components/blocks/data-table/DataTable.d.ts +29 -0
  107. package/dist/components/blocks/data-table/DataTable.js +84 -0
  108. package/dist/components/blocks/data-table/DataTable.types.d.ts +69 -0
  109. package/dist/components/blocks/data-table/DataTable.types.js +19 -0
  110. package/dist/components/blocks/data-table/data-table-impl.css +155 -0
  111. package/dist/components/blocks/download-banner/DownloadBanner.css +228 -0
  112. package/dist/components/blocks/download-banner/DownloadBanner.d.ts +28 -0
  113. package/dist/components/blocks/download-banner/DownloadBanner.js +75 -0
  114. package/dist/components/blocks/download-banner/DownloadBanner.types.d.ts +56 -0
  115. package/dist/components/blocks/download-banner/DownloadBanner.types.js +23 -0
  116. package/dist/components/blocks/download-banner/download-banner-impl.css +216 -0
  117. package/dist/components/blocks/faq-accordion/FaqAccordion.css +1 -0
  118. package/dist/components/blocks/faq-accordion/faq-accordion-impl.css +3 -7
  119. package/dist/components/blocks/feature-bullets/FeatureBullets.css +1 -0
  120. package/dist/components/blocks/feature-bullets/feature-bullets-impl.css +14 -1
  121. package/dist/components/blocks/feature-cards/FeatureCards.css +1 -0
  122. package/dist/components/blocks/feature-cards/FeatureCards.d.ts +2 -1
  123. package/dist/components/blocks/feature-cards/FeatureCards.js +4 -21
  124. package/dist/components/blocks/feature-cards/feature-cards-impl.css +10 -9
  125. package/dist/components/blocks/feature-cards-carousel/FeatureCardsCarousel.css +115 -0
  126. package/dist/components/blocks/feature-cards-carousel/FeatureCardsCarousel.d.ts +30 -0
  127. package/dist/components/blocks/feature-cards-carousel/FeatureCardsCarousel.js +160 -0
  128. package/dist/components/blocks/feature-cards-carousel/FeatureCardsCarousel.types.d.ts +52 -0
  129. package/dist/components/blocks/feature-cards-carousel/FeatureCardsCarousel.types.js +17 -0
  130. package/dist/components/blocks/feature-cards-carousel/feature-cards-carousel-impl.css +207 -0
  131. package/dist/components/blocks/feature-scroll/FeatureScroll.css +1 -0
  132. package/dist/components/blocks/feature-split/FeatureSplit.css +1 -0
  133. package/dist/components/blocks/feature-split/feature-split-impl.css +14 -1
  134. package/dist/components/blocks/feature-stack/FeatureStack.css +86 -0
  135. package/dist/components/blocks/feature-stack/FeatureStack.d.ts +19 -0
  136. package/dist/components/blocks/feature-stack/FeatureStack.js +27 -0
  137. package/dist/components/blocks/feature-stack/FeatureStack.types.d.ts +34 -0
  138. package/dist/components/blocks/feature-stack/FeatureStack.types.js +14 -0
  139. package/dist/components/blocks/feature-stack/feature-stack-impl.css +87 -0
  140. package/dist/components/blocks/image-banner/ImageBanner.css +133 -0
  141. package/dist/components/blocks/image-banner/ImageBanner.d.ts +32 -0
  142. package/dist/components/blocks/image-banner/ImageBanner.js +41 -0
  143. package/dist/components/blocks/image-banner/ImageBanner.types.d.ts +54 -0
  144. package/dist/components/blocks/image-banner/ImageBanner.types.js +14 -0
  145. package/dist/components/blocks/image-banner/image-banner-impl.css +281 -0
  146. package/dist/components/blocks/landing-hero/LandingHero.css +1 -0
  147. package/dist/components/blocks/logo-columns/LogoColumns.css +1 -0
  148. package/dist/components/blocks/logo-rows/LogoRows.css +1 -0
  149. package/dist/components/blocks/page-header/PageHeader.css +1 -0
  150. package/dist/components/blocks/page-header/PageHeader.js +2 -2
  151. package/dist/components/blocks/page-header/PageHeader.types.d.ts +4 -2
  152. package/dist/components/blocks/page-header/PageHeader.types.js +1 -1
  153. package/dist/components/blocks/page-header/page-header-impl.css +5 -3
  154. package/dist/components/blocks/product-hero/ProductHero.css +8 -3
  155. package/dist/components/blocks/product-hero/ProductHero.d.ts +1 -1
  156. package/dist/components/blocks/product-hero/ProductHero.js +5 -2
  157. package/dist/components/blocks/product-hero/ProductHero.types.d.ts +5 -1
  158. package/dist/components/blocks/product-hero/ProductHero.types.js +1 -0
  159. package/dist/components/blocks/product-hero/product-hero-impl.css +43 -12
  160. package/dist/components/blocks/resource-banner/ResourceBanner.css +106 -0
  161. package/dist/components/blocks/resource-banner/ResourceBanner.d.ts +32 -0
  162. package/dist/components/blocks/resource-banner/ResourceBanner.js +51 -0
  163. package/dist/components/blocks/resource-banner/ResourceBanner.types.d.ts +44 -0
  164. package/dist/components/blocks/resource-banner/ResourceBanner.types.js +14 -0
  165. package/dist/components/blocks/resource-banner/resource-banner-impl.css +69 -0
  166. package/dist/components/blocks/scene-scroll/SceneScroll.css +93 -0
  167. package/dist/components/blocks/scene-scroll/SceneScroll.d.ts +36 -0
  168. package/dist/components/blocks/scene-scroll/SceneScroll.js +54 -0
  169. package/dist/components/blocks/scene-scroll/SceneScroll.types.d.ts +32 -0
  170. package/dist/components/blocks/scene-scroll/SceneScroll.types.js +15 -0
  171. package/dist/components/blocks/scene-scroll/scene-scroll-impl.css +275 -0
  172. package/dist/components/blocks/stats/Stats.css +139 -0
  173. package/dist/components/blocks/stats/Stats.d.ts +24 -0
  174. package/dist/components/blocks/stats/Stats.js +120 -0
  175. package/dist/components/blocks/stats/Stats.types.d.ts +36 -0
  176. package/dist/components/blocks/stats/Stats.types.js +19 -0
  177. package/dist/components/blocks/stats/stats-count.d.ts +34 -0
  178. package/dist/components/blocks/stats/stats-count.js +61 -0
  179. package/dist/components/blocks/stats/stats-impl.css +284 -0
  180. package/dist/components/blocks/stats/use-wheel.d.ts +37 -0
  181. package/dist/components/blocks/stats/use-wheel.js +126 -0
  182. package/dist/components/blocks/steps/Steps.css +202 -0
  183. package/dist/components/blocks/steps/Steps.d.ts +32 -0
  184. package/dist/components/blocks/steps/Steps.js +65 -0
  185. package/dist/components/blocks/steps/Steps.types.d.ts +48 -0
  186. package/dist/components/blocks/steps/Steps.types.js +21 -0
  187. package/dist/components/blocks/steps/steps-impl.css +216 -0
  188. package/dist/components/blocks/steps/use-turn.d.ts +33 -0
  189. package/dist/components/blocks/steps/use-turn.js +75 -0
  190. package/dist/components/blocks/sticky-cards/StickyCards.css +154 -0
  191. package/dist/components/blocks/sticky-cards/StickyCards.d.ts +35 -0
  192. package/dist/components/blocks/sticky-cards/StickyCards.js +153 -0
  193. package/dist/components/blocks/sticky-cards/StickyCards.types.d.ts +43 -0
  194. package/dist/components/blocks/sticky-cards/StickyCards.types.js +18 -0
  195. package/dist/components/blocks/sticky-cards/sticky-cards-impl.css +393 -0
  196. package/dist/components/blocks/team-grid/TeamGrid.css +159 -0
  197. package/dist/components/blocks/team-grid/TeamGrid.d.ts +28 -0
  198. package/dist/components/blocks/team-grid/TeamGrid.js +74 -0
  199. package/dist/components/blocks/team-grid/TeamGrid.types.d.ts +51 -0
  200. package/dist/components/blocks/team-grid/TeamGrid.types.js +20 -0
  201. package/dist/components/blocks/team-grid/team-grid-impl.css +129 -0
  202. package/dist/components/blocks/testimonial-row/TestimonialRow.css +1 -0
  203. package/dist/components/blocks/testimonial-row/TestimonialRow.d.ts +1 -1
  204. package/dist/components/blocks/testimonial-row/TestimonialRow.js +4 -3
  205. package/dist/components/blocks/testimonial-row/TestimonialRow.types.d.ts +3 -0
  206. package/dist/components/blocks/testimonial-row/TestimonialRow.types.js +1 -1
  207. package/dist/components/blocks/testimonial-row/testimonial-row-impl.css +8 -0
  208. package/dist/components/blocks/testimonial-slider/TestimonialSlider.css +1 -0
  209. package/dist/components/blocks/testimonial-slider/TestimonialSlider.d.ts +1 -1
  210. package/dist/components/blocks/testimonial-slider/TestimonialSlider.js +2 -2
  211. package/dist/components/blocks/testimonial-slider/TestimonialSlider.types.d.ts +8 -0
  212. package/dist/components/blocks/testimonial-slider/TestimonialSlider.types.js +1 -1
  213. package/dist/components/blocks/text-scroll/TextScroll.css +53 -0
  214. package/dist/components/blocks/text-scroll/TextScroll.d.ts +5 -0
  215. package/dist/components/blocks/text-scroll/TextScroll.js +71 -0
  216. package/dist/components/blocks/text-scroll/TextScroll.types.d.ts +22 -0
  217. package/dist/components/blocks/text-scroll/TextScroll.types.js +10 -0
  218. package/dist/components/blocks/text-scroll/text-scroll-impl.css +115 -0
  219. package/dist/components/blocks/tiers/Tiers.css +233 -0
  220. package/dist/components/blocks/tiers/Tiers.d.ts +32 -0
  221. package/dist/components/blocks/tiers/Tiers.js +68 -0
  222. package/dist/components/blocks/tiers/Tiers.types.d.ts +45 -0
  223. package/dist/components/blocks/tiers/Tiers.types.js +25 -0
  224. package/dist/components/blocks/tiers/tiers-impl.css +185 -0
  225. package/dist/components/blocks/use-rotation.d.ts +12 -2
  226. package/dist/components/blocks/use-rotation.js +6 -28
  227. package/dist/components/comparison-cards-data.d.ts +59 -0
  228. package/dist/components/comparison-cards-data.js +12 -0
  229. package/dist/components/comparison-table-data.d.ts +39 -0
  230. package/dist/components/comparison-table-data.js +11 -0
  231. package/dist/components/core/accordion-item/AccordionItem.css +1 -5
  232. package/dist/components/core/background/Background.css +23 -0
  233. package/dist/components/core/background/Background.d.ts +10 -4
  234. package/dist/components/core/background/Background.js +16 -7
  235. package/dist/components/core/background/Background.types.d.ts +11 -8
  236. package/dist/components/core/background/Background.types.js +3 -1
  237. package/dist/components/core/background/background-impl.css +334 -1
  238. package/dist/components/core/background/halftone.d.ts +11 -0
  239. package/dist/components/core/background/halftone.js +43 -3
  240. package/dist/components/core/background/patterns.d.ts +66 -0
  241. package/dist/components/core/background/patterns.js +244 -0
  242. package/dist/components/core/background/shader.d.ts +44 -0
  243. package/dist/components/core/background/shader.js +307 -0
  244. package/dist/components/core/background/use-shader.d.ts +11 -0
  245. package/dist/components/core/background/use-shader.js +25 -0
  246. package/dist/components/core/background/use-spotlight.d.ts +2 -1
  247. package/dist/components/core/background/use-spotlight.js +9 -1
  248. package/dist/components/core/bottom-sheet/BottomSheet.js +2 -2
  249. package/dist/components/core/bottom-sheet/BottomSheet.types.d.ts +3 -1
  250. package/dist/components/core/breadcrumb/Breadcrumb.d.ts +1 -0
  251. package/dist/components/core/breadcrumb/Breadcrumb.js +1 -0
  252. package/dist/components/core/breadcrumb/breadcrumb-impl.css +11 -0
  253. package/dist/components/core/button/button-impl.css +14 -1
  254. package/dist/components/core/illustration-card/IllustrationCard.js +1 -1
  255. package/dist/components/core/illustration-card/illustration-card-impl.css +20 -0
  256. package/dist/components/core/illustration-scene/IllustrationScene.css +4 -7
  257. package/dist/components/core/illustration-scene/IllustrationScene.types.d.ts +2 -3
  258. package/dist/components/core/illustration-scene/illustration-scene-impl.css +7 -1
  259. package/dist/components/core/link/Link.js +7 -3
  260. package/dist/components/core/link/link-impl.css +29 -2
  261. package/dist/components/core/navbar/Navbar.css +2 -2
  262. package/dist/components/core/navbar/Navbar.types.d.ts +1 -1
  263. package/dist/components/core/navbar-item/NavbarItem.d.ts +2 -1
  264. package/dist/components/core/navbar-item/NavbarItem.js +3 -10
  265. package/dist/components/core/navbar-item/NavbarItem.types.d.ts +4 -1
  266. package/dist/components/core/navbar-item/NavbarItem.types.js +1 -1
  267. package/dist/components/core/navbar-item/navbar-item-impl.css +10 -0
  268. package/dist/components/core/navbar-menu/navbar-menu-impl.css +9 -0
  269. package/dist/components/core/navbar-switcher/navbar-switcher-impl.css +19 -0
  270. package/dist/components/core/pagination/Pagination.js +1 -1
  271. package/dist/components/core/pagination/pagination-impl.css +9 -0
  272. package/dist/components/core/pagination-dots/PaginationDots.d.ts +1 -1
  273. package/dist/components/core/pagination-dots/PaginationDots.js +3 -3
  274. package/dist/components/core/pagination-dots/PaginationDots.types.d.ts +3 -1
  275. package/dist/components/core/segmented-control/SegmentedControl.css +4 -3
  276. package/dist/components/core/segmented-control/SegmentedControl.js +17 -3
  277. package/dist/components/core/segmented-control-item/SegmentedControlItem.css +2 -1
  278. package/dist/components/core/select/Select.js +3 -3
  279. package/dist/components/core/select/Select.types.d.ts +11 -1
  280. package/dist/components/core/select/Select.types.js +1 -1
  281. package/dist/components/core/tab-list/TabList.js +9 -1
  282. package/dist/components/core/table-header-cell/TableHeaderCell.d.ts +1 -1
  283. package/dist/components/core/table-header-cell/TableHeaderCell.js +5 -2
  284. package/dist/components/core/table-header-cell/TableHeaderCell.types.d.ts +7 -1
  285. package/dist/components/core/table-header-cell/TableHeaderCell.types.js +1 -1
  286. package/dist/components/core/text-area/TextArea.d.ts +1 -1
  287. package/dist/components/core/text-area/TextArea.js +2 -2
  288. package/dist/components/core/text-area/TextArea.types.d.ts +9 -1
  289. package/dist/components/core/text-area/TextArea.types.js +1 -1
  290. package/dist/components/core/text-input/TextInput.d.ts +1 -1
  291. package/dist/components/core/text-input/TextInput.js +2 -2
  292. package/dist/components/core/text-input/TextInput.types.d.ts +9 -1
  293. package/dist/components/core/text-input/TextInput.types.js +1 -1
  294. package/dist/components/data-table-data.d.ts +33 -0
  295. package/dist/components/data-table-data.js +12 -0
  296. package/dist/components/download-banner-data.d.ts +52 -0
  297. package/dist/components/download-banner-data.js +11 -0
  298. package/dist/components/index.d.ts +45 -0
  299. package/dist/components/index.js +32 -0
  300. package/dist/components/resource-banner-data.d.ts +34 -0
  301. package/dist/components/resource-banner-data.js +13 -0
  302. package/dist/components/scene-scroll-data.d.ts +24 -0
  303. package/dist/components/scene-scroll-data.js +9 -0
  304. package/dist/components/stats-data.d.ts +28 -0
  305. package/dist/components/stats-data.js +11 -0
  306. package/dist/components/steps-data.d.ts +38 -0
  307. package/dist/components/steps-data.js +13 -0
  308. package/dist/components/sticky-cards-data.d.ts +43 -0
  309. package/dist/components/sticky-cards-data.js +12 -0
  310. package/dist/components/team-grid-data.d.ts +38 -0
  311. package/dist/components/team-grid-data.js +11 -0
  312. package/dist/components/tiers-data.d.ts +47 -0
  313. package/dist/components/tiers-data.js +13 -0
  314. package/dist/marketing.css +77 -28
  315. package/package.json +1 -1
@@ -276,7 +276,14 @@
276
276
  --dds-background-pool: var(--color-fg-on-brand);
277
277
  --dds-background-pool-deep: var(--color-bg-brand-active);
278
278
  --dds-background-strength: 0.6;
279
- --dds-background-hover: 0.2;
279
+ /*
280
+ * The pointer's pool on the coral is blended in rather than laid over: white at a fifth on top of
281
+ * the coral read as a pale pink patch, a disc of another colour. Overlay lifts the coral under it
282
+ * to a brighter coral instead — light on the band rather than a stain on it — and takes the whole
283
+ * pool without washing out: soft light at full strength was barely there.
284
+ */
285
+ --dds-background-hover: 1;
286
+ --dds-background-glow-blend: overlay;
280
287
  }
281
288
 
282
289
  /*
@@ -351,6 +358,13 @@
351
358
  calc(var(--dds-background-spot-x, 50%) - var(--size-aurora-spotlight)),
352
359
  calc(var(--dds-background-spot-y, 50%) - var(--size-aurora-spotlight))
353
360
  );
361
+ /*
362
+ * And blurred by a quarter of its radius, so no step of the fade survives as a ring and the pool
363
+ * has no edge at all — the eased stops alone still left a disc the eye could trace on the coral.
364
+ * Half the radius spread it so thin it all but vanished.
365
+ */
366
+ filter: blur(calc(var(--size-aurora-spotlight) / 4));
367
+ mix-blend-mode: var(--dds-background-glow-blend, normal);
354
368
  opacity: 0;
355
369
  transition-property: opacity, transform;
356
370
  transition-duration: var(--motion-duration-normal), var(--motion-duration-instant);
@@ -425,3 +439,322 @@
425
439
  transition: none;
426
440
  }
427
441
  }
442
+
443
+ /*
444
+ * The mesh, for `pattern="mesh"`: the whole band lit by five pools of the brand's reds that run
445
+ * into one another like liquid — the design system's own front page, as a pattern.
446
+ *
447
+ * The layer overhangs the band by a tenth each side and is blurred by size.mesh.blur, so no pool
448
+ * keeps an edge and the layer's own edge falls outside the band. It sits behind the content by a
449
+ * negative stacking order, which the root's isolation keeps inside the band.
450
+ *
451
+ * The inks are the tone's, set here as five custom properties because each pool takes a different
452
+ * one. On the coral ground, the drawing the pattern was made for: the brand's deepest red, its
453
+ * pressed red twice, its hover, and the coral itself — no white, which washed the reds to pink. On
454
+ * the dark ground the same reds glowing on near-black, the deepest swapped for the coral so the
455
+ * glow has somewhere to start. On the light ground a blush: the coral and its pale tint, held back.
456
+ */
457
+ :where([data-dds-context="marketing"]) .dds-background .dds-background__mesh {
458
+ --dds-background-mesh-1: var(--color-bg-brand-deep);
459
+ --dds-background-mesh-2: var(--color-bg-brand-active);
460
+ --dds-background-mesh-3: var(--color-bg-brand-hover);
461
+ --dds-background-mesh-4: var(--color-bg-brand-active);
462
+ --dds-background-mesh-5: var(--color-bg-brand);
463
+ /* The deepest pool is a shade under the words, not a ground of its own: at 0.85 it turned half
464
+ the band brown, at 0.35 it left the words on a flat red. */
465
+ --dds-background-mesh-shade: 0.55;
466
+ --dds-background-mesh-strength: 1;
467
+ inset: -10%;
468
+ z-index: -1;
469
+ filter: blur(var(--size-mesh-blur)) saturate(1.15);
470
+ }
471
+
472
+ :where([data-dds-context="marketing"]) .dds-background[data-tone='dark'] .dds-background__mesh {
473
+ --dds-background-mesh-1: var(--color-bg-brand);
474
+ --dds-background-mesh-shade: 0.7;
475
+ --dds-background-mesh-strength: 0.6;
476
+ }
477
+
478
+ :where([data-dds-context="marketing"]) .dds-background[data-tone='light'] .dds-background__mesh {
479
+ --dds-background-mesh-1: var(--color-bg-brand-subtle);
480
+ --dds-background-mesh-2: var(--color-bg-brand);
481
+ --dds-background-mesh-3: var(--color-bg-brand-subtle);
482
+ --dds-background-mesh-4: var(--color-bg-brand-hover);
483
+ --dds-background-mesh-5: var(--color-bg-brand-subtle);
484
+ --dds-background-mesh-shade: 1;
485
+ --dds-background-mesh-strength: 0.22;
486
+ }
487
+
488
+ /*
489
+ * A pool: an uneven round shape that drifts and stretches on two loops of its own length, and
490
+ * leans towards the pointer by its own depth — a hundredth of the pool per step — trailing it by
491
+ * its own lag, so the colours slide through one another instead of moving as one.
492
+ *
493
+ * Every length of time is a multiple of a motion token, so the mesh keeps pace with the others:
494
+ * the loops of motion.duration.drift, the lag of motion.duration.trail.
495
+ *
496
+ * The drift is a transform and the stretch a `scale` and `rotate`, all the compositor's to move;
497
+ * a changing `border-radius` repainted the whole blurred layer every frame.
498
+ */
499
+ :where([data-dds-context="marketing"]) .dds-background .dds-background__pool {
500
+ --depth: 6;
501
+ --lag: 2.8;
502
+ inline-size: 60%;
503
+ aspect-ratio: 1;
504
+ border-radius: 42% 58% 63% 37% / 41% 44% 56% 59%;
505
+ opacity: var(--dds-background-mesh-strength);
506
+ animation:
507
+ dds-background-mesh-drift calc(var(--motion-duration-drift) * 2.6) ease-in-out infinite alternate,
508
+ dds-background-mesh-morph calc(var(--motion-duration-drift) * 1.1) ease-in-out infinite alternate;
509
+ translate:
510
+ calc(var(--dds-background-lean-x, 0) * var(--depth) * 1%)
511
+ calc(var(--dds-background-lean-y, 0) * var(--depth) * 1%);
512
+ transition-property: translate;
513
+ transition-duration: calc(var(--motion-duration-trail) * var(--lag));
514
+ }
515
+
516
+ :where([data-dds-context="marketing"]) .dds-background .dds-background__pool:nth-child(1) {
517
+ --depth: -8;
518
+ --lag: 4.4;
519
+ inset: 45% auto auto -5%;
520
+ inline-size: 45%;
521
+ background-color: var(--dds-background-mesh-1);
522
+ opacity: calc(var(--dds-background-mesh-strength) * var(--dds-background-mesh-shade));
523
+ animation-duration: calc(var(--motion-duration-drift) * 2.6), calc(var(--motion-duration-drift) * 1.3);
524
+ }
525
+
526
+ :where([data-dds-context="marketing"]) .dds-background .dds-background__pool:nth-child(2) {
527
+ --depth: 10;
528
+ --lag: 3.2;
529
+ inset: -20% -15% auto auto;
530
+ background-color: var(--dds-background-mesh-2);
531
+ animation-duration: calc(var(--motion-duration-drift) * 3.1), calc(var(--motion-duration-drift) * 0.9);
532
+ animation-delay: calc(var(--motion-duration-drift) * -0.8), calc(var(--motion-duration-drift) * -0.3);
533
+ }
534
+
535
+ :where([data-dds-context="marketing"]) .dds-background .dds-background__pool:nth-child(3) {
536
+ --depth: 16;
537
+ --lag: 2;
538
+ inset: 30% 20% auto auto;
539
+ inline-size: 45%;
540
+ background-color: var(--dds-background-mesh-3);
541
+ animation-duration: calc(var(--motion-duration-drift) * 2.2), calc(var(--motion-duration-drift) * 0.7);
542
+ animation-delay: calc(var(--motion-duration-drift) * -1.4), calc(var(--motion-duration-drift) * -0.5);
543
+ }
544
+
545
+ :where([data-dds-context="marketing"]) .dds-background .dds-background__pool:nth-child(4) {
546
+ --depth: -12;
547
+ --lag: 5.2;
548
+ inset: auto auto -30% 25%;
549
+ inline-size: 50%;
550
+ background-color: var(--dds-background-mesh-4);
551
+ animation-duration: calc(var(--motion-duration-drift) * 3.5), calc(var(--motion-duration-drift) * 1.2);
552
+ animation-delay: calc(var(--motion-duration-drift) * -0.4), calc(var(--motion-duration-drift) * -0.9);
553
+ }
554
+
555
+ /* The coral itself, high in the far corner: the brightest the pools go. */
556
+ :where([data-dds-context="marketing"]) .dds-background .dds-background__pool:nth-child(5) {
557
+ --depth: 20;
558
+ --lag: 1.6;
559
+ inset: -5% 10% auto auto;
560
+ inline-size: 30%;
561
+ background-color: var(--dds-background-mesh-5);
562
+ animation-duration: calc(var(--motion-duration-drift) * 2.8), calc(var(--motion-duration-drift) * 0.8);
563
+ animation-delay: calc(var(--motion-duration-drift) * -1.8), calc(var(--motion-duration-drift) * -0.2);
564
+ }
565
+
566
+ @keyframes dds-background-mesh-drift {
567
+ 0% { transform: translate(0, 0) scale(1); }
568
+ 33% { transform: translate(12%, -8%) scale(1.15); }
569
+ 66% { transform: translate(-10%, 10%) scale(0.9); }
570
+ 100% { transform: translate(8%, 6%) scale(1.08); }
571
+ }
572
+
573
+ @keyframes dds-background-mesh-morph {
574
+ 0% { scale: 1 1; rotate: 0deg; }
575
+ 33% { scale: 1.25 0.8; rotate: 50deg; }
576
+ 66% { scale: 0.85 1.2; rotate: 110deg; }
577
+ 100% { scale: 1.15 0.9; rotate: 160deg; }
578
+ }
579
+
580
+ /*
581
+ * Off screen the pools wait where they are. With `animated` off they rest, and flow only while the
582
+ * pointer is over the band — the front page's own setting — stopping where they are when it leaves
583
+ * rather than jumping home.
584
+ */
585
+ :where([data-dds-context="marketing"]) .dds-background[data-offscreen='true'] .dds-background__pool,
586
+ :where([data-dds-context="marketing"]) .dds-background[data-animated='false'] .dds-background__pool {
587
+ animation-play-state: paused;
588
+ }
589
+
590
+ :where([data-dds-context="marketing"]) .dds-background[data-animated='false'][data-spotlit='true'] .dds-background__pool {
591
+ animation-play-state: running;
592
+ }
593
+
594
+ /* For a reader who asked for less motion the pools hold still and do not follow the pointer. */
595
+ @media (prefers-reduced-motion: reduce) {
596
+ :where([data-dds-context="marketing"]) .dds-background .dds-background__pool {
597
+ animation: none;
598
+ translate: none;
599
+ transition: none;
600
+ }
601
+ }
602
+
603
+ /*
604
+ * The shaded patterns — see `shader.ts` and `patterns.ts`.
605
+ *
606
+ * Each names its inks here, per tone, as six custom properties the shader reads back as the
607
+ * browser resolves them: the ground first, then the pattern's own. They are opaque — a shader
608
+ * cannot see through to the band — so a translucent role is mixed onto its ground here, where the
609
+ * stylesheet can do it, rather than in the shader. The strength is how loudly the pattern is
610
+ * painted on that ground, and the grain how much tooth it is given: least on the light ground,
611
+ * where grain reads as dirt, most on the dark, where it reads as film.
612
+ */
613
+ :where([data-dds-context="marketing"]) .dds-background {
614
+ --dds-background-gl-strength: 1;
615
+ --dds-background-gl-grain: 0.035;
616
+ --dds-background-gl-hairline: var(--border-width-default);
617
+ }
618
+
619
+ :where([data-dds-context="marketing"]) .dds-background[data-tone='dark'] {
620
+ --dds-background-gl-grain: 0.05;
621
+ }
622
+
623
+ :where([data-dds-context="marketing"]) .dds-background[data-tone='light'] {
624
+ --dds-background-gl-grain: 0.025;
625
+ }
626
+
627
+ /*
628
+ * The aurora and the mesh take none. They are drawn at half the density and scaled up, so the grain
629
+ * was scaled up with them: at the hairline patterns' amount it read as noise laid over the light,
630
+ * and at less it showed the lattice of the pixels it was drawn on. The browser's own smoothing as it
631
+ * scales them up is what keeps their long fades from banding.
632
+ */
633
+ :where([data-dds-context="marketing"]) .dds-background:is([data-pattern='aurora'], [data-pattern='mesh']) {
634
+ --dds-background-gl-grain: 0;
635
+ }
636
+
637
+ /* The aurora: curtains lighter than the ground they rise from, with a deeper red folded through. */
638
+ :where([data-dds-context="marketing"]) .dds-background[data-pattern='aurora'][data-tone='brand'] {
639
+ /* On the coral the curtains are the brand's own deeper reds — its hover and its pressed red —
640
+ with only a touch of light at their brightest, so the band stays coral. White curtains washed
641
+ the coral to pink. */
642
+ --dds-background-gl-ink0: var(--color-bg-brand);
643
+ --dds-background-gl-ink1: var(--color-bg-brand-active);
644
+ --dds-background-gl-ink2: var(--color-bg-brand-hover);
645
+ --dds-background-gl-ink3: color-mix(in srgb, var(--color-bg-brand) 72%, var(--color-fg-on-brand));
646
+ --dds-background-gl-strength: 1;
647
+ }
648
+
649
+ :where([data-dds-context="marketing"]) .dds-background[data-pattern='aurora'][data-tone='dark'] {
650
+ --dds-background-gl-ink0: var(--color-bg-brand-strong);
651
+ --dds-background-gl-ink1: var(--color-bg-brand);
652
+ --dds-background-gl-ink2: var(--color-bg-brand-active);
653
+ --dds-background-gl-ink3: color-mix(in srgb, var(--color-bg-brand) 60%, var(--color-fg-on-brand-strong));
654
+ --dds-background-gl-strength: 0.85;
655
+ }
656
+
657
+ :where([data-dds-context="marketing"]) .dds-background[data-pattern='aurora'][data-tone='light'] {
658
+ --dds-background-gl-ink0: var(--color-bg-page);
659
+ --dds-background-gl-ink1: var(--color-bg-brand);
660
+ --dds-background-gl-ink2: var(--color-bg-brand-hover);
661
+ --dds-background-gl-ink3: color-mix(in srgb, var(--color-bg-brand) 50%, var(--color-bg-page));
662
+ --dds-background-gl-strength: 0.35;
663
+ }
664
+
665
+ /*
666
+ * The mesh: the front page's five pools, in the same inks as the stylesheet's version of them —
667
+ * the deepest red, the pressed red, the hover, the pressed red again, and the coral — on each
668
+ * ground. The strength is that version's too: full on the coral, a glow on the dark, a blush on
669
+ * the light. How far the deepest is held back is its `--dds-background-mesh-shade`.
670
+ */
671
+ :where([data-dds-context="marketing"]) .dds-background[data-pattern='mesh'][data-tone='brand'] {
672
+ --dds-background-gl-ink0: var(--color-bg-brand);
673
+ --dds-background-gl-ink1: var(--color-bg-brand-deep);
674
+ --dds-background-gl-ink2: var(--color-bg-brand-active);
675
+ --dds-background-gl-ink3: var(--color-bg-brand-hover);
676
+ --dds-background-gl-ink4: var(--color-bg-brand-active);
677
+ --dds-background-gl-ink5: var(--color-bg-brand);
678
+ }
679
+
680
+ :where([data-dds-context="marketing"]) .dds-background[data-pattern='mesh'][data-tone='dark'] {
681
+ --dds-background-gl-ink0: var(--color-bg-brand-strong);
682
+ --dds-background-gl-ink1: var(--color-bg-brand);
683
+ --dds-background-gl-ink2: var(--color-bg-brand-active);
684
+ --dds-background-gl-ink3: var(--color-bg-brand-hover);
685
+ --dds-background-gl-ink4: var(--color-bg-brand-active);
686
+ --dds-background-gl-ink5: var(--color-bg-brand);
687
+ /* Less than the stylesheet's 0.6: its pools are smaller than the band, and these reach every
688
+ corner of it, so at that strength the near-black ground was gone. */
689
+ --dds-background-gl-strength: 0.35;
690
+ }
691
+
692
+ :where([data-dds-context="marketing"]) .dds-background[data-pattern='mesh'][data-tone='light'] {
693
+ --dds-background-gl-ink0: var(--color-bg-page);
694
+ --dds-background-gl-ink1: var(--color-bg-brand-subtle);
695
+ --dds-background-gl-ink2: var(--color-bg-brand);
696
+ --dds-background-gl-ink3: var(--color-bg-brand-subtle);
697
+ --dds-background-gl-ink4: var(--color-bg-brand-hover);
698
+ --dds-background-gl-ink5: var(--color-bg-brand-subtle);
699
+ --dds-background-gl-strength: 0.22;
700
+ }
701
+
702
+ /* The grid and the rings: the ground, the resting rule, the lit rule, and the bloom at the foot. */
703
+ :where([data-dds-context="marketing"]) .dds-background[data-pattern='grid'] {
704
+ --dds-background-gl-pitch: var(--size-rule-grid-pitch);
705
+ }
706
+
707
+ :where([data-dds-context="marketing"]) .dds-background[data-pattern='rings'] {
708
+ --dds-background-gl-pitch: var(--size-rings-pitch);
709
+ }
710
+
711
+ :where([data-dds-context="marketing"]) .dds-background:is([data-pattern='grid'], [data-pattern='rings']) {
712
+ --dds-background-gl-hairline: var(--dds-background-hairline);
713
+ }
714
+
715
+ /* On the coral, the brand's own reds: the bloom the lines rise out of is its pressed red, a deeper
716
+ coral rather than a whitened one, and the rules a faint lighter coral — white blooms and white
717
+ rules washed the band to pink. The lit lines and the travelling light keep most of the white,
718
+ since they are the one thing on the band meant to catch the eye. */
719
+ :where([data-dds-context="marketing"]) .dds-background:is([data-pattern='grid'], [data-pattern='rings'])[data-tone='brand'] {
720
+ --dds-background-gl-ink0: var(--color-bg-brand);
721
+ --dds-background-gl-ink1: color-mix(in srgb, var(--color-fg-on-brand) 22%, var(--color-bg-brand));
722
+ --dds-background-gl-ink2: color-mix(in srgb, var(--color-fg-on-brand) 70%, var(--color-bg-brand));
723
+ --dds-background-gl-ink3: var(--color-bg-brand-active);
724
+ }
725
+
726
+ :where([data-dds-context="marketing"]) .dds-background:is([data-pattern='grid'], [data-pattern='rings'])[data-tone='dark'] {
727
+ --dds-background-gl-ink0: var(--color-bg-brand-strong);
728
+ --dds-background-gl-ink1: color-mix(in srgb, var(--color-fg-on-brand-strong) 16%, var(--color-bg-brand-strong));
729
+ --dds-background-gl-ink2: var(--color-bg-brand);
730
+ --dds-background-gl-ink3: var(--color-bg-brand-deep);
731
+ }
732
+
733
+ :where([data-dds-context="marketing"]) .dds-background:is([data-pattern='grid'], [data-pattern='rings'])[data-tone='light'] {
734
+ --dds-background-gl-ink0: var(--color-bg-page);
735
+ --dds-background-gl-ink1: color-mix(in srgb, var(--color-bg-brand) 28%, var(--color-bg-page));
736
+ --dds-background-gl-ink2: var(--color-bg-brand);
737
+ --dds-background-gl-ink3: color-mix(in srgb, var(--color-bg-brand) 12%, var(--color-bg-page));
738
+ }
739
+
740
+ /*
741
+ * The shader's canvas is opaque, so until it has a frame it stays out of sight and the stylesheet's
742
+ * version shows. Once it has — `data-shaded` — the stylesheet's paint steps aside: the grid's and
743
+ * the rings' background images, every pattern's pseudo-elements, and the mesh's pools.
744
+ */
745
+ :where([data-dds-context="marketing"]) .dds-background:not([data-pattern='halftone']):not([data-shaded='true']) .dds-background__canvas {
746
+ visibility: hidden;
747
+ }
748
+
749
+ :where([data-dds-context="marketing"]) .dds-background[data-shaded='true'] {
750
+ background-image: none;
751
+ }
752
+
753
+ :where([data-dds-context="marketing"]) .dds-background[data-shaded='true']::before,
754
+ :where([data-dds-context="marketing"]) .dds-background[data-shaded='true']::after {
755
+ display: none;
756
+ }
757
+
758
+ :where([data-dds-context="marketing"]) .dds-background[data-shaded='true'] .dds-background__mesh {
759
+ display: none;
760
+ }
@@ -89,6 +89,17 @@ export interface HalftoneTuning {
89
89
  ceiling: number;
90
90
  /** How sharply the glow falls off — higher is a tighter, harder-edged glow. */
91
91
  falloff: number;
92
+ /**
93
+ * How far the glow's edge wanders from a smooth ellipse, from 0 to 1. A slow field of waves warps
94
+ * the distance each dot is lit by, so the pool of light has lobes and inlets that drift as it
95
+ * moves — light on a surface rather than a spotlight. 0 is the plain ellipse.
96
+ */
97
+ texture: number;
98
+ /**
99
+ * How strong a soft wash of the dots' own ink is under the glow, from 0 to 1 — the light the dots
100
+ * are lit by, as well as the dots. It follows the glow and takes its shape. 0 draws dots alone.
101
+ */
102
+ bloom: number;
92
103
  /** Where the glow rests, as a share of the band's width. */
93
104
  restX: number;
94
105
  /** Where the glow rests, as a share of the band's height. */
@@ -61,6 +61,11 @@ const SETTINGS = {
61
61
  // dots would sit where the fade has thinned them, and the heart of the light would read as dim.
62
62
  ceiling: 0.47,
63
63
  falloff: 1.2,
64
+ // A third of the way from a plain ellipse: enough that the glow's edge reads as uneven light,
65
+ // not so much that the pool breaks into islands.
66
+ texture: 0.35,
67
+ // A faint wash under the glow, so the dots read as lit rather than as printed on a flat ground.
68
+ bloom: 0.18,
64
69
  // Centred, on the band's foot, so the light rises from the bottom edge under the words.
65
70
  restX: 0.5,
66
71
  restY: 0.97,
@@ -198,6 +203,9 @@ export function halftone(root, canvas, options) {
198
203
  let tuning = { ...fromTokens, ...SETTINGS, ...overrides };
199
204
  // One colour per brightness step, from the base at the faintest to the active at the brightest.
200
205
  let fills = [];
206
+ // The bloom's ink: the dots' base, at the strength the bloom is drawn at, and clear.
207
+ let bloomFrom = 'transparent';
208
+ let bloomTo = 'transparent';
201
209
  function settle() {
202
210
  tuning = { ...fromTokens, ...SETTINGS, ...overrides };
203
211
  resolved = tuning;
@@ -212,6 +220,9 @@ export function halftone(root, canvas, options) {
212
220
  const alpha = t * (from[3] + (to[3] - from[3]) * t) * Math.max(0, Math.min(1, tuning.opacity));
213
221
  return `rgba(${mix(from[0], to[0])}, ${mix(from[1], to[1])}, ${mix(from[2], to[2])}, ${alpha})`;
214
222
  });
223
+ const glow = Math.max(0, Math.min(1, tuning.bloom)) * Math.max(0, Math.min(1, tuning.opacity)) * from[3];
224
+ bloomFrom = `rgba(${from[0]}, ${from[1]}, ${from[2]}, ${glow})`;
225
+ bloomTo = `rgba(${from[0]}, ${from[1]}, ${from[2]}, 0)`;
215
226
  }
216
227
  let width = 0;
217
228
  let height = 0;
@@ -313,8 +324,27 @@ export function halftone(root, canvas, options) {
313
324
  // it the light makes the dot smaller than SMALLEST whatever else is true. Tested before the
314
325
  // exponential, which is the one costly operation per dot. Where the size floor keeps every dot
315
326
  // above SMALLEST there is no such distance, and every dot is drawn.
327
+ // The bloom: one radial wash under the glow, stretched to the glow's own ellipse.
328
+ if (tuning.bloom > 0) {
329
+ context.save();
330
+ context.translate(glowX, glowY);
331
+ context.scale(1, reachY / reachX);
332
+ const wash = context.createRadialGradient(0, 0, 0, 0, 0, reachX);
333
+ wash.addColorStop(0, bloomFrom);
334
+ wash.addColorStop(1, bloomTo);
335
+ context.fillStyle = wash;
336
+ context.fillRect(-reachX, -reachX, reachX * 2, reachX * 2);
337
+ context.restore();
338
+ }
316
339
  const sizeFloor = Math.max(0, Math.min(1, tuning.sizeFloor));
317
340
  const farthest = widest * sizeFloor >= SMALLEST ? Infinity : Math.log(widest / SMALLEST) / falloff;
341
+ // The glow's texture: a slow field of three waves at periods that do not divide into each other,
342
+ // warping each dot's distance from the glow. It moves only while the glow drifts, so a band
343
+ // asked to hold still keeps one shape. The most it can bring a dot closer is `slack`, which the
344
+ // skip tests allow for so no dot the warp would light is passed over.
345
+ const texture = Math.max(0, Math.min(1, tuning.texture));
346
+ const time = drifts() ? performance.now() / 1000 : 0;
347
+ const slack = 1 - Math.min(0.9, texture * 1.6);
318
348
  const paths = Array.from({ length: STEPS }, () => new Path2D());
319
349
  const used = new Uint8Array(STEPS);
320
350
  // The pattern's upper edge: nothing above `top`, full from `top + topFade` down, and a smooth
@@ -338,12 +368,22 @@ export function halftone(root, canvas, options) {
338
368
  const dy = (homeRowY - glowY) / reachY;
339
369
  const dy2 = dy * dy;
340
370
  // A whole row too far from the glow to show is skipped in one test.
341
- if (dy2 > farthest)
371
+ if (dy2 * slack > farthest)
342
372
  continue;
343
373
  const rowAt = row * columns;
374
+ const v = homeRowY / height;
375
+ const rowWave = Math.sin(v * 5.3 - time * 0.27);
344
376
  for (let column = 0; column < columns; column += 1) {
345
- const dx = (startX + column * pitch - glowX) / reachX;
346
- const d2 = dx * dx + dy2;
377
+ const homeColumnX = startX + column * pitch;
378
+ const dx = (homeColumnX - glowX) / reachX;
379
+ const plain = dx * dx + dy2;
380
+ if (plain * slack > farthest)
381
+ continue;
382
+ const u = homeColumnX / width;
383
+ const warp = texture === 0
384
+ ? 0
385
+ : texture * (Math.sin(u * 7.1 + time * 0.35) * rowWave + 0.6 * Math.sin((u + v) * 11.7 - time * 0.5));
386
+ const d2 = plain * (1 + warp);
347
387
  if (d2 > farthest)
348
388
  continue;
349
389
  const light = Math.exp(-d2 * falloff);
@@ -0,0 +1,66 @@
1
+ /**
2
+ * The fragment shaders behind the patterns the stylesheet can only approximate.
3
+ *
4
+ * Each is a function of where it is, the time and the light: no state, no texture, nothing to
5
+ * load. What they share — noise, a folded noise, the grain, the inks and measures read from the
6
+ * stylesheet — is in `shader.ts`. Every one ends in `grain`, which is the difference between a
7
+ * gradient and a surface: it gives the field a tooth, and dithers a long fade so it never bands.
8
+ *
9
+ * Inks arrive as `u_ink0` (the ground) to `u_ink5`, each pattern naming in the stylesheet what its
10
+ * own are, per tone. `u_strength` is how loudly it is painted on that tone.
11
+ */
12
+ /**
13
+ * The aurora: curtains of light rising from the band's foot.
14
+ *
15
+ * Sheets of noise stretched upright, swaying as a slower noise moves under them, with a ragged
16
+ * crest and fine rays running up through them — light falling through folds rather than pools of
17
+ * colour. The pointer bends the sheets towards it and brightens them where it is.
18
+ */
19
+ export declare const AURORA = "\nvoid main() {\n \n vec2 frag = gl_FragCoord.xy / u_scale;\n vec2 px = vec2(frag.x, u_size.y - frag.y);\n vec2 uv = px / u_size;\n float aspect = u_size.x / u_size.y;\n // The field's own scale, in CSS pixels, so a narrow band and a wide one show folds of a like size\n // \u2014 measured against the band's proportions, a phone-width band got one fold stretched tall. It\n // grows a little with the band, so a full-width hero is not a field of small eddies.\n vec2 field = px / (280.0 + 0.25 * max(u_size.x, u_size.y));\n vec2 lamp0 = u_pointer * u_size / (280.0 + 0.25 * max(u_size.x, u_size.y));\n\n vec2 p = vec2(field.x, uv.y);\n float t = u_time * 0.06;\n\n vec2 toward = vec2(field.x - lamp0.x, uv.y - u_pointer.y);\n float near = exp(-dot(toward, toward) * 5.0) * u_hover;\n\n float sway = fbm(vec2(p.x * 0.8 + t, t * 0.7)) - 0.5;\n float x = p.x * 1.5 + sway * 1.6 - toward.x * near * 0.8;\n\n float folds = fbm(vec2(x * 2.2, p.y * 0.35 - t * 1.6));\n float curtain = smoothstep(0.3, 0.72, folds);\n float rays = 0.6 + 0.4 * noise(vec2(x * 26.0, p.y * 1.4 - t * 3.0));\n\n float crest = 0.08 + 0.38 * fbm(vec2(x * 1.2 + 4.0, t * 0.8));\n float rise = smoothstep(crest, 0.82, uv.y);\n float base = smoothstep(0.55, 1.0, uv.y);\n\n float light = (curtain * rays * rise + base * 0.3 + near * 0.45) * u_strength;\n\n vec3 tint = mix(u_ink1, u_ink2, smoothstep(0.3, 0.8, fbm(vec2(x * 1.1 - 3.0, p.y * 0.8 + t))));\n tint = mix(tint, u_ink3, pow(clamp(curtain * rise, 0.0, 1.0), 2.5) * 0.65);\n\n vec3 colour = mix(u_ink0, tint, clamp(light, 0.0, 1.0));\n gl_FragColor = vec4(grain(colour, gl_FragCoord.xy), 1.0);\n}\n";
20
+ /**
21
+ * The mesh: the design system's own front page — five soft pools of the brand's reds, run together.
22
+ *
23
+ * The same drawing as the front page's panel and as this pattern's stylesheet version: each pool
24
+ * in its place, its red, its size, drifting and stretching on loops of its own length, and leaning
25
+ * towards the pointer at its own depth, so the colours slide through one another rather than moving
26
+ * as one. Where the stylesheet blurs five shapes, this draws each as a soft-edged pool outright —
27
+ * an uneven round whose outline wobbles as it turns — and lays them over the ground in order.
28
+ *
29
+ * The first pool is the deepest red, held back by `u_shade` so the words keep a red to stand on.
30
+ */
31
+ export declare const MESH = "\n// One pool: where it rests, in shares of the band; how big, in shares of its longer side; how far it leans after the\n// pointer; and its loops \u2014 the drift and the stretch \u2014 in seconds, offset by a phase of its own.\nfloat pool(vec2 px, vec2 at, float size, float depth, float drift, float morph, float phase) {\n // Sized by the band's longer side rather than its width: on a phone the band is tall and narrow,\n // and pools sized by its width came out small \u2014 the deepest a hard, dark blot in one corner and\n // the rest of the band flat. Measured against the longer side they cover a tall band as they do\n // a wide one.\n float w = max(u_size.x, u_size.y);\n float t = u_time;\n float tau = 6.2831853;\n\n // The drift: a slow wander and a swell, there and back.\n vec2 wander = vec2(\n 0.10 * sin(tau * t / drift + phase),\n 0.08 * sin(tau * t / (drift * 1.3) + phase * 1.7)\n ) * size * w;\n float swell = 1.0 + 0.1 * sin(tau * t / (drift * 0.8) + phase * 0.6);\n\n // Leaning after the pointer by its depth, a hundredth of the pool per step, as the front page's do.\n vec2 lean = (u_pointer - 0.5) * 2.0 * u_hover * depth * 0.01 * size * w;\n\n vec2 centre = at * u_size + wander + lean;\n vec2 d = px - centre;\n\n // The stretch: one way then the other, turning as it goes.\n float turn = 1.4 * sin(tau * t / (morph * 2.0) + phase);\n float c = cos(turn);\n float s = sin(turn);\n d = mat2(c, -s, s, c) * d;\n float stretch = sin(tau * t / morph + phase);\n d /= vec2(1.0 + 0.18 * stretch, 1.0 - 0.15 * stretch);\n\n // An uneven round, its outline wobbling as it turns.\n float angle = atan(d.y, d.x);\n float radius = size * w * 0.5 * swell * (1.0 + 0.1 * sin(3.0 * angle + phase) + 0.06 * sin(5.0 * angle - phase * 2.0));\n float r = length(d) / radius;\n // Soft all the way out: a blur's falloff rather than an edge.\n return exp(-r * r * 1.1);\n}\n\nvoid main() {\n \n vec2 frag = gl_FragCoord.xy / u_scale;\n vec2 px = vec2(frag.x, u_size.y - frag.y);\n vec2 uv = px / u_size;\n float aspect = u_size.x / u_size.y;\n // The field's own scale, in CSS pixels, so a narrow band and a wide one show folds of a like size\n // \u2014 measured against the band's proportions, a phone-width band got one fold stretched tall. It\n // grows a little with the band, so a full-width hero is not a field of small eddies.\n vec2 field = px / (280.0 + 0.25 * max(u_size.x, u_size.y));\n vec2 lamp0 = u_pointer * u_size / (280.0 + 0.25 * max(u_size.x, u_size.y));\n\n vec3 c = u_ink0;\n // The deepest red, off the near side low down \u2014 a shade under the words, held back.\n c = mix(c, u_ink1, pool(px, vec2(0.06, 0.82), 0.62, -8.0, 26.0, 13.0, 0.0) * u_shade * u_strength);\n // The pressed red, high in the far corner, the largest.\n c = mix(c, u_ink2, pool(px, vec2(0.92, -0.08), 0.85, 10.0, 31.0, 9.0, 1.7) * u_strength);\n // The hover red, through the middle.\n c = mix(c, u_ink3, pool(px, vec2(0.6, 0.5), 0.62, 16.0, 22.0, 7.0, 3.1) * u_strength);\n // The pressed red again, swelling up from the foot.\n c = mix(c, u_ink4, pool(px, vec2(0.5, 1.1), 0.75, -12.0, 35.0, 12.0, 4.4) * u_strength);\n // The coral itself, high in the far corner: the brightest the pools go.\n c = mix(c, u_ink5, pool(px, vec2(0.74, 0.06), 0.42, 20.0, 28.0, 8.0, 5.9) * u_strength);\n\n // A little more colour, as the front page's blur saturates its pools.\n float grey = dot(c, vec3(0.299, 0.587, 0.114));\n c = mix(vec3(grey), c, 1.15);\n\n gl_FragColor = vec4(grain(clamp(c, 0.0, 1.0), gl_FragCoord.xy), 1.0);\n}\n";
32
+ /**
33
+ * The grid: hairlines a pitch apart, rising from the foot as they did, now lit.
34
+ *
35
+ * A bloom at the foot they rise out of, a brighter point where two lines cross, and every few
36
+ * lines a short dash of light running along it — up a rule, across another — at a pace of its
37
+ * own, so the grid reads as carrying something. The pointer lights the lines around it.
38
+ */
39
+ export declare const GRID = "\n\nfloat hairline(float distance) {\n float halfWidth = u_hairline * 0.5;\n float pixel = 1.0 / u_scale;\n return 1.0 - smoothstep(halfWidth, halfWidth + pixel, distance);\n}\n\n// A soft bloom at the middle of the band's foot \u2014 where the halftone's glow rests \u2014 so the lines\n// rise out of light rather than out of a flat ground.\nfloat bloom(vec2 uv) {\n vec2 d = vec2((uv.x - 0.5) * 1.5, (1.0 - uv.y) * 2.2);\n return exp(-dot(d, d));\n}\n\n// The pointer's light, a soft circle a pitch and a half across.\nfloat lamp(vec2 px) {\n vec2 d = px - u_pointer * u_size;\n float reach = u_pitch * 1.6;\n return exp(-dot(d, d) / (reach * reach)) * u_hover;\n}\n\n// A short bright dash running along a line, at a place along it, fading off behind.\nfloat dash(float along) {\n return smoothstep(0.0, 0.06, along) * (1.0 - smoothstep(0.06, 0.3, along));\n}\n\nvoid main() {\n \n vec2 frag = gl_FragCoord.xy / u_scale;\n vec2 px = vec2(frag.x, u_size.y - frag.y);\n vec2 uv = px / u_size;\n float aspect = u_size.x / u_size.y;\n // The field's own scale, in CSS pixels, so a narrow band and a wide one show folds of a like size\n // \u2014 measured against the band's proportions, a phone-width band got one fold stretched tall. It\n // grows a little with the band, so a full-width hero is not a field of small eddies.\n vec2 field = px / (280.0 + 0.25 * max(u_size.x, u_size.y));\n vec2 lamp0 = u_pointer * u_size / (280.0 + 0.25 * max(u_size.x, u_size.y));\n\n float pitch = u_pitch;\n float shift = mod(u_time / 10.0, 1.0) * pitch;\n vec2 g = vec2(px.x - u_size.x * 0.5 + pitch * 0.5, px.y + shift);\n vec2 d = abs(fract(g / pitch) - 0.5) * pitch;\n vec2 lines = vec2(hairline(pitch * 0.5 - d.x), hairline(pitch * 0.5 - d.y));\n float line = max(lines.x, lines.y);\n float meet = lines.x * lines.y;\n\n vec2 cell = floor(g / pitch);\n float upward = fract(px.y / (pitch * 7.0) + u_time * 0.07 + hash(vec2(cell.x, 3.1)));\n float across = fract(px.x / (pitch * 9.0) - u_time * 0.05 + hash(vec2(cell.y, 7.7)));\n float pulses =\n lines.x * dash(upward) * step(0.7, hash(vec2(cell.x, 9.7))) +\n lines.y * dash(across) * step(0.78, hash(vec2(cell.y, 4.2)));\n\n float fade = smoothstep(0.0, 1.0, uv.y);\n float glow = bloom(uv);\n float lit = lamp(px);\n\n vec3 c = mix(u_ink0, u_ink3, glow * 0.45 * u_strength);\n c = mix(c, u_ink1, line * fade * (0.55 + 0.45 * glow) * u_strength);\n c = mix(c, u_ink2, clamp(pulses * fade + line * lit + meet * fade * 0.35, 0.0, 1.0) * u_strength);\n\n gl_FragColor = vec4(grain(c, gl_FragCoord.xy), 1.0);\n}\n";
40
+ /**
41
+ * The rings: circles spreading from the middle of the band's foot, now lit.
42
+ *
43
+ * The same bloom at the foot, and on every ring a few arcs of light travelling round it, each ring
44
+ * its own way and pace, so the rings shimmer as they spread. The pointer lights the rings around it.
45
+ */
46
+ export declare const RINGS = "\n\nfloat hairline(float distance) {\n float halfWidth = u_hairline * 0.5;\n float pixel = 1.0 / u_scale;\n return 1.0 - smoothstep(halfWidth, halfWidth + pixel, distance);\n}\n\n// A soft bloom at the middle of the band's foot \u2014 where the halftone's glow rests \u2014 so the lines\n// rise out of light rather than out of a flat ground.\nfloat bloom(vec2 uv) {\n vec2 d = vec2((uv.x - 0.5) * 1.5, (1.0 - uv.y) * 2.2);\n return exp(-dot(d, d));\n}\n\n// The pointer's light, a soft circle a pitch and a half across.\nfloat lamp(vec2 px) {\n vec2 d = px - u_pointer * u_size;\n float reach = u_pitch * 1.6;\n return exp(-dot(d, d) / (reach * reach)) * u_hover;\n}\n\n// A short bright dash running along a line, at a place along it, fading off behind.\nfloat dash(float along) {\n return smoothstep(0.0, 0.06, along) * (1.0 - smoothstep(0.06, 0.3, along));\n}\n\nvoid main() {\n \n vec2 frag = gl_FragCoord.xy / u_scale;\n vec2 px = vec2(frag.x, u_size.y - frag.y);\n vec2 uv = px / u_size;\n float aspect = u_size.x / u_size.y;\n // The field's own scale, in CSS pixels, so a narrow band and a wide one show folds of a like size\n // \u2014 measured against the band's proportions, a phone-width band got one fold stretched tall. It\n // grows a little with the band, so a full-width hero is not a field of small eddies.\n vec2 field = px / (280.0 + 0.25 * max(u_size.x, u_size.y));\n vec2 lamp0 = u_pointer * u_size / (280.0 + 0.25 * max(u_size.x, u_size.y));\n\n float pitch = u_pitch;\n vec2 centre = vec2(u_size.x * 0.5, u_size.y);\n vec2 from = px - centre;\n float radius = length(from);\n float shift = mod(u_time / 10.0, 1.0) * pitch;\n float place = (radius - shift) / pitch;\n float d = abs(fract(place) - 0.5) * pitch;\n float line = hairline(pitch * 0.5 - d);\n\n float ring = floor(place);\n float angle = atan(from.x, -from.y);\n float turn = (hash(vec2(ring, 1.7)) > 0.5 ? 1.0 : -1.0) * (0.15 + 0.25 * hash(vec2(ring, 5.3)));\n float arc = noise(vec2(angle * 3.0 + u_time * turn, ring * 3.1));\n float sparkle = line * smoothstep(0.62, 0.9, arc);\n\n float fade = smoothstep(0.0, 1.0, uv.y);\n float glow = bloom(uv);\n float lit = lamp(px);\n\n vec3 c = mix(u_ink0, u_ink3, glow * 0.45 * u_strength);\n c = mix(c, u_ink1, line * fade * (0.55 + 0.45 * glow) * u_strength);\n c = mix(c, u_ink2, clamp(sparkle * fade + line * lit, 0.0, 1.0) * u_strength);\n\n gl_FragColor = vec4(grain(c, gl_FragCoord.xy), 1.0);\n}\n";
47
+ /** Each pattern's shader and the highest density it draws at: soft fields need little, hairlines two. */
48
+ export declare const SHADERS: {
49
+ readonly aurora: {
50
+ readonly fragment: "\nvoid main() {\n \n vec2 frag = gl_FragCoord.xy / u_scale;\n vec2 px = vec2(frag.x, u_size.y - frag.y);\n vec2 uv = px / u_size;\n float aspect = u_size.x / u_size.y;\n // The field's own scale, in CSS pixels, so a narrow band and a wide one show folds of a like size\n // — measured against the band's proportions, a phone-width band got one fold stretched tall. It\n // grows a little with the band, so a full-width hero is not a field of small eddies.\n vec2 field = px / (280.0 + 0.25 * max(u_size.x, u_size.y));\n vec2 lamp0 = u_pointer * u_size / (280.0 + 0.25 * max(u_size.x, u_size.y));\n\n vec2 p = vec2(field.x, uv.y);\n float t = u_time * 0.06;\n\n vec2 toward = vec2(field.x - lamp0.x, uv.y - u_pointer.y);\n float near = exp(-dot(toward, toward) * 5.0) * u_hover;\n\n float sway = fbm(vec2(p.x * 0.8 + t, t * 0.7)) - 0.5;\n float x = p.x * 1.5 + sway * 1.6 - toward.x * near * 0.8;\n\n float folds = fbm(vec2(x * 2.2, p.y * 0.35 - t * 1.6));\n float curtain = smoothstep(0.3, 0.72, folds);\n float rays = 0.6 + 0.4 * noise(vec2(x * 26.0, p.y * 1.4 - t * 3.0));\n\n float crest = 0.08 + 0.38 * fbm(vec2(x * 1.2 + 4.0, t * 0.8));\n float rise = smoothstep(crest, 0.82, uv.y);\n float base = smoothstep(0.55, 1.0, uv.y);\n\n float light = (curtain * rays * rise + base * 0.3 + near * 0.45) * u_strength;\n\n vec3 tint = mix(u_ink1, u_ink2, smoothstep(0.3, 0.8, fbm(vec2(x * 1.1 - 3.0, p.y * 0.8 + t))));\n tint = mix(tint, u_ink3, pow(clamp(curtain * rise, 0.0, 1.0), 2.5) * 0.65);\n\n vec3 colour = mix(u_ink0, tint, clamp(light, 0.0, 1.0));\n gl_FragColor = vec4(grain(colour, gl_FragCoord.xy), 1.0);\n}\n";
51
+ readonly density: 0.5;
52
+ };
53
+ readonly mesh: {
54
+ readonly fragment: "\n// One pool: where it rests, in shares of the band; how big, in shares of its longer side; how far it leans after the\n// pointer; and its loops — the drift and the stretch — in seconds, offset by a phase of its own.\nfloat pool(vec2 px, vec2 at, float size, float depth, float drift, float morph, float phase) {\n // Sized by the band's longer side rather than its width: on a phone the band is tall and narrow,\n // and pools sized by its width came out small — the deepest a hard, dark blot in one corner and\n // the rest of the band flat. Measured against the longer side they cover a tall band as they do\n // a wide one.\n float w = max(u_size.x, u_size.y);\n float t = u_time;\n float tau = 6.2831853;\n\n // The drift: a slow wander and a swell, there and back.\n vec2 wander = vec2(\n 0.10 * sin(tau * t / drift + phase),\n 0.08 * sin(tau * t / (drift * 1.3) + phase * 1.7)\n ) * size * w;\n float swell = 1.0 + 0.1 * sin(tau * t / (drift * 0.8) + phase * 0.6);\n\n // Leaning after the pointer by its depth, a hundredth of the pool per step, as the front page's do.\n vec2 lean = (u_pointer - 0.5) * 2.0 * u_hover * depth * 0.01 * size * w;\n\n vec2 centre = at * u_size + wander + lean;\n vec2 d = px - centre;\n\n // The stretch: one way then the other, turning as it goes.\n float turn = 1.4 * sin(tau * t / (morph * 2.0) + phase);\n float c = cos(turn);\n float s = sin(turn);\n d = mat2(c, -s, s, c) * d;\n float stretch = sin(tau * t / morph + phase);\n d /= vec2(1.0 + 0.18 * stretch, 1.0 - 0.15 * stretch);\n\n // An uneven round, its outline wobbling as it turns.\n float angle = atan(d.y, d.x);\n float radius = size * w * 0.5 * swell * (1.0 + 0.1 * sin(3.0 * angle + phase) + 0.06 * sin(5.0 * angle - phase * 2.0));\n float r = length(d) / radius;\n // Soft all the way out: a blur's falloff rather than an edge.\n return exp(-r * r * 1.1);\n}\n\nvoid main() {\n \n vec2 frag = gl_FragCoord.xy / u_scale;\n vec2 px = vec2(frag.x, u_size.y - frag.y);\n vec2 uv = px / u_size;\n float aspect = u_size.x / u_size.y;\n // The field's own scale, in CSS pixels, so a narrow band and a wide one show folds of a like size\n // — measured against the band's proportions, a phone-width band got one fold stretched tall. It\n // grows a little with the band, so a full-width hero is not a field of small eddies.\n vec2 field = px / (280.0 + 0.25 * max(u_size.x, u_size.y));\n vec2 lamp0 = u_pointer * u_size / (280.0 + 0.25 * max(u_size.x, u_size.y));\n\n vec3 c = u_ink0;\n // The deepest red, off the near side low down — a shade under the words, held back.\n c = mix(c, u_ink1, pool(px, vec2(0.06, 0.82), 0.62, -8.0, 26.0, 13.0, 0.0) * u_shade * u_strength);\n // The pressed red, high in the far corner, the largest.\n c = mix(c, u_ink2, pool(px, vec2(0.92, -0.08), 0.85, 10.0, 31.0, 9.0, 1.7) * u_strength);\n // The hover red, through the middle.\n c = mix(c, u_ink3, pool(px, vec2(0.6, 0.5), 0.62, 16.0, 22.0, 7.0, 3.1) * u_strength);\n // The pressed red again, swelling up from the foot.\n c = mix(c, u_ink4, pool(px, vec2(0.5, 1.1), 0.75, -12.0, 35.0, 12.0, 4.4) * u_strength);\n // The coral itself, high in the far corner: the brightest the pools go.\n c = mix(c, u_ink5, pool(px, vec2(0.74, 0.06), 0.42, 20.0, 28.0, 8.0, 5.9) * u_strength);\n\n // A little more colour, as the front page's blur saturates its pools.\n float grey = dot(c, vec3(0.299, 0.587, 0.114));\n c = mix(vec3(grey), c, 1.15);\n\n gl_FragColor = vec4(grain(clamp(c, 0.0, 1.0), gl_FragCoord.xy), 1.0);\n}\n";
55
+ readonly density: 0.5;
56
+ };
57
+ readonly grid: {
58
+ readonly fragment: "\n\nfloat hairline(float distance) {\n float halfWidth = u_hairline * 0.5;\n float pixel = 1.0 / u_scale;\n return 1.0 - smoothstep(halfWidth, halfWidth + pixel, distance);\n}\n\n// A soft bloom at the middle of the band's foot — where the halftone's glow rests — so the lines\n// rise out of light rather than out of a flat ground.\nfloat bloom(vec2 uv) {\n vec2 d = vec2((uv.x - 0.5) * 1.5, (1.0 - uv.y) * 2.2);\n return exp(-dot(d, d));\n}\n\n// The pointer's light, a soft circle a pitch and a half across.\nfloat lamp(vec2 px) {\n vec2 d = px - u_pointer * u_size;\n float reach = u_pitch * 1.6;\n return exp(-dot(d, d) / (reach * reach)) * u_hover;\n}\n\n// A short bright dash running along a line, at a place along it, fading off behind.\nfloat dash(float along) {\n return smoothstep(0.0, 0.06, along) * (1.0 - smoothstep(0.06, 0.3, along));\n}\n\nvoid main() {\n \n vec2 frag = gl_FragCoord.xy / u_scale;\n vec2 px = vec2(frag.x, u_size.y - frag.y);\n vec2 uv = px / u_size;\n float aspect = u_size.x / u_size.y;\n // The field's own scale, in CSS pixels, so a narrow band and a wide one show folds of a like size\n // — measured against the band's proportions, a phone-width band got one fold stretched tall. It\n // grows a little with the band, so a full-width hero is not a field of small eddies.\n vec2 field = px / (280.0 + 0.25 * max(u_size.x, u_size.y));\n vec2 lamp0 = u_pointer * u_size / (280.0 + 0.25 * max(u_size.x, u_size.y));\n\n float pitch = u_pitch;\n float shift = mod(u_time / 10.0, 1.0) * pitch;\n vec2 g = vec2(px.x - u_size.x * 0.5 + pitch * 0.5, px.y + shift);\n vec2 d = abs(fract(g / pitch) - 0.5) * pitch;\n vec2 lines = vec2(hairline(pitch * 0.5 - d.x), hairline(pitch * 0.5 - d.y));\n float line = max(lines.x, lines.y);\n float meet = lines.x * lines.y;\n\n vec2 cell = floor(g / pitch);\n float upward = fract(px.y / (pitch * 7.0) + u_time * 0.07 + hash(vec2(cell.x, 3.1)));\n float across = fract(px.x / (pitch * 9.0) - u_time * 0.05 + hash(vec2(cell.y, 7.7)));\n float pulses =\n lines.x * dash(upward) * step(0.7, hash(vec2(cell.x, 9.7))) +\n lines.y * dash(across) * step(0.78, hash(vec2(cell.y, 4.2)));\n\n float fade = smoothstep(0.0, 1.0, uv.y);\n float glow = bloom(uv);\n float lit = lamp(px);\n\n vec3 c = mix(u_ink0, u_ink3, glow * 0.45 * u_strength);\n c = mix(c, u_ink1, line * fade * (0.55 + 0.45 * glow) * u_strength);\n c = mix(c, u_ink2, clamp(pulses * fade + line * lit + meet * fade * 0.35, 0.0, 1.0) * u_strength);\n\n gl_FragColor = vec4(grain(c, gl_FragCoord.xy), 1.0);\n}\n";
59
+ readonly density: 2;
60
+ };
61
+ readonly rings: {
62
+ readonly fragment: "\n\nfloat hairline(float distance) {\n float halfWidth = u_hairline * 0.5;\n float pixel = 1.0 / u_scale;\n return 1.0 - smoothstep(halfWidth, halfWidth + pixel, distance);\n}\n\n// A soft bloom at the middle of the band's foot — where the halftone's glow rests — so the lines\n// rise out of light rather than out of a flat ground.\nfloat bloom(vec2 uv) {\n vec2 d = vec2((uv.x - 0.5) * 1.5, (1.0 - uv.y) * 2.2);\n return exp(-dot(d, d));\n}\n\n// The pointer's light, a soft circle a pitch and a half across.\nfloat lamp(vec2 px) {\n vec2 d = px - u_pointer * u_size;\n float reach = u_pitch * 1.6;\n return exp(-dot(d, d) / (reach * reach)) * u_hover;\n}\n\n// A short bright dash running along a line, at a place along it, fading off behind.\nfloat dash(float along) {\n return smoothstep(0.0, 0.06, along) * (1.0 - smoothstep(0.06, 0.3, along));\n}\n\nvoid main() {\n \n vec2 frag = gl_FragCoord.xy / u_scale;\n vec2 px = vec2(frag.x, u_size.y - frag.y);\n vec2 uv = px / u_size;\n float aspect = u_size.x / u_size.y;\n // The field's own scale, in CSS pixels, so a narrow band and a wide one show folds of a like size\n // — measured against the band's proportions, a phone-width band got one fold stretched tall. It\n // grows a little with the band, so a full-width hero is not a field of small eddies.\n vec2 field = px / (280.0 + 0.25 * max(u_size.x, u_size.y));\n vec2 lamp0 = u_pointer * u_size / (280.0 + 0.25 * max(u_size.x, u_size.y));\n\n float pitch = u_pitch;\n vec2 centre = vec2(u_size.x * 0.5, u_size.y);\n vec2 from = px - centre;\n float radius = length(from);\n float shift = mod(u_time / 10.0, 1.0) * pitch;\n float place = (radius - shift) / pitch;\n float d = abs(fract(place) - 0.5) * pitch;\n float line = hairline(pitch * 0.5 - d);\n\n float ring = floor(place);\n float angle = atan(from.x, -from.y);\n float turn = (hash(vec2(ring, 1.7)) > 0.5 ? 1.0 : -1.0) * (0.15 + 0.25 * hash(vec2(ring, 5.3)));\n float arc = noise(vec2(angle * 3.0 + u_time * turn, ring * 3.1));\n float sparkle = line * smoothstep(0.62, 0.9, arc);\n\n float fade = smoothstep(0.0, 1.0, uv.y);\n float glow = bloom(uv);\n float lit = lamp(px);\n\n vec3 c = mix(u_ink0, u_ink3, glow * 0.45 * u_strength);\n c = mix(c, u_ink1, line * fade * (0.55 + 0.45 * glow) * u_strength);\n c = mix(c, u_ink2, clamp(sparkle * fade + line * lit, 0.0, 1.0) * u_strength);\n\n gl_FragColor = vec4(grain(c, gl_FragCoord.xy), 1.0);\n}\n";
63
+ readonly density: 2;
64
+ };
65
+ };
66
+ export type ShadedPattern = keyof typeof SHADERS;