@autoaiai/aicut 0.1.7 → 0.1.13

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 (306) hide show
  1. package/MANUAL.md +94 -84
  2. package/package.json +1 -1
  3. package/product.json +2 -1
  4. package/skills/aicut/SKILL.md +4 -4
  5. package/skills/aicut/references/commands.md +15 -15
  6. package/skills/aicut/references/film-format.md +1 -1
  7. package/skills/aicut/references/recipes.md +4 -4
  8. package/skills/aicut/references/troubleshooting.md +2 -2
  9. package/skills/aicut-motion/SKILL.md +75 -9
  10. package/skills/aicut-motion/THIRD_PARTY_NOTICES.md +14 -0
  11. package/skills/aicut-motion/assets/atlas/lib/harness.js +116 -0
  12. package/skills/aicut-motion/assets/atlas/lib/sound.js +274 -0
  13. package/skills/aicut-motion/assets/atlas/styles/001-flat-vector-science/code.js +937 -0
  14. package/skills/aicut-motion/assets/atlas/styles/001-flat-vector-science/poster.jpg +0 -0
  15. package/skills/aicut-motion/assets/atlas/styles/001-flat-vector-science/style.json +13 -0
  16. package/skills/aicut-motion/assets/atlas/styles/002-watercolor-wash/code.js +909 -0
  17. package/skills/aicut-motion/assets/atlas/styles/002-watercolor-wash/poster.jpg +0 -0
  18. package/skills/aicut-motion/assets/atlas/styles/002-watercolor-wash/style.json +13 -0
  19. package/skills/aicut-motion/assets/atlas/styles/003-shadow-puppet-theatre/code.js +881 -0
  20. package/skills/aicut-motion/assets/atlas/styles/003-shadow-puppet-theatre/poster.jpg +0 -0
  21. package/skills/aicut-motion/assets/atlas/styles/003-shadow-puppet-theatre/style.json +13 -0
  22. package/skills/aicut-motion/assets/atlas/styles/004-whiteboard-explainer/code.js +897 -0
  23. package/skills/aicut-motion/assets/atlas/styles/004-whiteboard-explainer/poster.jpg +0 -0
  24. package/skills/aicut-motion/assets/atlas/styles/004-whiteboard-explainer/style.json +13 -0
  25. package/skills/aicut-motion/assets/atlas/styles/005-psychedelic-poster/code.js +814 -0
  26. package/skills/aicut-motion/assets/atlas/styles/005-psychedelic-poster/poster.jpg +0 -0
  27. package/skills/aicut-motion/assets/atlas/styles/005-psychedelic-poster/style.json +13 -0
  28. package/skills/aicut-motion/assets/atlas/styles/006-tech-glow-explainer/code.js +990 -0
  29. package/skills/aicut-motion/assets/atlas/styles/006-tech-glow-explainer/poster.jpg +0 -0
  30. package/skills/aicut-motion/assets/atlas/styles/006-tech-glow-explainer/style.json +13 -0
  31. package/skills/aicut-motion/assets/atlas/styles/007-papercraft-models/code.js +994 -0
  32. package/skills/aicut-motion/assets/atlas/styles/007-papercraft-models/poster.jpg +0 -0
  33. package/skills/aicut-motion/assets/atlas/styles/007-papercraft-models/style.json +13 -0
  34. package/skills/aicut-motion/assets/atlas/styles/008-1940s-film-noir/code.js +792 -0
  35. package/skills/aicut-motion/assets/atlas/styles/008-1940s-film-noir/poster.jpg +0 -0
  36. package/skills/aicut-motion/assets/atlas/styles/008-1940s-film-noir/style.json +13 -0
  37. package/skills/aicut-motion/assets/atlas/styles/009-manim-style-math/code.js +840 -0
  38. package/skills/aicut-motion/assets/atlas/styles/009-manim-style-math/poster.jpg +0 -0
  39. package/skills/aicut-motion/assets/atlas/styles/009-manim-style-math/style.json +13 -0
  40. package/skills/aicut-motion/assets/atlas/styles/010-illuminated-manuscript/code.js +1126 -0
  41. package/skills/aicut-motion/assets/atlas/styles/010-illuminated-manuscript/poster.jpg +0 -0
  42. package/skills/aicut-motion/assets/atlas/styles/010-illuminated-manuscript/style.json +13 -0
  43. package/skills/aicut-motion/assets/atlas/styles/011-editorial-essay-collage/code.js +854 -0
  44. package/skills/aicut-motion/assets/atlas/styles/011-editorial-essay-collage/poster.jpg +0 -0
  45. package/skills/aicut-motion/assets/atlas/styles/011-editorial-essay-collage/style.json +13 -0
  46. package/skills/aicut-motion/assets/atlas/styles/012-bioluminescent-deep-sea/code.js +1124 -0
  47. package/skills/aicut-motion/assets/atlas/styles/012-bioluminescent-deep-sea/poster.jpg +0 -0
  48. package/skills/aicut-motion/assets/atlas/styles/012-bioluminescent-deep-sea/style.json +13 -0
  49. package/skills/aicut-motion/assets/atlas/styles/013-product-launch-reveal/code.js +728 -0
  50. package/skills/aicut-motion/assets/atlas/styles/013-product-launch-reveal/poster.jpg +0 -0
  51. package/skills/aicut-motion/assets/atlas/styles/013-product-launch-reveal/style.json +13 -0
  52. package/skills/aicut-motion/assets/atlas/styles/014-halftone-pop-panel/code.js +664 -0
  53. package/skills/aicut-motion/assets/atlas/styles/014-halftone-pop-panel/poster.jpg +0 -0
  54. package/skills/aicut-motion/assets/atlas/styles/014-halftone-pop-panel/style.json +13 -0
  55. package/skills/aicut-motion/assets/atlas/styles/015-isometric-pastel/code.js +991 -0
  56. package/skills/aicut-motion/assets/atlas/styles/015-isometric-pastel/poster.jpg +0 -0
  57. package/skills/aicut-motion/assets/atlas/styles/015-isometric-pastel/style.json +13 -0
  58. package/skills/aicut-motion/assets/atlas/styles/016-oscilloscope-vector-art/code.js +801 -0
  59. package/skills/aicut-motion/assets/atlas/styles/016-oscilloscope-vector-art/poster.jpg +0 -0
  60. package/skills/aicut-motion/assets/atlas/styles/016-oscilloscope-vector-art/style.json +13 -0
  61. package/skills/aicut-motion/assets/atlas/styles/017-risograph-zine/code.js +692 -0
  62. package/skills/aicut-motion/assets/atlas/styles/017-risograph-zine/poster.jpg +0 -0
  63. package/skills/aicut-motion/assets/atlas/styles/017-risograph-zine/style.json +13 -0
  64. package/skills/aicut-motion/assets/atlas/styles/018-claymation-set/code.js +756 -0
  65. package/skills/aicut-motion/assets/atlas/styles/018-claymation-set/poster.jpg +0 -0
  66. package/skills/aicut-motion/assets/atlas/styles/018-claymation-set/style.json +13 -0
  67. package/skills/aicut-motion/assets/atlas/styles/019-neon-sign-at-night/code.js +922 -0
  68. package/skills/aicut-motion/assets/atlas/styles/019-neon-sign-at-night/poster.jpg +0 -0
  69. package/skills/aicut-motion/assets/atlas/styles/019-neon-sign-at-night/style.json +13 -0
  70. package/skills/aicut-motion/assets/atlas/styles/020-kawaii-anime/code.js +852 -0
  71. package/skills/aicut-motion/assets/atlas/styles/020-kawaii-anime/poster.jpg +0 -0
  72. package/skills/aicut-motion/assets/atlas/styles/020-kawaii-anime/style.json +13 -0
  73. package/skills/aicut-motion/assets/atlas/styles/021-ukiyo-e-woodblock/code.js +724 -0
  74. package/skills/aicut-motion/assets/atlas/styles/021-ukiyo-e-woodblock/poster.jpg +0 -0
  75. package/skills/aicut-motion/assets/atlas/styles/021-ukiyo-e-woodblock/style.json +13 -0
  76. package/skills/aicut-motion/assets/atlas/styles/022-16-bit-pixel-odyssey/code.js +793 -0
  77. package/skills/aicut-motion/assets/atlas/styles/022-16-bit-pixel-odyssey/poster.jpg +0 -0
  78. package/skills/aicut-motion/assets/atlas/styles/022-16-bit-pixel-odyssey/style.json +13 -0
  79. package/skills/aicut-motion/assets/atlas/styles/023-chalkboard-lesson/code.js +846 -0
  80. package/skills/aicut-motion/assets/atlas/styles/023-chalkboard-lesson/poster.jpg +0 -0
  81. package/skills/aicut-motion/assets/atlas/styles/023-chalkboard-lesson/style.json +13 -0
  82. package/skills/aicut-motion/assets/atlas/styles/024-cut-paper-diorama/code.js +1008 -0
  83. package/skills/aicut-motion/assets/atlas/styles/024-cut-paper-diorama/poster.jpg +0 -0
  84. package/skills/aicut-motion/assets/atlas/styles/024-cut-paper-diorama/style.json +13 -0
  85. package/skills/aicut-motion/assets/atlas/styles/025-cathedral-glass/code.js +680 -0
  86. package/skills/aicut-motion/assets/atlas/styles/025-cathedral-glass/poster.jpg +0 -0
  87. package/skills/aicut-motion/assets/atlas/styles/025-cathedral-glass/style.json +13 -0
  88. package/skills/aicut-motion/assets/atlas/styles/026-editorial-chart/code.js +633 -0
  89. package/skills/aicut-motion/assets/atlas/styles/026-editorial-chart/poster.jpg +0 -0
  90. package/skills/aicut-motion/assets/atlas/styles/026-editorial-chart/style.json +13 -0
  91. package/skills/aicut-motion/assets/atlas/styles/027-synthwave-outrun/code.js +757 -0
  92. package/skills/aicut-motion/assets/atlas/styles/027-synthwave-outrun/poster.jpg +0 -0
  93. package/skills/aicut-motion/assets/atlas/styles/027-synthwave-outrun/style.json +13 -0
  94. package/skills/aicut-motion/assets/atlas/styles/028-origami-studio/code.js +828 -0
  95. package/skills/aicut-motion/assets/atlas/styles/028-origami-studio/poster.jpg +0 -0
  96. package/skills/aicut-motion/assets/atlas/styles/028-origami-studio/style.json +13 -0
  97. package/skills/aicut-motion/assets/atlas/styles/029-linocut-relief-print/code.js +697 -0
  98. package/skills/aicut-motion/assets/atlas/styles/029-linocut-relief-print/poster.jpg +0 -0
  99. package/skills/aicut-motion/assets/atlas/styles/029-linocut-relief-print/style.json +13 -0
  100. package/skills/aicut-motion/assets/atlas/styles/030-holographic-foil-card/code.js +687 -0
  101. package/skills/aicut-motion/assets/atlas/styles/030-holographic-foil-card/poster.jpg +0 -0
  102. package/skills/aicut-motion/assets/atlas/styles/030-holographic-foil-card/style.json +13 -0
  103. package/skills/aicut-motion/assets/atlas/styles/031-voxel-diorama/code.js +727 -0
  104. package/skills/aicut-motion/assets/atlas/styles/031-voxel-diorama/poster.jpg +0 -0
  105. package/skills/aicut-motion/assets/atlas/styles/031-voxel-diorama/style.json +13 -0
  106. package/skills/aicut-motion/assets/atlas/styles/032-sumi-e-ink-wash/code.js +903 -0
  107. package/skills/aicut-motion/assets/atlas/styles/032-sumi-e-ink-wash/poster.jpg +0 -0
  108. package/skills/aicut-motion/assets/atlas/styles/032-sumi-e-ink-wash/style.json +13 -0
  109. package/skills/aicut-motion/assets/atlas/styles/033-etch-a-sketch-toy/code.js +662 -0
  110. package/skills/aicut-motion/assets/atlas/styles/033-etch-a-sketch-toy/poster.jpg +0 -0
  111. package/skills/aicut-motion/assets/atlas/styles/033-etch-a-sketch-toy/style.json +13 -0
  112. package/skills/aicut-motion/assets/atlas/styles/034-cosmic-star-chart/code.js +951 -0
  113. package/skills/aicut-motion/assets/atlas/styles/034-cosmic-star-chart/poster.jpg +0 -0
  114. package/skills/aicut-motion/assets/atlas/styles/034-cosmic-star-chart/style.json +13 -0
  115. package/skills/aicut-motion/assets/atlas/styles/035-punk-zine-collage/code.js +759 -0
  116. package/skills/aicut-motion/assets/atlas/styles/035-punk-zine-collage/poster.jpg +0 -0
  117. package/skills/aicut-motion/assets/atlas/styles/035-punk-zine-collage/style.json +13 -0
  118. package/skills/aicut-motion/assets/atlas/styles/036-thermal-camera/code.js +738 -0
  119. package/skills/aicut-motion/assets/atlas/styles/036-thermal-camera/poster.jpg +0 -0
  120. package/skills/aicut-motion/assets/atlas/styles/036-thermal-camera/style.json +13 -0
  121. package/skills/aicut-motion/assets/atlas/styles/037-engineering-blueprint/code.js +793 -0
  122. package/skills/aicut-motion/assets/atlas/styles/037-engineering-blueprint/poster.jpg +0 -0
  123. package/skills/aicut-motion/assets/atlas/styles/037-engineering-blueprint/style.json +13 -0
  124. package/skills/aicut-motion/assets/atlas/styles/038-impasto-night/code.js +673 -0
  125. package/skills/aicut-motion/assets/atlas/styles/038-impasto-night/poster.jpg +0 -0
  126. package/skills/aicut-motion/assets/atlas/styles/038-impasto-night/style.json +13 -0
  127. package/skills/aicut-motion/assets/atlas/styles/039-win95-desktop/code.js +907 -0
  128. package/skills/aicut-motion/assets/atlas/styles/039-win95-desktop/poster.jpg +0 -0
  129. package/skills/aicut-motion/assets/atlas/styles/039-win95-desktop/style.json +13 -0
  130. package/skills/aicut-motion/assets/atlas/styles/040-low-poly-faceted/code.js +833 -0
  131. package/skills/aicut-motion/assets/atlas/styles/040-low-poly-faceted/poster.jpg +0 -0
  132. package/skills/aicut-motion/assets/atlas/styles/040-low-poly-faceted/style.json +13 -0
  133. package/skills/aicut-motion/assets/atlas/styles/041-manga-page/code.js +845 -0
  134. package/skills/aicut-motion/assets/atlas/styles/041-manga-page/poster.jpg +0 -0
  135. package/skills/aicut-motion/assets/atlas/styles/041-manga-page/style.json +13 -0
  136. package/skills/aicut-motion/assets/atlas/styles/042-wood-type-letterpress/code.js +496 -0
  137. package/skills/aicut-motion/assets/atlas/styles/042-wood-type-letterpress/poster.jpg +0 -0
  138. package/skills/aicut-motion/assets/atlas/styles/042-wood-type-letterpress/style.json +13 -0
  139. package/skills/aicut-motion/assets/atlas/styles/043-light-painting/code.js +635 -0
  140. package/skills/aicut-motion/assets/atlas/styles/043-light-painting/poster.jpg +0 -0
  141. package/skills/aicut-motion/assets/atlas/styles/043-light-painting/style.json +13 -0
  142. package/skills/aicut-motion/assets/atlas/styles/044-felt-patchwork/code.js +799 -0
  143. package/skills/aicut-motion/assets/atlas/styles/044-felt-patchwork/poster.jpg +0 -0
  144. package/skills/aicut-motion/assets/atlas/styles/044-felt-patchwork/style.json +13 -0
  145. package/skills/aicut-motion/assets/atlas/styles/045-phosphor-terminal/code.js +547 -0
  146. package/skills/aicut-motion/assets/atlas/styles/045-phosphor-terminal/poster.jpg +0 -0
  147. package/skills/aicut-motion/assets/atlas/styles/045-phosphor-terminal/style.json +13 -0
  148. package/skills/aicut-motion/assets/atlas/styles/046-glassmorphism-ui/code.js +592 -0
  149. package/skills/aicut-motion/assets/atlas/styles/046-glassmorphism-ui/poster.jpg +0 -0
  150. package/skills/aicut-motion/assets/atlas/styles/046-glassmorphism-ui/style.json +13 -0
  151. package/skills/aicut-motion/assets/atlas/styles/047-tarot-card/code.js +768 -0
  152. package/skills/aicut-motion/assets/atlas/styles/047-tarot-card/poster.jpg +0 -0
  153. package/skills/aicut-motion/assets/atlas/styles/047-tarot-card/style.json +13 -0
  154. package/skills/aicut-motion/assets/atlas/styles/048-transit-map/code.js +666 -0
  155. package/skills/aicut-motion/assets/atlas/styles/048-transit-map/poster.jpg +0 -0
  156. package/skills/aicut-motion/assets/atlas/styles/048-transit-map/style.json +13 -0
  157. package/skills/aicut-motion/assets/atlas/styles/049-gouache-travel-poster/code.js +559 -0
  158. package/skills/aicut-motion/assets/atlas/styles/049-gouache-travel-poster/poster.jpg +0 -0
  159. package/skills/aicut-motion/assets/atlas/styles/049-gouache-travel-poster/style.json +13 -0
  160. package/skills/aicut-motion/assets/atlas/styles/050-napkin-sketch/code.js +943 -0
  161. package/skills/aicut-motion/assets/atlas/styles/050-napkin-sketch/poster.jpg +0 -0
  162. package/skills/aicut-motion/assets/atlas/styles/050-napkin-sketch/style.json +13 -0
  163. package/skills/aicut-motion/assets/atlas/styles/051-art-deco-foil/code.js +606 -0
  164. package/skills/aicut-motion/assets/atlas/styles/051-art-deco-foil/poster.jpg +0 -0
  165. package/skills/aicut-motion/assets/atlas/styles/051-art-deco-foil/style.json +13 -0
  166. package/skills/aicut-motion/assets/atlas/styles/052-radiograph-on-lightbox/code.js +730 -0
  167. package/skills/aicut-motion/assets/atlas/styles/052-radiograph-on-lightbox/poster.jpg +0 -0
  168. package/skills/aicut-motion/assets/atlas/styles/052-radiograph-on-lightbox/style.json +13 -0
  169. package/skills/aicut-motion/assets/atlas/styles/053-vaporwave/code.js +782 -0
  170. package/skills/aicut-motion/assets/atlas/styles/053-vaporwave/poster.jpg +0 -0
  171. package/skills/aicut-motion/assets/atlas/styles/053-vaporwave/style.json +13 -0
  172. package/skills/aicut-motion/assets/atlas/styles/054-ebru-paper-marbling/code.js +504 -0
  173. package/skills/aicut-motion/assets/atlas/styles/054-ebru-paper-marbling/poster.jpg +0 -0
  174. package/skills/aicut-motion/assets/atlas/styles/054-ebru-paper-marbling/style.json +13 -0
  175. package/skills/aicut-motion/assets/atlas/styles/055-isotype-pictograms/code.js +517 -0
  176. package/skills/aicut-motion/assets/atlas/styles/055-isotype-pictograms/poster.jpg +0 -0
  177. package/skills/aicut-motion/assets/atlas/styles/055-isotype-pictograms/style.json +13 -0
  178. package/skills/aicut-motion/assets/atlas/styles/056-soft-3d-clay/code.js +713 -0
  179. package/skills/aicut-motion/assets/atlas/styles/056-soft-3d-clay/poster.jpg +0 -0
  180. package/skills/aicut-motion/assets/atlas/styles/056-soft-3d-clay/style.json +13 -0
  181. package/skills/aicut-motion/assets/atlas/styles/057-cross-stitch-sampler/code.js +717 -0
  182. package/skills/aicut-motion/assets/atlas/styles/057-cross-stitch-sampler/poster.jpg +0 -0
  183. package/skills/aicut-motion/assets/atlas/styles/057-cross-stitch-sampler/style.json +13 -0
  184. package/skills/aicut-motion/assets/atlas/styles/058-constructivist-propaganda/code.js +540 -0
  185. package/skills/aicut-motion/assets/atlas/styles/058-constructivist-propaganda/poster.jpg +0 -0
  186. package/skills/aicut-motion/assets/atlas/styles/058-constructivist-propaganda/style.json +13 -0
  187. package/skills/aicut-motion/assets/atlas/styles/059-pocket-monochrome/code.js +524 -0
  188. package/skills/aicut-motion/assets/atlas/styles/059-pocket-monochrome/poster.jpg +0 -0
  189. package/skills/aicut-motion/assets/atlas/styles/059-pocket-monochrome/style.json +13 -0
  190. package/skills/aicut-motion/assets/atlas/styles/060-botanical-plate/code.js +934 -0
  191. package/skills/aicut-motion/assets/atlas/styles/060-botanical-plate/poster.jpg +0 -0
  192. package/skills/aicut-motion/assets/atlas/styles/060-botanical-plate/style.json +13 -0
  193. package/skills/aicut-motion/assets/atlas/styles/061-kinetic-type/code.js +690 -0
  194. package/skills/aicut-motion/assets/atlas/styles/061-kinetic-type/poster.jpg +0 -0
  195. package/skills/aicut-motion/assets/atlas/styles/061-kinetic-type/style.json +13 -0
  196. package/skills/aicut-motion/assets/atlas/styles/062-byzantine-mosaic/code.js +612 -0
  197. package/skills/aicut-motion/assets/atlas/styles/062-byzantine-mosaic/poster.jpg +0 -0
  198. package/skills/aicut-motion/assets/atlas/styles/062-byzantine-mosaic/style.json +13 -0
  199. package/skills/aicut-motion/assets/atlas/styles/063-y2k-liquid-chrome/code.js +666 -0
  200. package/skills/aicut-motion/assets/atlas/styles/063-y2k-liquid-chrome/poster.jpg +0 -0
  201. package/skills/aicut-motion/assets/atlas/styles/063-y2k-liquid-chrome/style.json +13 -0
  202. package/skills/aicut-motion/assets/atlas/styles/064-sticky-note-wall/code.js +651 -0
  203. package/skills/aicut-motion/assets/atlas/styles/064-sticky-note-wall/poster.jpg +0 -0
  204. package/skills/aicut-motion/assets/atlas/styles/064-sticky-note-wall/style.json +13 -0
  205. package/skills/aicut-motion/assets/atlas/styles/065-vintage-newsprint/code.js +656 -0
  206. package/skills/aicut-motion/assets/atlas/styles/065-vintage-newsprint/poster.jpg +0 -0
  207. package/skills/aicut-motion/assets/atlas/styles/065-vintage-newsprint/style.json +13 -0
  208. package/skills/aicut-motion/assets/atlas/styles/066-hidden-line-wireframe/code.js +672 -0
  209. package/skills/aicut-motion/assets/atlas/styles/066-hidden-line-wireframe/poster.jpg +0 -0
  210. package/skills/aicut-motion/assets/atlas/styles/066-hidden-line-wireframe/style.json +13 -0
  211. package/skills/aicut-motion/assets/atlas/styles/067-memphis-group/code.js +548 -0
  212. package/skills/aicut-motion/assets/atlas/styles/067-memphis-group/poster.jpg +0 -0
  213. package/skills/aicut-motion/assets/atlas/styles/067-memphis-group/style.json +13 -0
  214. package/skills/aicut-motion/assets/atlas/styles/068-ballpoint-notebook/code.js +989 -0
  215. package/skills/aicut-motion/assets/atlas/styles/068-ballpoint-notebook/poster.jpg +0 -0
  216. package/skills/aicut-motion/assets/atlas/styles/068-ballpoint-notebook/style.json +13 -0
  217. package/skills/aicut-motion/assets/atlas/styles/069-op-art-sphere/code.js +440 -0
  218. package/skills/aicut-motion/assets/atlas/styles/069-op-art-sphere/poster.jpg +0 -0
  219. package/skills/aicut-motion/assets/atlas/styles/069-op-art-sphere/style.json +13 -0
  220. package/skills/aicut-motion/assets/atlas/styles/070-topographic-survey/code.js +895 -0
  221. package/skills/aicut-motion/assets/atlas/styles/070-topographic-survey/poster.jpg +0 -0
  222. package/skills/aicut-motion/assets/atlas/styles/070-topographic-survey/style.json +13 -0
  223. package/skills/aicut-motion/assets/atlas/styles/071-pointillist-afternoon/code.js +563 -0
  224. package/skills/aicut-motion/assets/atlas/styles/071-pointillist-afternoon/poster.jpg +0 -0
  225. package/skills/aicut-motion/assets/atlas/styles/071-pointillist-afternoon/style.json +13 -0
  226. package/skills/aicut-motion/assets/atlas/styles/072-vector-arcade-1980/code.js +627 -0
  227. package/skills/aicut-motion/assets/atlas/styles/072-vector-arcade-1980/poster.jpg +0 -0
  228. package/skills/aicut-motion/assets/atlas/styles/072-vector-arcade-1980/style.json +13 -0
  229. package/skills/aicut-motion/assets/atlas/styles/073-patent-drawing/code.js +1096 -0
  230. package/skills/aicut-motion/assets/atlas/styles/073-patent-drawing/poster.jpg +0 -0
  231. package/skills/aicut-motion/assets/atlas/styles/073-patent-drawing/style.json +13 -0
  232. package/skills/aicut-motion/assets/atlas/styles/074-bauhaus-poster/code.js +595 -0
  233. package/skills/aicut-motion/assets/atlas/styles/074-bauhaus-poster/poster.jpg +0 -0
  234. package/skills/aicut-motion/assets/atlas/styles/074-bauhaus-poster/style.json +13 -0
  235. package/skills/aicut-motion/assets/atlas/styles/075-bitmap-paint-doodle/code.js +665 -0
  236. package/skills/aicut-motion/assets/atlas/styles/075-bitmap-paint-doodle/poster.jpg +0 -0
  237. package/skills/aicut-motion/assets/atlas/styles/075-bitmap-paint-doodle/style.json +13 -0
  238. package/skills/aicut-motion/assets/atlas/styles/076-nail-thread-string-art/code.js +645 -0
  239. package/skills/aicut-motion/assets/atlas/styles/076-nail-thread-string-art/poster.jpg +0 -0
  240. package/skills/aicut-motion/assets/atlas/styles/076-nail-thread-string-art/style.json +13 -0
  241. package/skills/aicut-motion/assets/atlas/styles/077-ridgeline/code.js +436 -0
  242. package/skills/aicut-motion/assets/atlas/styles/077-ridgeline/poster.jpg +0 -0
  243. package/skills/aicut-motion/assets/atlas/styles/077-ridgeline/style.json +13 -0
  244. package/skills/aicut-motion/assets/atlas/styles/078-sketchbook-graphite/code.js +1237 -0
  245. package/skills/aicut-motion/assets/atlas/styles/078-sketchbook-graphite/poster.jpg +0 -0
  246. package/skills/aicut-motion/assets/atlas/styles/078-sketchbook-graphite/style.json +13 -0
  247. package/skills/aicut-motion/assets/atlas/styles/079-teletext-page/code.js +622 -0
  248. package/skills/aicut-motion/assets/atlas/styles/079-teletext-page/poster.jpg +0 -0
  249. package/skills/aicut-motion/assets/atlas/styles/079-teletext-page/style.json +13 -0
  250. package/skills/aicut-motion/assets/atlas/styles/080-silkscreen-grid/code.js +524 -0
  251. package/skills/aicut-motion/assets/atlas/styles/080-silkscreen-grid/poster.jpg +0 -0
  252. package/skills/aicut-motion/assets/atlas/styles/080-silkscreen-grid/style.json +13 -0
  253. package/skills/aicut-motion/assets/atlas/styles/081-guilloch-security-print/code.js +689 -0
  254. package/skills/aicut-motion/assets/atlas/styles/081-guilloch-security-print/poster.jpg +0 -0
  255. package/skills/aicut-motion/assets/atlas/styles/081-guilloch-security-print/style.json +13 -0
  256. package/skills/aicut-motion/assets/atlas/styles/082-ascii-demoscene/code.js +574 -0
  257. package/skills/aicut-motion/assets/atlas/styles/082-ascii-demoscene/poster.jpg +0 -0
  258. package/skills/aicut-motion/assets/atlas/styles/082-ascii-demoscene/style.json +13 -0
  259. package/skills/aicut-motion/assets/atlas/styles/083-international-typographic-style/code.js +469 -0
  260. package/skills/aicut-motion/assets/atlas/styles/083-international-typographic-style/poster.jpg +0 -0
  261. package/skills/aicut-motion/assets/atlas/styles/083-international-typographic-style/style.json +13 -0
  262. package/skills/aicut-motion/assets/atlas/styles/084-flow-field/code.js +531 -0
  263. package/skills/aicut-motion/assets/atlas/styles/084-flow-field/poster.jpg +0 -0
  264. package/skills/aicut-motion/assets/atlas/styles/084-flow-field/style.json +13 -0
  265. package/skills/aicut-motion/assets/atlas/styles/085-macpaint-1984/code.js +675 -0
  266. package/skills/aicut-motion/assets/atlas/styles/085-macpaint-1984/poster.jpg +0 -0
  267. package/skills/aicut-motion/assets/atlas/styles/085-macpaint-1984/style.json +13 -0
  268. package/skills/aicut-motion/assets/atlas/styles/086-multi-scale-truchet/code.js +609 -0
  269. package/skills/aicut-motion/assets/atlas/styles/086-multi-scale-truchet/poster.jpg +0 -0
  270. package/skills/aicut-motion/assets/atlas/styles/086-multi-scale-truchet/style.json +13 -0
  271. package/skills/aicut-motion/references/design-systems/README.md +1 -1
  272. package/skills/aicut-motion/references/motion-delivery.md +55 -0
  273. package/skills/aicut-motion/references/pages.md +1 -1
  274. package/skills/aicut-motion/references/style-index.json +690 -0
  275. package/skills/aicut-motion/references/style-research.md +73 -0
  276. package/skills/aicut-motion/references/style-selection.md +71 -0
  277. package/skills/aicut-motion/scripts/atlas-viewer.html +195 -0
  278. package/skills/aicut-motion/scripts/atlas.mjs +109 -0
  279. package/studio/client.mjs +3 -3
  280. package/studio/server/fetch-github.mjs +14 -3
  281. package/studio/server/server.mjs +71 -6
  282. package/studio/server/skill-summaries.mjs +60 -0
  283. package/studio/server/skills.mjs +121 -7
  284. package/studio/server/styles-store.mjs +235 -0
  285. package/studio/server/template-posters.mjs +2 -2
  286. package/studio/server/templates-page.mjs +93 -93
  287. package/studio/server/templates.mjs +32 -7
  288. package/studio/ui/dist/assets/ExportCenter-DasulhAv.js +1 -0
  289. package/studio/ui/dist/assets/SettingsModal-BZw0Hz2_.js +2 -0
  290. package/studio/ui/dist/assets/apple-touch-icon-ClxQ7j-O.png +0 -0
  291. package/studio/ui/dist/assets/es-C8XPZARI.js +1 -0
  292. package/studio/ui/dist/assets/favicon-Blwx8Trs.png +0 -0
  293. package/studio/ui/dist/assets/index-BjJVmBzB.js +1623 -0
  294. package/studio/ui/dist/assets/index-C3BfKZFO.css +1 -0
  295. package/studio/ui/dist/assets/ja-BeNkdaEj.js +1 -0
  296. package/studio/ui/dist/assets/ko-DcTVhdzK.js +1 -0
  297. package/studio/ui/dist/assets/zh-CN-Cg51ctbL.js +1 -0
  298. package/studio/ui/dist/index.html +4 -3
  299. package/studio/ui/dist/assets/ExportCenter-F-2vF-lQ.js +0 -1
  300. package/studio/ui/dist/assets/SettingsModal-BgVYguNu.js +0 -2
  301. package/studio/ui/dist/assets/es-BRUzvuoM.js +0 -1
  302. package/studio/ui/dist/assets/index-Bhg-9dA3.css +0 -1
  303. package/studio/ui/dist/assets/index-CWur1Iw9.js +0 -1625
  304. package/studio/ui/dist/assets/ja-Cth-uDcW.js +0 -1
  305. package/studio/ui/dist/assets/ko-upvw591D.js +0 -1
  306. package/studio/ui/dist/assets/zh-CN-BCfJFazB.js +0 -1
@@ -0,0 +1,690 @@
1
+ {
2
+ // Kinetic typography frame: oversized words stacked edge to edge with tight leading, mixed
3
+ // weights, outline vs fill, strobe echoes, a slice-glitch, and one word stretched and masked
4
+ // with a halftone image. Strict two-colour: near-black + acid yellow.
5
+ const INK = '#e6ff2e', BG = '#0c0c0b';
6
+
7
+ // fit a word so its ink spans exactly targetW (uses actual bounding box, not advance)
8
+ function fit(ctx, font, size, text, targetW) {
9
+ ctx.font = `${font.replace('{s}', size)}`;
10
+ const m = ctx.measureText(text);
11
+ const w = m.actualBoundingBoxLeft + m.actualBoundingBoxRight;
12
+ const s = (size * targetW) / w;
13
+ ctx.font = `${font.replace('{s}', s)}`;
14
+ const m2 = ctx.measureText(text);
15
+ return { size: s, left: m2.actualBoundingBoxLeft, asc: m2.actualBoundingBoxAscent, desc: m2.actualBoundingBoxDescent, w: m2.actualBoundingBoxLeft + m2.actualBoundingBoxRight };
16
+ }
17
+
18
+
19
+ // =====================================================================================
20
+ // EXPLAINER — "How does AI learn?" told in kinetic type: words slam on the beat, cuts are
21
+ // slice-glitches and whip-pans, arrows are heavy rules with chevrons, numbers are Anton.
22
+ // =====================================================================================
23
+ const XS = (t, a, b) => H.clamp((t - a) / (b - a));
24
+ let ANTON_CAP = 0;
25
+ function capRatio(ctx) {
26
+ if (!ANTON_CAP) { ctx.font = '400 200px "Anton"'; ANTON_CAP = ctx.measureText('H').actualBoundingBoxAscent / 200; }
27
+ return ANTON_CAP;
28
+ }
29
+ // Anton word at left x / baseline y with a given cap height; returns drawn width
30
+ function aw(ctx, text, x, y, cap, o = {}) {
31
+ let size = cap / capRatio(ctx);
32
+ ctx.save();
33
+ ctx.font = `400 ${size}px "Anton"`; ctx.textBaseline = 'alphabetic'; ctx.textAlign = 'left';
34
+ if (o.fitW) { const mw = ctx.measureText(text).width; if (mw > o.fitW) { size *= o.fitW / mw; ctx.font = `400 ${size}px "Anton"`; } }
35
+ const m = ctx.measureText(text), w = m.width * (o.sx || 1);
36
+ let ox = x; if (o.align === 'center') ox = x - w / 2; else if (o.align === 'right') ox = x - w;
37
+ ctx.translate(ox, y); if (o.sx) ctx.scale(o.sx, 1);
38
+ if (o.outline) { ctx.strokeStyle = o.color || INK; ctx.lineWidth = (o.lw || 3) / (o.sx || 1); ctx.lineJoin = 'miter'; ctx.strokeText(text, 0, 0); }
39
+ else { ctx.fillStyle = o.color || INK; ctx.fillText(text, 0, 0); }
40
+ ctx.restore();
41
+ return w;
42
+ }
43
+ function tag(ctx, text, x, y, o = {}) { // inverted label block (ink box, black mono text)
44
+ ctx.save();
45
+ ctx.font = `700 ${o.size || 18}px "Space Mono"`; ctx.textBaseline = 'middle';
46
+ const w = ctx.measureText(text).width + 16, h = (o.size || 18) + 12;
47
+ const ox = o.align === 'center' ? x - w / 2 : x;
48
+ ctx.fillStyle = o.inv ? BG : INK; ctx.fillRect(ox, y - h / 2, w, h);
49
+ if (o.inv) { ctx.strokeStyle = INK; ctx.lineWidth = 2; ctx.strokeRect(ox, y - h / 2, w, h); }
50
+ ctx.fillStyle = o.inv ? INK : BG; ctx.fillText(text, ox + 8, y + 1);
51
+ ctx.restore();
52
+ return w;
53
+ }
54
+ // pet pictogram brightness for halftone (u,v in -1..1, y down): clean geometric shapes,
55
+ // lit from the upper left so the dot portrait has some volume
56
+ const inTri = (px, py, ax, ay, bx, by, cx, cy) => {
57
+ const d1 = (px - bx) * (ay - by) - (ax - bx) * (py - by), d2 = (px - cx) * (by - cy) - (bx - cx) * (py - cy), d3 = (px - ax) * (cy - ay) - (cx - ax) * (py - ay);
58
+ return !((d1 < 0 || d2 < 0 || d3 < 0) && (d1 > 0 || d2 > 0 || d3 > 0));
59
+ };
60
+ const inEll = (u, v, cx, cy, rx, ry) => { const a = (u - cx) / rx, b = (v - cy) / ry; return a * a + b * b < 1; };
61
+ function petB(kind, u, v) {
62
+ const lit = (cx, cy, r) => H.clamp(1.08 - 0.3 * Math.hypot(u - cx + r * 0.38, v - cy + r * 0.42) / r, 0.72, 1);
63
+ if (kind === 'cat') {
64
+ const hx = 0, hy = 0.16, hr = 0.56;
65
+ const head = inEll(u, v, hx, hy, hr, hr * 0.92);
66
+ let ear = 0;
67
+ for (const s of [-1, 1]) {
68
+ if (inTri(u, v, s * 0.56, 0.02, s * 0.5, -0.86, s * 0.08, -0.28)) ear = 1;
69
+ if (inTri(u, v, s * 0.45, -0.1, s * 0.45, -0.62, s * 0.22, -0.3)) ear = ear ? 2 : ear;
70
+ }
71
+ for (const s of [-1, 1]) for (const dy of [-0.05, 0.07]) { // whiskers: straight bright strokes outside the face
72
+ const x = u * s; if (x > 0.42 && x < 0.95) { const yy = 0.36 + dy + (x - 0.42) * dy * 1.6; if (Math.abs(v - yy) < 0.026) return 0.8; }
73
+ }
74
+ if (!head && !ear) return 0.035;
75
+ if (ear === 2) return 0.5;
76
+ if (ear && !head) return lit(hx, hy, hr) * 0.92;
77
+ for (const s of [-1, 1]) { if (inEll(u, v, s * 0.23, 0.08, 0.1, 0.12)) return inEll(u, v, s * 0.23 - 0.03, 0.04, 0.035, 0.035) ? 0.9 : 0.03; }
78
+ if (inTri(u, v, -0.08, 0.27, 0.08, 0.27, 0, 0.36)) return 0.03;
79
+ if (Math.abs(u) < 0.022 && v > 0.35 && v < 0.43) return 0.1;
80
+ return lit(hx, hy, hr);
81
+ }
82
+ // dog: round head, long floppy ears, pale muzzle, dark nose
83
+ const hx = 0, hy = 0.04, hr = 0.5;
84
+ for (const s of [-1, 1]) {
85
+ const a = 0.32 * s, du = u - s * 0.52, dv = v - 0.14, ru = du * Math.cos(a) + dv * Math.sin(a), rv = -du * Math.sin(a) + dv * Math.cos(a);
86
+ if ((ru / 0.17) ** 2 + (rv / 0.44) ** 2 < 1 && !inEll(u, v, hx, hy, hr * 0.98, hr * 1.06)) return 0.6;
87
+ if ((ru / 0.24) ** 2 + (rv / 0.51) ** 2 < 1 && !inEll(u, v, hx, hy, hr * 0.92, hr)) return 0.02; // dark gap so the ear reads
88
+ }
89
+ const head = inEll(u, v, hx, hy, hr * 0.92, hr), muz = inEll(u, v, 0, 0.36, 0.3, 0.22);
90
+ if (!head && !muz) return 0.035;
91
+ if (inEll(u, v, 0, 0.27, 0.11, 0.075)) return inEll(u, v, -0.03, 0.25, 0.03, 0.022) ? 0.75 : 0.03;
92
+ for (const s of [-1, 1]) { if (inEll(u, v, s * 0.2, -0.02, 0.08, 0.09)) return inEll(u, v, s * 0.2 - 0.025, -0.05, 0.03, 0.03) ? 0.9 : 0.03; }
93
+ if (muz) { if (Math.abs(u) < 0.02 && v > 0.33 && v < 0.45) return 0.15; if (v > 0.43 && Math.abs(u) < 0.2 && Math.abs(v - 0.45 - u * u * 1.2) < 0.025) return 0.15; return 1; }
94
+ return lit(hx, hy, hr) * 0.85;
95
+ }
96
+ // halftone portrait on a 45° screen, baked once per kind+size
97
+ const PET_CACHE = {};
98
+ function petTex(kind, size) {
99
+ const key = kind + '|' + Math.round(size);
100
+ if (PET_CACHE[key]) return PET_CACHE[key];
101
+ const S = Math.ceil(size), cv = H.layer(S, S), c = cv.getContext('2d'), h = size / 2;
102
+ const step = Math.max(3.4, Math.min(size / 28, 6.2)), ca = Math.SQRT1_2;
103
+ c.fillStyle = INK; c.beginPath();
104
+ for (let j = -S; j < S * 1.5; j += step) for (let i = -S; i < S * 1.5; i += step) {
105
+ const x = (i - j) * ca + h, y = (i + j) * ca - h * 0.42;
106
+ if (x < 1 || y < 1 || x > S - 1 || y > S - 1) continue;
107
+ const b = petB(kind, ((x - h) / h) * 1.08, ((y - h) / h) * 1.08);
108
+ const r = Math.sqrt(b) * step * 0.6;
109
+ if (r < 0.45) continue;
110
+ c.moveTo(x + r, y); c.arc(x, y, r, 0, Math.PI * 2);
111
+ }
112
+ c.fill();
113
+ return (PET_CACHE[key] = cv);
114
+ }
115
+ function halftonePet(ctx, kind, cx, cy, size) { const tx = petTex(kind, size); ctx.drawImage(tx, cx - size / 2, cy - size / 2); }
116
+ // ink-block offset shadow (two-colour, like the hero's TO tab), black face, heavy rule
117
+ function blockBox(ctx, x, y, w, h, lw, off) {
118
+ ctx.fillStyle = INK; ctx.fillRect(x + off, y + off, w, h);
119
+ ctx.fillStyle = BG; ctx.fillRect(x, y, w, h);
120
+ ctx.strokeStyle = INK; ctx.lineWidth = lw; ctx.strokeRect(x, y, w, h);
121
+ }
122
+ function card(ctx, kind, x, y, size, label) {
123
+ ctx.save();
124
+ const off = size > 150 ? 9 : 6;
125
+ blockBox(ctx, x, y, size, size, 3, off);
126
+ halftonePet(ctx, kind, x + size / 2, y + size / 2, size - 10);
127
+ if (label) tag(ctx, label, x, y + size + 18, { size: 22 });
128
+ ctx.restore();
129
+ }
130
+ function arrowLine(ctx, x0, y0, x1, y1, p, lw = 6, head = 22) {
131
+ if (p <= 0) return;
132
+ const x = x0 + (x1 - x0) * p, y = y0 + (y1 - y0) * p, a = Math.atan2(y1 - y0, x1 - x0);
133
+ ctx.save(); ctx.strokeStyle = INK; ctx.fillStyle = INK; ctx.lineWidth = lw;
134
+ ctx.beginPath(); ctx.moveTo(x0, y0); ctx.lineTo(x - Math.cos(a) * head * 0.6, y - Math.sin(a) * head * 0.6); ctx.stroke();
135
+ ctx.beginPath(); ctx.moveTo(x, y); ctx.lineTo(x - Math.cos(a - 0.5) * head, y - Math.sin(a - 0.5) * head); ctx.lineTo(x - Math.cos(a + 0.5) * head, y - Math.sin(a + 0.5) * head); ctx.closePath(); ctx.fill();
136
+ ctx.restore();
137
+ }
138
+ function modelBox(ctx, x, y, w, h, t, adjust, drawP = 1) {
139
+ ctx.save();
140
+ ctx.strokeStyle = INK; ctx.lineWidth = 4;
141
+ // box draws on as a perimeter
142
+ const per = 2 * (w + h), L = per * drawP;
143
+ if (drawP >= 1) blockBox(ctx, x, y, w, h, 4, 10);
144
+ else {
145
+ ctx.beginPath(); ctx.moveTo(x, y);
146
+ const pts = [[x + w, y], [x + w, y + h], [x, y + h], [x, y]];
147
+ let acc = 0, px = x, py = y;
148
+ for (const [qx, qy] of pts) { const d = Math.hypot(qx - px, qy - py); if (acc + d <= L) { ctx.lineTo(qx, qy); acc += d; px = qx; py = qy; } else { const k = (L - acc) / d; ctx.lineTo(px + (qx - px) * k, py + (qy - py) * k); break; } }
149
+ ctx.stroke();
150
+ }
151
+ if (drawP >= 1) {
152
+ aw(ctx, 'MODEL', x + 16, y + 16 + h * 0.18, h * 0.18);
153
+ // weights: bars that jiggle while processing and re-set when adjusted
154
+ const r = H.rng(321), n = 9, bw = (w - 32) / n;
155
+ for (let i = 0; i < n; i++) {
156
+ const b0 = r.range(0.15, 0.95), b1 = r.range(0.35, 0.9);
157
+ const k = adjust <= 0 ? 0 : H.ease.back(H.clamp(adjust));
158
+ let hh = b0 + (b1 - b0) * k + 0.04 * Math.sin(t * 9 + i * 1.7);
159
+ const bh = (h * 0.5) * H.clamp(hh, 0.05, 1.1);
160
+ const bx = x + 16 + i * bw + 3, by = y + h - 16 - bh;
161
+ ctx.fillStyle = INK;
162
+ if (i % 2) { // alternate rounds: 3px stripe hatch inside a rule, like a print screen
163
+ for (let yy = y + h - 16 - 3; yy > by; yy -= 6) ctx.fillRect(bx, Math.max(by, yy), bw - 6, Math.min(3, yy - by + 3));
164
+ ctx.strokeStyle = INK; ctx.lineWidth = 2; ctx.strokeRect(bx + 1, by + 1, bw - 8, bh - 1);
165
+ } else ctx.fillRect(bx, by, bw - 6, bh);
166
+ }
167
+ ctx.font = '700 24px "Space Mono"'; ctx.fillStyle = INK; ctx.fillText('WEIGHTS', x + 16, y + h * 0.44);
168
+ }
169
+ ctx.restore();
170
+ }
171
+ function header(ctx) {
172
+ ctx.save(); ctx.font = '700 28px "Space Mono"'; ctx.fillStyle = INK; ctx.textBaseline = 'middle';
173
+ try { ctx.letterSpacing = '2px'; } catch (e) {}
174
+ const w = ctx.measureText('HOW DOES AI LEARN?').width;
175
+ ctx.fillText('HOW DOES AI LEARN?', 30, 34); ctx.fillRect(30, 57, w, 4); ctx.fillRect(30, 64, w * 0.38, 2); ctx.restore();
176
+ }
177
+ // ---------- the hero's typographic finishing, reused in the explainer ----------
178
+ // a filled Anton word rendered through a scratch layer so it can carry the hero's signatures:
179
+ // knocked-out speed lines through the lower third, a shoved slice band with thin rule ticks,
180
+ // and an optional rotated-dot halftone fill (like DREAM) held together by an outline.
181
+ let SCR = null, SCRB = null;
182
+ const DOT_CACHE = {};
183
+ function dotTex(key, w, h, top, capH) { // baked once per word: 45° dot screen, a glow rising from the baseline + drifting cloud bands
184
+ if (DOT_CACHE[key]) return DOT_CACHE[key];
185
+ const cv = H.layer(Math.ceil(w), Math.ceil(h)), c = cv.getContext('2d'), nz = H.makeNoise(31);
186
+ const step = 8, ca = Math.SQRT1_2;
187
+ c.fillStyle = INK; c.beginPath();
188
+ for (let j = -h; j < w + h; j += step) for (let i = -h; i < w + h; i += step) {
189
+ const x = (i - j) * ca, y = (i + j) * ca - w * 0.5;
190
+ if (x < -6 || y < -6 || x > w + 6 || y > h + 6) continue;
191
+ const v = H.clamp((y - top) / capH), cl = nz.fbm2(x * 0.004, y * 0.012, 4) * 0.5 + 0.5;
192
+ const sun = Math.max(0, 0.5 - Math.hypot((x - w * 0.62) / 1.8, y - top - capH * 1.05) / (capH * 1.6));
193
+ const b = H.clamp(0.24 + 0.62 * Math.pow(v, 1.4) + 0.35 * Math.pow(H.clamp(cl * 1.6 - 0.6), 1.2) + sun);
194
+ const r = Math.sqrt(b) * step * 0.62;
195
+ if (r < 0.5) continue;
196
+ c.moveTo(x + r, y); c.arc(x, y, r, 0, Math.PI * 2);
197
+ }
198
+ c.fill();
199
+ return (DOT_CACHE[key] = cv);
200
+ }
201
+ function heroWord(ctx, text, x, y, cap, o = {}) {
202
+ if (!SCR) { SCR = H.layer(1520, 600); SCRB = H.layer(1520, 80); }
203
+ const c = SCR.getContext('2d');
204
+ c.setTransform(1, 0, 0, 1, 0, 0); c.globalCompositeOperation = 'source-over'; c.globalAlpha = 1;
205
+ let size = cap / capRatio(ctx);
206
+ c.font = `400 ${size}px "Anton"`;
207
+ if (o.fitW) { const mw = c.measureText(text).width; if (mw > o.fitW) { size *= o.fitW / mw; c.font = `400 ${size}px "Anton"`; } }
208
+ const capH = size * capRatio(ctx), w = c.measureText(text).width;
209
+ const PX = 80, PY = 40, sw = Math.min(SCR.width, Math.ceil(w + PX * 2)), sh = Math.min(SCR.height, Math.ceil(capH + PY * 2 + size * 0.12));
210
+ const bx = PX, by = PY + capH;
211
+ c.clearRect(0, 0, SCR.width, SCR.height);
212
+ // echoes behind (outline copies fanning right/up, as on the hero's MACHINES)
213
+ if (o.echo) for (let e = o.echo; e >= 1; e--) {
214
+ ctx.save(); ctx.globalAlpha = 0.22 * (o.echo + 1 - e) / o.echo; ctx.font = `400 ${size}px "Anton"`; ctx.textBaseline = 'alphabetic';
215
+ ctx.strokeStyle = INK; ctx.lineWidth = 2; ctx.lineJoin = 'miter'; ctx.strokeText(text, x + e * 24, y - e * 4); ctx.restore();
216
+ }
217
+ c.textBaseline = 'alphabetic'; c.textAlign = 'left'; c.fillStyle = INK;
218
+ c.fillText(text, bx, by);
219
+ if (o.dots) { c.globalCompositeOperation = 'source-in'; c.drawImage(dotTex(o.dots + '|' + Math.round(w), sw, sh, PY, capH), 0, 0); c.globalCompositeOperation = 'source-over'; }
220
+ if (o.speed) { c.globalCompositeOperation = 'destination-out'; for (let k = 0; k < 3; k++) c.fillRect(0, Math.round(by - capH * (0.2 + k * 0.045)), sw, Math.max(2, Math.round((4 - k) * capH / 240))); c.globalCompositeOperation = 'source-over'; }
221
+ if (o.dots) { c.strokeStyle = INK; c.lineWidth = 2.6; c.lineJoin = 'miter'; c.strokeText(text, bx, by); }
222
+ let sy = 0, sl = o.slice;
223
+ if (sl) {
224
+ sy = Math.round(by - capH * sl.at);
225
+ const bc = SCRB.getContext('2d'); bc.clearRect(0, 0, SCRB.width, SCRB.height);
226
+ bc.drawImage(SCR, 0, sy, sw, sl.h, 0, 0, sw, sl.h);
227
+ c.clearRect(0, sy, sw, sl.h); c.drawImage(SCRB, 0, 0, sw, sl.h, sl.shift, sy, sw, sl.h);
228
+ }
229
+ ctx.drawImage(SCR, 0, 0, sw, sh, x - bx, y - by, sw, sh);
230
+ if (sl) { // the thin rule ticks the hero draws beside its slice
231
+ const yy = y - by + sy; ctx.fillStyle = INK;
232
+ const tx = x - (sl.tick ?? 24); ctx.fillRect(tx, yy + sl.h + 5, sl.shift - 12, 2); ctx.fillRect(tx, yy - 7, sl.shift * 0.6, 2);
233
+ }
234
+ return w;
235
+ }
236
+ // heavy rule arrow as a polygon (so it can be filled, or stroked as a strobe echo)
237
+ function arrowShape(ctx, x0, y, x1, lw, head) {
238
+ ctx.beginPath();
239
+ ctx.moveTo(x0, y - lw / 2); ctx.lineTo(x1 - head * 0.9, y - lw / 2); ctx.lineTo(x1 - head * 0.9, y - head * 0.55);
240
+ ctx.lineTo(x1, y); ctx.lineTo(x1 - head * 0.9, y + head * 0.55); ctx.lineTo(x1 - head * 0.9, y + lw / 2); ctx.lineTo(x0, y + lw / 2); ctx.closePath();
241
+ }
242
+ function loopIcon(ctx, cx, cy, r, lw) { // ↻ drawn as a heavy circular arrow
243
+ ctx.save(); ctx.strokeStyle = INK; ctx.fillStyle = INK; ctx.lineWidth = lw; ctx.lineCap = 'butt';
244
+ const a0 = -Math.PI * 0.35, a1 = Math.PI * 1.25;
245
+ ctx.beginPath(); ctx.arc(cx, cy, r, a0, a1); ctx.stroke();
246
+ const hx = cx + Math.cos(a1) * r, hy = cy + Math.sin(a1) * r, tg = a1 + Math.PI / 2, hs = lw * 1.7;
247
+ ctx.beginPath(); ctx.moveTo(hx + Math.cos(tg) * hs * 1.3, hy + Math.sin(tg) * hs * 1.3);
248
+ ctx.lineTo(hx + Math.cos(a1) * hs, hy + Math.sin(a1) * hs); ctx.lineTo(hx - Math.cos(a1) * hs, hy - Math.sin(a1) * hs); ctx.closePath(); ctx.fill();
249
+ ctx.restore();
250
+ }
251
+ const fmt = (n) => Math.round(n).toLocaleString('en-US');
252
+
253
+ // ---------- scenes ----------
254
+ function ktHook(ctx, W, H_, t) {
255
+ const sl = (c) => (t >= c ? 1 - H.ease.out(XS(t, c, c + 0.16)) : -1);
256
+ const a = sl(0), b = sl(0.5), c = sl(1.0);
257
+ if (a > -1) { ctx.save(); const s = 1 + a * 0.4; ctx.translate(40, 200); ctx.scale(s, s); aw(ctx, 'HOW DOES', 0, 0, 180); ctx.restore(); }
258
+ if (b > -1) { const s = 1 + b * 0.5; ctx.save(); ctx.translate(40 + b * 600, 0); aw(ctx, 'AI', 0, 520, 300 * s, { outline: true, lw: 6 }); for (let e = 1; e <= 3; e++) { ctx.globalAlpha = 0.2 * (4 - e) / 3; aw(ctx, 'AI', e * 26, 520 - e * 4, 300, { outline: true, lw: 2 }); } ctx.restore(); }
259
+ if (c > -1) { ctx.save(); ctx.translate(0, c * 300); heroWord(ctx, 'LEARN?', 420, 520, 300, { fitW: W - 460, speed: true, slice: t >= 1.5 ? { at: 0.6, h: 30, shift: 38 } : null }); ctx.restore(); }
260
+ }
261
+ function ktData(ctx, W, H_, t) {
262
+ const MX = 900;
263
+ modelBox(ctx, MX, 120, 340, 500, t, 0, H.ease.out(XS(t, 2.3, 2.7)));
264
+ // conveyor arrows into the model
265
+ const rows = [175, 335, 495];
266
+ rows.forEach((y, i) => arrowLine(ctx, 20, y + 55, MX - 8, y + 55, H.ease.out(XS(t, 2.4 + i * 0.08, 2.8 + i * 0.08)), 3, 18));
267
+ // labelled examples streaming in, faster and faster
268
+ ctx.save(); ctx.beginPath(); ctx.rect(0, 0, MX - 4, H_); ctx.clip();
269
+ const r = H.rng(77), K = 46;
270
+ for (let k = 0; k < K; k++) {
271
+ const kind = r() < 0.5 ? 'cat' : 'dog';
272
+ const ts = 2.45 + 3.1 * Math.pow(k / K, 0.75);
273
+ if (t < ts) continue;
274
+ const x = -130 + (t - ts) * (620 + k * 22);
275
+ if (x > MX + 10) continue;
276
+ card(ctx, kind, x, rows[k % 3], 110, kind.toUpperCase());
277
+ }
278
+ ctx.restore();
279
+ // counter
280
+ if (t < 2.6) return;
281
+ const p = XS(t, 2.6, 5.0);
282
+ const n = p >= 1 ? 1e6 : Math.min(1e6, Math.floor(Math.pow(10, 6 * H.ease.inOut(p))) - 1);
283
+ const w = aw(ctx, fmt(n), 30, 700, 64);
284
+ for (let e = 2; e >= 1; e--) { ctx.save(); ctx.globalAlpha = 0.24 * (3 - e) / 2; aw(ctx, 'EXAMPLES', 30 + w + 18 + e * 14, 700 - e * 3, 64, { outline: true, lw: 1.6 }); ctx.restore(); }
285
+ aw(ctx, 'EXAMPLES', 30 + w + 18, 700, 64, { outline: true, lw: 2.5 });
286
+ }
287
+ function ktLoop(ctx, W, H_, t) {
288
+ // test picture
289
+ card(ctx, 'cat', 70, 150, 230, null);
290
+ const truth = t >= 8.05;
291
+ tag(ctx, truth ? 'ANSWER: CAT' : 'NEW PICTURE', 70, 410, { size: 26 });
292
+ arrowLine(ctx, 315, 265, 465, 265, H.ease.out(XS(t, 6.55, 6.85)));
293
+ const adjust = XS(t, 8.95, 9.35);
294
+ modelBox(ctx, 475, 140, 330, 300, t, adjust, 1);
295
+ arrowLine(ctx, 815, 265, 880, 265, H.ease.out(XS(t, 7.0, 7.2)));
296
+ // data pulse travelling through on the second pass
297
+ if (t > 9.3 && t < 9.6) { const k = XS(t, 9.3, 9.6), x = 315 + k * 565; ctx.fillStyle = INK; ctx.fillRect(x - 14, 252, 28, 26); }
298
+ // output guess
299
+ const right = t >= 9.6;
300
+ if (t >= 7.25 && !right) {
301
+ const s = 1 + 0.4 * Math.pow(1 - H.ease.out(XS(t, 7.25, 7.4)), 2);
302
+ ctx.save(); ctx.translate(900, 330); ctx.scale(s, s);
303
+ for (let e = 3; e >= 1; e--) { ctx.save(); ctx.globalAlpha = 0.2 * (4 - e) / 3; aw(ctx, 'DOG?', e * 22, -e * 4, 150, { outline: true, lw: 2 }); ctx.restore(); }
304
+ aw(ctx, 'DOG?', 0, 0, 150, { outline: true, lw: 5 }); ctx.restore();
305
+ }
306
+ if (right) {
307
+ const s = 1 + 0.4 * Math.pow(1 - H.ease.out(XS(t, 9.6, 9.75)), 2);
308
+ ctx.save(); ctx.translate(900, 330); ctx.scale(s, s); heroWord(ctx, 'CAT', 0, 0, 150, { speed: true, slice: t >= 10.0 ? { at: 0.58, h: 18, shift: 26 } : null }); ctx.restore();
309
+ // tick drawn on
310
+ const k = H.ease.out(XS(t, 9.75, 9.95));
311
+ if (k > 0) { ctx.save(); ctx.strokeStyle = INK; ctx.lineWidth = 16; ctx.lineCap = 'square'; ctx.beginPath(); ctx.moveTo(1170, 270);
312
+ const p1 = [1195, 300], p2 = [1250, 220]; if (k < 0.4) { ctx.lineTo(1170 + (p1[0] - 1170) * k / 0.4, 270 + (p1[1] - 270) * k / 0.4); } else { ctx.lineTo(p1[0], p1[1]); const q = (k - 0.4) / 0.6; ctx.lineTo(p1[0] + (p2[0] - p1[0]) * q, p1[1] + (p2[1] - p1[1]) * q); } ctx.stroke(); ctx.restore(); }
313
+ }
314
+ // the big X on the wrong guess
315
+ if (t >= 7.85 && !right) {
316
+ const k1 = H.ease.out(XS(t, 7.85, 7.97)), k2 = H.ease.out(XS(t, 7.97, 8.09));
317
+ ctx.save(); ctx.strokeStyle = INK; ctx.lineWidth = 20; ctx.lineCap = 'butt';
318
+ ctx.beginPath(); ctx.moveTo(880, 170); ctx.lineTo(880 + 340 * k1, 170 + 200 * k1); ctx.stroke();
319
+ if (k2 > 0) { ctx.beginPath(); ctx.moveTo(1220, 170); ctx.lineTo(1220 - 340 * k2, 170 + 200 * k2); ctx.stroke(); }
320
+ ctx.restore();
321
+ }
322
+ // labels that land on what they label
323
+ const drop = (t0) => (1 - H.ease.back(XS(t, t0, t0 + 0.25))) * -60;
324
+ if (t >= 7.35) tag(ctx, 'GUESS', 900, 126 + drop(7.35), { size: 28 });
325
+ if (t >= 8.0 && !right) tag(ctx, 'CHECK ✗ WRONG', 900, 400 + drop(8.0), { size: 28 });
326
+ // error signal travels back to the model
327
+ const e = XS(t, 8.45, 8.9);
328
+ if (e > 0 && t < 9.6) {
329
+ ctx.save(); ctx.strokeStyle = INK; ctx.lineWidth = 5; ctx.setLineDash([16, 10]); ctx.lineDashOffset = t * 80;
330
+ const path = [[1050, 420], [1050, 520], [640, 520], [640, 448]];
331
+ let L = 0; for (let i = 1; i < path.length; i++) L += Math.hypot(path[i][0] - path[i - 1][0], path[i][1] - path[i - 1][1]);
332
+ let left = L * H.ease.inOut(e); ctx.beginPath(); ctx.moveTo(1050, 420); let end = path[0];
333
+ for (let i = 1; i < path.length && left > 0; i++) { const d = Math.hypot(path[i][0] - path[i - 1][0], path[i][1] - path[i - 1][1]), k = Math.min(1, left / d); end = [path[i - 1][0] + (path[i][0] - path[i - 1][0]) * k, path[i - 1][1] + (path[i][1] - path[i - 1][1]) * k]; ctx.lineTo(end[0], end[1]); left -= d; }
334
+ ctx.stroke(); ctx.setLineDash([]);
335
+ if (e >= 1) arrowLine(ctx, 640, 470, 640, 446, 1, 5, 22);
336
+ ctx.restore();
337
+ if (t >= 8.6) tag(ctx, 'ADJUST WEIGHTS', 760, 556 + drop(8.6), { size: 28 });
338
+ }
339
+ // loop strip
340
+ const words = ['GUESS', 'CHECK', 'ADJUST'];
341
+ let act = t < 7.3 ? -1 : t < 7.9 ? 0 : t < 8.5 ? 1 : t < 9.4 ? 2 : t < 9.9 ? 0 : Math.floor((t - 9.9) / 0.12) % 3;
342
+ let x = 30;
343
+ const glyphW = (txt, cap) => { ctx.save(); ctx.font = `400 ${cap / capRatio(ctx)}px "Anton"`; const m = ctx.measureText(txt).width; ctx.restore(); return m; };
344
+ words.forEach((wd, i) => {
345
+ x += aw(ctx, wd, x, 690, 64, { outline: i !== act, lw: 2.5 }) + 14;
346
+ if (i < 2) { const aW = glyphW('→', 64); ctx.fillStyle = INK; arrowShape(ctx, x + 4, 690 - 30, x + aW - 2, 7, 22); ctx.fill(); x += aW + 14; }
347
+ });
348
+ { const iW = glyphW('↻ ', 40); loopIcon(ctx, x + 10 + 19, 690 - 19, 15, 6); aw(ctx, '×1,000,000', x + 10 + Math.max(iW, 46), 690, 40, { outline: false }); }
349
+ }
350
+ function ktProgress(ctx, W, H_, t) {
351
+ for (let e = 2; e >= 1; e--) { ctx.save(); ctx.globalAlpha = 0.22 * (3 - e) / 2; aw(ctx, 'ACCURACY', 40 + e * 20, 190 - e * 4, 92, { outline: true, lw: 1.6 }); ctx.restore(); }
352
+ aw(ctx, 'ACCURACY', 40, 190, 92, { outline: true, lw: 3 });
353
+ const p = H.ease.inOut(XS(t, 10.85, 12.3));
354
+ const v = Math.round(61 + 36 * p);
355
+ const punch = t >= 12.3 ? 1 + 0.18 * Math.exp(-(t - 12.3) * 10) : 1;
356
+ ctx.save(); ctx.translate(40, 540); ctx.scale(punch, punch); const w = heroWord(ctx, v + '%', 0, 0, 250, { speed: true, slice: t >= 12.55 ? { at: 0.64, h: 30, shift: 22, tick: 12 } : null }); ctx.restore();
357
+ if (t >= 12.3) { // emphasis box draws around the number
358
+ const k = H.ease.out(XS(t, 12.3, 12.55));
359
+ ctx.save(); ctx.strokeStyle = INK; ctx.lineWidth = 6; ctx.beginPath();
360
+ ctx.rect(22, 262, (w + 40) * k, 306); ctx.stroke(); ctx.restore();
361
+ }
362
+ // bar chart of training rounds
363
+ const x0 = 720, y0 = 600, cw = 520, ch = 360, n = 12;
364
+ ctx.fillStyle = INK; ctx.fillRect(x0, y0, cw, 4);
365
+ for (let i = 0; i < n; i++) {
366
+ const acc = 61 + 36 * (1 - Math.exp(-i / 3.2)) / (1 - Math.exp(-(n - 1) / 3.2));
367
+ const k = H.ease.out(XS(t, 10.85 + i * 0.12, 11.05 + i * 0.12));
368
+ const bh = ((acc - 40) / 60) * ch * k, bx = x0 + i * (cw / n) + 4, bw = cw / n - 10;
369
+ if (bh <= 0) continue;
370
+ if (i % 2) { // alternate rounds: 3px stripe hatch held by a hairline rule
371
+ for (let yy = y0 - 3; yy > y0 - bh; yy -= 6) ctx.fillRect(bx, Math.max(y0 - bh, yy - 3), bw, Math.min(3, yy - (y0 - bh)));
372
+ ctx.strokeStyle = INK; ctx.lineWidth = 2; ctx.strokeRect(bx + 1, y0 - bh + 1, bw - 2, bh - 1);
373
+ } else ctx.fillRect(bx, y0 - bh, bw, bh);
374
+ }
375
+ ctx.font = '700 28px "Space Mono"'; ctx.fillText('TRAINING ROUNDS →', x0, y0 + 40);
376
+ if (t >= 12.3) tag(ctx, '97%', x0 + cw - 90, y0 - ch * (57 / 60) - 30, { size: 26 });
377
+ }
378
+ function ktTake(ctx, W, H_, t) {
379
+ const sl = (c) => (t >= c ? 1 + 0.4 * Math.pow(1 - H.ease.out(XS(t, c, c + 0.16)), 2) : 0);
380
+ const settled = (c) => t >= c + 0.16;
381
+ let s = sl(13.0); if (s) { ctx.save(); ctx.translate(40, 270); ctx.scale(s, s);
382
+ heroWord(ctx, 'MORE EXAMPLES', 0, 0, 230, { fitW: W - 80, echo: settled(13.0) ? 3 : 0 }); ctx.restore(); }
383
+ s = sl(13.25); if (s) { ctx.save(); ctx.translate(40, 420); ctx.scale(s, s);
384
+ if (settled(13.25)) for (let e = 3; e >= 1; e--) { ctx.globalAlpha = 0.22 * (4 - e) / 3; arrowShape(ctx, e * 26, -50 - e * 4, 300 + e * 26, 26, 70); ctx.strokeStyle = INK; ctx.lineWidth = 2; ctx.lineJoin = 'miter'; ctx.stroke(); }
385
+ ctx.globalAlpha = 1; ctx.fillStyle = INK; arrowShape(ctx, 0, -50, 300, 26, 70); ctx.fill();
386
+ ctx.fillStyle = BG; for (let k = 0; k < 2; k++) ctx.fillRect(0, -50 + 4 + k * 5, 240, 2); // knocked-out speed lines
387
+ ctx.restore(); }
388
+ s = sl(13.5); if (s) { ctx.save(); ctx.translate(40, 690); ctx.scale(s, s);
389
+ if (t < 13.8) {
390
+ if (settled(13.5)) for (let e = 2; e >= 1; e--) { ctx.save(); ctx.globalAlpha = 0.2 * (3 - e) / 2; aw(ctx, 'BETTER GUESSES', e * 24, -e * 4, 230, { fitW: W - 80, outline: true, lw: 2 }); ctx.restore(); }
391
+ aw(ctx, 'BETTER GUESSES', 0, 0, 230, { fitW: W - 80, outline: true, lw: 5 });
392
+ } else heroWord(ctx, 'BETTER GUESSES', 0, 0, 230, { fitW: W - 80, dots: 'bg', echo: 2 });
393
+ ctx.restore(); }
394
+ }
395
+ function sceneLayer(W, H_, fn, t) { const c = H.layer(W, H_), x = c.getContext('2d'); fn(x, W, H_, t); return c; }
396
+
397
+ function explainKT(ctx, W, H_, t) {
398
+ ctx.fillStyle = BG; ctx.fillRect(0, 0, W, H_);
399
+ const slam = [0, 0.5, 1.0, 7.25, 7.97, 9.6, 12.3, 13.0, 13.5].reduce((a, c) => a + (t >= c ? Math.exp(-(t - c) * 14) : 0), 0);
400
+ const sr = H.rng(Math.floor(t * 30) + 5);
401
+ ctx.save(); ctx.translate((sr() - 0.5) * 12 * slam, (sr() - 0.5) * 12 * slam);
402
+ if (t < 2.5) {
403
+ // hook: the stack slams in, then shrinks into the corner header
404
+ const k = H.ease.inOut(XS(t, 1.85, 2.45));
405
+ ctx.save(); ctx.translate(30 * k, 20 * k); ctx.scale(1 - 0.93 * k, 1 - 0.93 * k); ktHook(ctx, W, H_, t); ctx.restore();
406
+ if (t > 2.3) header(ctx);
407
+ if (t > 2.3) ktData(ctx, W, H_, t);
408
+ } else if (t < 6.0) {
409
+ header(ctx); ktData(ctx, W, H_, t);
410
+ } else if (t < 10.5) {
411
+ header(ctx);
412
+ if (t < 6.4) {
413
+ // slice-glitch cut: the data scene is torn into bands that fly apart
414
+ const p = H.ease.in(XS(t, 6.0, 6.4));
415
+ const A = sceneLayer(W, H_, ktData, 5.99), B = sceneLayer(W, H_, ktLoop, t);
416
+ ctx.drawImage(B, 0, 0);
417
+ const bands = 9, bh = H_ / bands;
418
+ for (let i = 0; i < bands; i++) {
419
+ const dir = i % 2 ? 1 : -1, off = dir * p * W * (1.1 + (i % 3) * 0.35);
420
+ ctx.fillStyle = BG; if (p < 0.98) ctx.fillRect(off, i * bh, W, bh);
421
+ ctx.drawImage(A, 0, i * bh, W, bh, off, i * bh, W, bh);
422
+ }
423
+ } else ktLoop(ctx, W, H_, t);
424
+ } else if (t < 13.0) {
425
+ header(ctx);
426
+ if (t < 10.8) {
427
+ // whip-pan: scene smears off left with echo copies, next one arrives from the right
428
+ const p = H.ease.inOut(XS(t, 10.5, 10.8));
429
+ const A = sceneLayer(W, H_, ktLoop, 10.49), B = sceneLayer(W, H_, ktProgress, t);
430
+ for (let e = 3; e >= 0; e--) { ctx.globalAlpha = e ? 0.18 : 1; ctx.drawImage(A, -p * W * 1.2 + e * 60 * p, 0); ctx.drawImage(B, W * (1 - p) + e * 60 * (1 - p) * (p > 0 ? 1 : 0), 0); }
431
+ ctx.globalAlpha = 1;
432
+ } else ktProgress(ctx, W, H_, t);
433
+ } else {
434
+ ktTake(ctx, W, H_, t);
435
+ }
436
+ ctx.restore();
437
+ // one-frame inverted flashes on the key hits (wrong guess, 97%)
438
+ if ((t >= 7.97 && t < 8.0) || (t >= 12.3 && t < 12.333)) { ctx.save(); ctx.globalCompositeOperation = 'difference'; ctx.fillStyle = '#fff'; ctx.fillRect(0, 0, W, H_); ctx.restore(); }
439
+ H.grain(ctx, W, H_, 0.04, 91 + Math.floor(t * 12));
440
+ }
441
+
442
+ registerStyle({
443
+ id: 'kinetic-type',
444
+ name: 'Kinetic Type',
445
+ category: 'Generative & Type',
446
+ description: 'A typographic music-video frame: condensed words cropped edge to edge with zero leading, filled vs outlined, strobe echoes, a slice glitch and one word stretched and filled with a halftone dreamscape — strictly two colours.',
447
+ motion: 'One word per beat at 120 bpm: THE punches in, MACHINES whips in from the right trailing outline echoes, LEARNED slams up with a one-frame inverted flash, the TO tab wipes up, DREAM springs open and its halftone moon develops; then the slice glitch and echoes pulse on every beat.',
448
+ fonts: ['400 200px "Anton"', '400 60px "Inter"', '800 60px "Inter"', '700 14px "Space Mono"', '400 14px "Space Mono"'],
449
+ duration: 5,
450
+ frameT: 4.9,
451
+ readsPixels: true,
452
+ explainerDuration: 14.5,
453
+ explainerNote: 'Words slam in on the beat and do the explaining; scenes change with a slice-glitch tear and a whip-pan, arrows are heavy rules with chevrons, labels are inverted mono tags that drop onto their targets, and the counters are huge Anton numerals with a strobe-flash on the key hits.',
454
+ explainer(ctx, W, H_, t) { explainKT(ctx, W, H_, t); },
455
+ // Sound: a type-driven music video. A low 120 bpm pulse bed (the cut grid: beats at 0, 0.5, 1.0 ... 13.0, 13.5)
456
+ // drops out for each hard cut; every word that slams in gets a punchy kick + snap (bigger with a synth stab on the
457
+ // key hits); the slice-glitch cut is a real glitch tear, the whip-pan one fast whoosh, counters tick as short
458
+ // filtered blips, the bars of the chart are pitched plucks. Timings mirror explainKT / kt* scenes (slam list etc).
459
+ explainerSound(k) {
460
+ const E = H.ease;
461
+ const pan = (px) => H.clamp((px / 1280) * 2 - 1, -1, 1) * 0.7;
462
+ const when = (t0, t1, ease, u) => { for (let i = 0; i <= 400; i++) { const t = t0 + ((t1 - t0) * i) / 400; if (ease(i / 400) >= u - 1e-6) return t; } return t1; };
463
+ // word slam: kick body + bright snap
464
+ const slam = (t, g, p = 0) => { k.tone(t, { f: [[0, 160], [0.12, 46]], dur: 0.26, gain: 0.42 * g, a: 0.002, pan: p }); k.noise(t, { dur: 0.06, filter: 'bandpass', f: 1800, q: 0.8, gain: 0.22 * g, a: 0.001, pan: p }); k.noise(t, { dur: 0.12, color: 'brown', filter: 'lowpass', f: 400, gain: 0.2 * g, a: 0.002, pan: p }); };
465
+ // detuned saw stab
466
+ const stab = (t, notes, dur, g) => notes.forEach((n, i) => { for (const det of [-8, 8]) k.tone(t, { f: n, type: 'sawtooth', detune: det, dur, gain: 0.03 * g, a: 0.004, hold: dur * 0.3, filter: 'lowpass', f2: [[0, 2200], [dur, 500]], q: 0.8, pan: (i - (notes.length - 1) / 2) * 0.3, verb: 0.2 }); });
467
+ const blip = (t, f, g, p) => k.tone(t, { f, type: 'square', dur: 0.03, gain: 0.04 * g, a: 0.001, hold: 0.015, filter: 'lowpass', f2: 2000, pan: p, minor: true });
468
+ const zip = (t, dur, p) => k.noise(t, { dur, filter: 'bandpass', f: [[0, 700], [dur, 2000]], q: 1.5, gain: 0.07, a: dur * 0.5, pan: p, minor: true });
469
+ const tagIn = (t0, p) => k.play('tock', when(t0, t0 + 0.25, E.back, 1), { f: 650, gain: 0.32, pan: p, minor: true });
470
+
471
+ // ---- music: low pulse on the 120 bpm grid, out for the cuts
472
+ k.music('pulse', 0, 6.0, { bpm: 120, gain: 0.5, chords: [['A2', 'E3', 'A3', 'C4'], ['F2', 'C3', 'A3', 'C4'], ['G2', 'D3', 'G3', 'B3']] });
473
+ k.music('pulse', 6.5, 10.5, { bpm: 120, gain: 0.45, chords: [['D3', 'A3', 'C4', 'F4'], ['E3', 'B3', 'D4', 'G#4']] });
474
+ k.music('pulse', 11.0, 12.3, { bpm: 120, gain: 0.45, chords: [['F2', 'C3', 'A3', 'E4']] });
475
+ k.music('pulse', 13.0, 14.5, { bpm: 120, gain: 0.5, chords: [['C3', 'G3', 'C4', 'E4']] });
476
+
477
+ // ---- hook: HOW DOES / AI / LEARN? slam on beats 0, 0.5, 1.0; the stack shrinks to the header (1.85-2.45)
478
+ slam(0.0, 1, -0.3);
479
+ slam(0.5, 0.9, 0.3); k.noise(0.42, { dur: 0.1, filter: 'highpass', f: [[0, 1500], [0.1, 3500]], gain: 0.06, a: 0.08, pan: 0.6, minor: true }); // AI whips in
480
+ slam(1.0, 1.2, 0.2); stab(1.0, ['A3', 'C4', 'E4'], 0.4, 1);
481
+ k.tone(1.85, { f: [[0, 300], [0.6, 90]], type: 'triangle', dur: 0.6, gain: 0.05, a: 0.1, minor: true }); // shrink away
482
+
483
+ // ---- data: model box draws (2.3-2.7), arrows, counter, 1,000,000
484
+ k.tone(2.3, { f: [[0, 120], [0.4, 420]], type: 'sawtooth', dur: 0.42, gain: 0.04, a: 0.05, hold: 0.3, filter: 'lowpass', f2: 900, pan: pan(1070), minor: true });
485
+ [0, 1, 2].forEach((i) => zip(2.4 + i * 0.08, 0.38, -0.3));
486
+ { let prev = 0, last = -1; for (let t = 2.62; t <= 4.98; t += 0.002) { const s = Math.floor(E.inOut(XS(t, 2.6, 5.0)) * 32); if (s !== prev && t - last >= 1 / 10) { blip(t, 700 + 500 * XS(t, 2.6, 5.0), 1, -0.5); last = t; } prev = s; } }
487
+ slam(5.0, 1, -0.5); stab(5.0, ['G3', 'B3', 'D4'], 0.3, 0.8);
488
+
489
+ // ---- slice-glitch tear 6.0-6.4 (bands fly apart, ease in)
490
+ k.play('glitch', 6.0, { gain: 1.1 });
491
+ k.noise(6.05, { dur: 0.35, filter: 'bandpass', f: [[0, 600], [0.35, 3000]], q: 0.9, gain: 0.12, a: 0.3 });
492
+
493
+ // ---- loop: arrows, DOG? slam, wrong X, error dashes, weights jump, pulse through, CAT ✓
494
+ zip(6.55, 0.3, pan(390)); zip(7.0, 0.2, pan(850));
495
+ slam(7.25, 1, pan(1050)); tagIn(7.35, pan(950));
496
+ k.noise(7.85, { dur: 0.12, filter: 'bandpass', f: [[0, 900], [0.12, 2200]], q: 1, gain: 0.12, a: 0.08, pan: pan(1050) }); // first stroke of the X
497
+ slam(7.97, 1.3, pan(1050)); stab(7.97, ['A#2', 'E3', 'A3'], 0.45, 1.2); // second stroke + flash: wrong
498
+ // (the CHECK tag lands at 8.09 inside the wrong hit, so it stays silent)
499
+ k.withMinor(() => { for (let i = 0; i < 6; i++) { const u = (i + 0.5) / 6, tt = when(8.45, 8.9, E.inOut, u); blip(tt, 520 - i * 30, 1.2, pan(1050 - 410 * u)); } }); // error dashes run back
500
+ tagIn(8.6, pan(760));
501
+ k.tone(8.95, { f: [[0, 180], [0.12, 520]], type: 'sawtooth', dur: 0.14, gain: 0.05, a: 0.02, hold: 0.06, filter: 'lowpass', f2: 1200, pan: pan(640) }); // weights jump (back ease overshoot)
502
+ k.play('click', when(8.95, 9.35, E.back, 1), { f: 1500, gain: 1.1, pan: pan(640), minor: true });
503
+ k.tone(9.3, { f: [[0, 220], [0.3, 660]], type: 'square', dur: 0.3, gain: 0.03, a: 0.02, hold: 0.2, filter: 'lowpass', f2: 1400, pan: 0, minor: true }); // data pulse runs through
504
+ slam(9.6, 1.1, pan(1030)); stab(9.6, ['C4', 'E4', 'G4'], 0.35, 1);
505
+ stab(9.8, ['G4', 'C5'], 0.4, 0.6); // tick drawn
506
+
507
+ // ---- whip-pan 10.5-10.8
508
+ k.play('whoosh', 10.48, { dur: 0.34, top: 2200, gain: 0.8 });
509
+
510
+ // ---- progress: bars rise (10.85 + i*0.12), counter, 97% slam
511
+ k.withMinor(() => { for (let i = 0; i < 12; i++) { const acc = 61 + 36 * (1 - Math.exp(-i / 3.2)) / (1 - Math.exp(-11 / 3.2)); k.tone(10.85 + i * 0.12 + 0.04, { f: 110 * Math.pow(2, (acc - 61) / 36 * 1.2), type: 'triangle', dur: 0.15, gain: 0.12, a: 0.003, pan: pan(740 + i * 43) }); } });
512
+ { let prev = 0, last = -1; for (let t = 10.86; t <= 12.28; t += 0.002) { const s = Math.floor(E.inOut(XS(t, 10.85, 12.3)) * 36), near = ((t - 10.89) % 0.12 + 0.12) % 0.12 < 0.04; if (s !== prev && !near && t - last >= 1 / 8) { k.noise(t, { dur: 0.015, filter: 'bandpass', f: 2200, q: 2, gain: 0.16, a: 0.001, pan: -0.5, minor: true }); last = t; } prev = s; } }
513
+ slam(12.3, 1.6, -0.3); k.play('impact', 12.3, { gain: 0.9, pan: -0.3 });
514
+ stab(12.3, ['A3', 'C#4', 'E4', 'A4'], 0.7, 1.4);
515
+ k.noise(12.3, { dur: 0.25, filter: 'bandpass', f: [[0, 800], [0.25, 1800]], q: 1.2, gain: 0.05, a: 0.15, minor: true }); // box draws round it
516
+
517
+ // ---- takeaway: three slams on the beat, resolve as BETTER GUESSES fills in (13.8)
518
+ slam(13.0, 0.85, -0.2);
519
+ slam(13.25, 0.7, -0.3); zip(13.22, 0.1, -0.3);
520
+ slam(13.5, 0.95, 0); stab(13.5, ['C4', 'E4', 'G4'], 0.3, 0.7);
521
+ stab(13.8, ['C3', 'G3', 'C4', 'E4', 'G4'], 0.65, 1);
522
+ },
523
+ draw(ctx, W, H_, t) {
524
+ ctx.fillStyle = BG; ctx.fillRect(0, 0, W, H_);
525
+ const beat = (t * 2) % 1; // 120 bpm
526
+ const kick = Math.exp(-beat * 6);
527
+ ctx.textBaseline = 'alphabetic'; ctx.textAlign = 'left';
528
+ // per-word cue times (on the beat)
529
+ const P = (t0, d) => H.clamp((t - t0) / d);
530
+ const pThe = P(0.0, 0.16), pMac = P(0.5, 0.26), pLea = P(1.0, 0.2), pSlice = t >= 1.5 ? 1 : 0;
531
+ const pTo = P(2.0, 0.18), pDr = P(2.5, 0.32), pDots = P(2.6, 0.5);
532
+ // camera shake on each slam, decaying fast
533
+ const slam = [0, 1.0, 2.5].reduce((a, c) => a + (t >= c ? Math.exp(-(t - c) * 14) : 0), 0);
534
+ const sr = H.rng(Math.floor(t * 30) + 1);
535
+ ctx.save();
536
+ ctx.translate((sr() - 0.5) * 14 * slam, (sr() - 0.5) * 14 * slam);
537
+
538
+ // ---------- ROW 1: "THE" light + "MACHINES" outline, with strobe echoes ----------
539
+ const C1 = 128, C2 = 262, gap = 12, top = 16;
540
+ ctx.font = '400 200px "Anton"';
541
+ const aCap = ctx.measureText('MACHINES').actualBoundingBoxAscent / 200; // cap-height ratio
542
+ const s1 = C1 / aCap;
543
+ ctx.font = `400 ${s1}px "Anton"`;
544
+ const mM = ctx.measureText('MACHINES');
545
+ const mW = mM.actualBoundingBoxLeft + mM.actualBoundingBoxRight;
546
+ ctx.font = '400 100px "Inter"';
547
+ const iCap = ctx.measureText('THE').actualBoundingBoxAscent / 100;
548
+ const sT = C1 / iCap;
549
+ ctx.font = `400 ${sT}px "Inter"`;
550
+ const mT = ctx.measureText('THE');
551
+ const tW = mT.actualBoundingBoxLeft + mT.actualBoundingBoxRight;
552
+ const inner = 22;
553
+ const row1W = tW + inner + mW;
554
+ const sx1 = (W + 12) / row1W;
555
+ const base1 = top + C1;
556
+ ctx.save(); ctx.translate(-4, 0); ctx.scale(sx1, 1);
557
+ if (pThe > 0) {
558
+ const sc = 1 + 0.35 * Math.pow(1 - H.ease.out(pThe), 2);
559
+ ctx.save(); ctx.translate(tW / 2, base1 - C1 / 2); ctx.scale(sc, sc); ctx.translate(-tW / 2, -(base1 - C1 / 2));
560
+ ctx.fillStyle = INK; ctx.fillText('THE', mT.actualBoundingBoxLeft, base1); ctx.restore();
561
+ }
562
+ const mx = tW + inner + mM.actualBoundingBoxLeft;
563
+ ctx.font = `400 ${s1}px "Anton"`; ctx.lineJoin = 'miter'; ctx.strokeStyle = INK;
564
+ if (pMac > 0) {
565
+ // whip in from the right; echoes trail horizontally while moving, then fan upward
566
+ const em = H.ease.out(pMac), offX = (1 - em) * 900;
567
+ const settle = H.ease.inOut(P(0.76, 0.4));
568
+ for (let e = 3; e >= 1; e--) {
569
+ ctx.globalAlpha = 0.18 * (4 - e) * (0.6 + 0.4 * (t > 1.5 ? kick : 1)); ctx.lineWidth = 1.4 / sx1;
570
+ const ex = offX + e * 70 * (1 - em) * 3, ey = -e * 30 * settle;
571
+ ctx.strokeText('MACHINES', mx + ex, base1 + ey);
572
+ }
573
+ ctx.globalAlpha = 1; ctx.lineWidth = 3.2 / sx1;
574
+ ctx.strokeText('MACHINES', mx + offX, base1);
575
+ }
576
+ ctx.restore();
577
+
578
+ // ---------- ROW 2: "LEARNED" massive fill, stretched edge to edge, slice glitch ----------
579
+ const s2 = C2 / aCap;
580
+ ctx.font = `400 ${s2}px "Anton"`;
581
+ const mL = ctx.measureText('LEARNED');
582
+ const lW = mL.actualBoundingBoxLeft + mL.actualBoundingBoxRight;
583
+ const sx2 = (W + 30) / lW;
584
+ const base2 = base1 + gap + C2;
585
+ const lay = H.layer(W, H_), lc = lay.getContext('2d');
586
+ lc.save(); lc.translate(-15, 0); lc.scale(sx2, 1);
587
+ lc.font = `400 ${s2}px "Anton"`; lc.fillStyle = INK; lc.textBaseline = 'alphabetic';
588
+ lc.fillText('LEARNED', mL.actualBoundingBoxLeft, base2);
589
+ lc.restore();
590
+ // knocked-out speed lines through the lower third
591
+ lc.globalCompositeOperation = 'destination-out';
592
+ for (let k = 0; k < 3; k++) lc.fillRect(0, base2 - C2 * (0.2 + k * 0.045), W, 4 - k);
593
+ // slice glitch: cut a band and shove it sideways on the beat
594
+ const sy = Math.round(base2 - C2 * 0.6), sh = 28, shift = 22 + Math.round(kick * 26);
595
+ if (pSlice) {
596
+ const band = H.layer(W, sh); band.getContext('2d').drawImage(lay, 0, sy, W, sh, 0, 0, W, sh);
597
+ lc.fillRect(0, sy, W, sh);
598
+ lc.globalCompositeOperation = 'source-over'; lc.drawImage(band, shift, sy);
599
+ }
600
+ if (pLea > 0) {
601
+ const el = H.ease.out(pLea), dy = (1 - el) * 420;
602
+ // squash on impact (2 frames), then hold
603
+ const sq = pLea >= 1 ? 1 + 0.06 * Math.exp(-(t - 1.2) * 20) : 1;
604
+ ctx.save(); ctx.translate(0, base2); ctx.scale(1, 1 / sq); ctx.translate(0, -base2 + dy);
605
+ ctx.drawImage(lay, 0, 0); ctx.restore();
606
+ if (pSlice) { ctx.fillStyle = INK; ctx.fillRect(0, sy + sh + 5, shift - 12, 2); ctx.fillRect(0, sy - 7, shift * 0.6, 2); }
607
+ }
608
+
609
+ // ---------- ROW 3: "TO" tab + "DREAM" stretched with halftone image ----------
610
+ const r3top = base2 + gap;
611
+ const r3h = H_ - r3top - 6;
612
+ // TO tab: filled block with knocked-out letters, rotated
613
+ const tabW = 104;
614
+ ctx.save();
615
+ const tabH = r3h * H.ease.out(pTo);
616
+ ctx.beginPath(); ctx.rect(0, r3top + r3h - tabH, tabW, tabH); ctx.clip();
617
+ ctx.fillStyle = INK; ctx.fillRect(0, r3top, tabW, r3h);
618
+ ctx.save();
619
+ ctx.translate(tabW / 2 + 2, r3top + 14); ctx.rotate(Math.PI / 2);
620
+ ctx.font = '800 92px "Inter"'; ctx.fillStyle = BG; ctx.textBaseline = 'middle';
621
+ ctx.fillText('TO', 0, 2);
622
+ ctx.restore();
623
+ ctx.font = '700 14px "Space Mono"'; ctx.fillStyle = BG;
624
+ ctx.save(); ctx.translate(20, H_ - 14); ctx.rotate(-Math.PI / 2); ctx.fillText('02:47', 0, 0); ctx.restore();
625
+ ctx.save(); ctx.font = '700 13px "Space Mono"'; ctx.fillStyle = BG; ctx.textBaseline = 'alphabetic';
626
+ ctx.translate(tabW - 16, H_ - 14); ctx.rotate(-Math.PI / 2); ctx.fillText('[VERSE 02]', 0, 0); ctx.restore();
627
+ const chx = tabW / 2, chy = r3top + 150, cs = 9 + kick * 4;
628
+ ctx.strokeStyle = BG; ctx.lineWidth = 2;
629
+ ctx.beginPath(); ctx.moveTo(chx - cs, chy); ctx.lineTo(chx + cs, chy); ctx.moveTo(chx, chy - cs); ctx.lineTo(chx, chy + cs); ctx.stroke();
630
+ ctx.beginPath(); ctx.arc(chx, chy, cs * 0.6, 0, Math.PI * 2); ctx.stroke();
631
+ ctx.restore();
632
+
633
+ // DREAM: stretched to fill the remaining box
634
+ const dx0 = tabW + 14, dW = W - dx0 + 20, dH = r3h;
635
+ ctx.font = '400 200px "Anton"';
636
+ const mD = ctx.measureText('DREAM');
637
+ const dWnat = mD.actualBoundingBoxLeft + mD.actualBoundingBoxRight, dHnat = mD.actualBoundingBoxAscent;
638
+ const sxD = dW / dWnat, syD = ((dH - 6) / dHnat) * Math.max(0.02, pDr <= 0 ? 0 : H.ease.back(pDr));
639
+ const dTop = r3top + (dH - 6) * (1 - syD / ((dH - 6) / dHnat));
640
+ const mask = H.layer(W, H_), mk = mask.getContext('2d');
641
+ mk.save(); mk.translate(dx0, dTop); mk.scale(sxD, syD);
642
+ mk.font = '400 200px "Anton"'; mk.fillStyle = '#fff'; mk.textBaseline = 'alphabetic';
643
+ mk.fillText('DREAM', mD.actualBoundingBoxLeft, dHnat);
644
+ mk.restore();
645
+ // halftone dreamscape: moon + drifting clouds rendered as rotated dot screen
646
+ const img = H.layer(W, H_), ic = img.getContext('2d');
647
+ const nz = H.makeNoise(31);
648
+ const step = 9, ang = Math.PI / 4, ca = Math.cos(ang), sa = Math.sin(ang);
649
+ ic.fillStyle = INK;
650
+ const moonX = 860, moonY = r3top + dH * 0.42, moonR = 120;
651
+ for (let v = -900; v < 1500; v += step) {
652
+ for (let u = -900; u < 1900; u += step) {
653
+ const x = u * ca - v * sa + 200, y = u * sa + v * ca - 200;
654
+ if (x < dx0 - 10 || x > W + 10 || y < r3top - 10 || y > H_ + 10) continue;
655
+ // image brightness
656
+ const cl = nz.fbm2(x * 0.0035 + t * 0.08, y * 0.009, 4) * 0.5 + 0.5;
657
+ const md = Math.hypot(x - moonX, y - moonY);
658
+ // crescent moon: disc minus offset disc, soft glow around
659
+ const md2 = Math.hypot(x - moonX - 46, y - moonY + 26);
660
+ const disc = md < moonR ? 1 : 0, bite = md2 < moonR * 0.92 ? 1 : 0;
661
+ const glow = Math.max(0, 0.42 - Math.max(0, md - moonR) / 260);
662
+ const cloud = Math.pow(H.clamp(cl * 1.6 - 0.55), 1.3); // banded drifting clouds
663
+ const sky = 0.06 + 0.22 * H.smooth(r3top, H_, y); // brighter toward horizon
664
+ let b = disc && !bite ? 1 : Math.max(sky, glow * (1 - bite * 0.7), cloud * 0.9);
665
+ b = H.clamp(b);
666
+ const rad = Math.sqrt(b) * step * 0.62 * H.ease.out(H.clamp(pDots * 1.6 - (1 - b) * 0.6));
667
+ if (rad < 0.5) continue;
668
+ ic.beginPath(); ic.arc(x, y, rad, 0, Math.PI * 2); ic.fill();
669
+ }
670
+ }
671
+ // mask image by DREAM letters
672
+ ic.globalCompositeOperation = 'destination-in'; ic.drawImage(mask, 0, 0);
673
+ if (pDr > 0) ctx.drawImage(img, 0, 0);
674
+ // letter outline so the word holds its shape
675
+ if (pDr > 0) { ctx.save(); ctx.translate(dx0, dTop); ctx.scale(sxD, syD);
676
+ ctx.font = '400 200px "Anton"'; ctx.strokeStyle = INK; ctx.lineWidth = 2.4 / Math.min(sxD, syD);
677
+ ctx.strokeText('DREAM', mD.actualBoundingBoxLeft, dHnat);
678
+ ctx.restore(); }
679
+
680
+ ctx.restore(); // camera shake
681
+ // one-frame inverted flash on the LEARNED and DREAM impacts (music-video cut)
682
+ if ((t >= 1.2 && t < 1.2 + 1 / 30) || (t >= 2.82 && t < 2.82 + 1 / 30)) {
683
+ ctx.save(); ctx.globalCompositeOperation = 'difference'; ctx.fillStyle = '#ffffff'; ctx.fillRect(0, 0, W, H_); ctx.restore();
684
+ }
685
+
686
+ // ---------- finish: subtle print/video grain ----------
687
+ H.grain(ctx, W, H_, 0.04, 77 + Math.floor(t * 12));
688
+ },
689
+ });
690
+ }