videowright 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (306) hide show
  1. package/README.md +91 -0
  2. package/dist/cli/argv.d.ts +28 -0
  3. package/dist/cli/argv.d.ts.map +1 -0
  4. package/dist/cli/argv.js +115 -0
  5. package/dist/cli/argv.js.map +1 -0
  6. package/dist/cli/bin.d.ts +7 -0
  7. package/dist/cli/bin.d.ts.map +1 -0
  8. package/dist/cli/bin.js +10 -0
  9. package/dist/cli/bin.js.map +1 -0
  10. package/dist/cli/dev.d.ts +19 -0
  11. package/dist/cli/dev.d.ts.map +1 -0
  12. package/dist/cli/dev.js +104 -0
  13. package/dist/cli/dev.js.map +1 -0
  14. package/dist/cli/discover.d.ts +29 -0
  15. package/dist/cli/discover.d.ts.map +1 -0
  16. package/dist/cli/discover.js +104 -0
  17. package/dist/cli/discover.js.map +1 -0
  18. package/dist/cli/discover_project.d.ts +29 -0
  19. package/dist/cli/discover_project.d.ts.map +1 -0
  20. package/dist/cli/discover_project.js +108 -0
  21. package/dist/cli/discover_project.js.map +1 -0
  22. package/dist/cli/errors.d.ts +10 -0
  23. package/dist/cli/errors.d.ts.map +1 -0
  24. package/dist/cli/errors.js +13 -0
  25. package/dist/cli/errors.js.map +1 -0
  26. package/dist/cli/ffmpeg.d.ts +57 -0
  27. package/dist/cli/ffmpeg.d.ts.map +1 -0
  28. package/dist/cli/ffmpeg.js +122 -0
  29. package/dist/cli/ffmpeg.js.map +1 -0
  30. package/dist/cli/index.d.ts +7 -0
  31. package/dist/cli/index.d.ts.map +1 -0
  32. package/dist/cli/index.js +152 -0
  33. package/dist/cli/index.js.map +1 -0
  34. package/dist/cli/playwright_check.d.ts +44 -0
  35. package/dist/cli/playwright_check.d.ts.map +1 -0
  36. package/dist/cli/playwright_check.js +20 -0
  37. package/dist/cli/playwright_check.js.map +1 -0
  38. package/dist/cli/prompt.d.ts +13 -0
  39. package/dist/cli/prompt.d.ts.map +1 -0
  40. package/dist/cli/prompt.js +47 -0
  41. package/dist/cli/prompt.js.map +1 -0
  42. package/dist/cli/render.d.ts +60 -0
  43. package/dist/cli/render.d.ts.map +1 -0
  44. package/dist/cli/render.js +471 -0
  45. package/dist/cli/render.js.map +1 -0
  46. package/dist/cli/script_cmd.d.ts +26 -0
  47. package/dist/cli/script_cmd.d.ts.map +1 -0
  48. package/dist/cli/script_cmd.js +88 -0
  49. package/dist/cli/script_cmd.js.map +1 -0
  50. package/dist/cli/time_shim.d.ts +44 -0
  51. package/dist/cli/time_shim.d.ts.map +1 -0
  52. package/dist/cli/time_shim.js +390 -0
  53. package/dist/cli/time_shim.js.map +1 -0
  54. package/dist/cli/ts_loader.d.ts +28 -0
  55. package/dist/cli/ts_loader.d.ts.map +1 -0
  56. package/dist/cli/ts_loader.js +95 -0
  57. package/dist/cli/ts_loader.js.map +1 -0
  58. package/dist/cli/vite_helpers.d.ts +62 -0
  59. package/dist/cli/vite_helpers.d.ts.map +1 -0
  60. package/dist/cli/vite_helpers.js +273 -0
  61. package/dist/cli/vite_helpers.js.map +1 -0
  62. package/dist/index.d.ts +11 -0
  63. package/dist/index.d.ts.map +1 -0
  64. package/dist/index.js +14 -0
  65. package/dist/index.js.map +1 -0
  66. package/dist/player/hash_router.d.ts +23 -0
  67. package/dist/player/hash_router.d.ts.map +1 -0
  68. package/dist/player/hash_router.js +49 -0
  69. package/dist/player/hash_router.js.map +1 -0
  70. package/dist/player/hud.d.ts +33 -0
  71. package/dist/player/hud.d.ts.map +1 -0
  72. package/dist/player/hud.js +357 -0
  73. package/dist/player/hud.js.map +1 -0
  74. package/dist/player/index.d.ts +123 -0
  75. package/dist/player/index.d.ts.map +1 -0
  76. package/dist/player/index.js +848 -0
  77. package/dist/player/index.js.map +1 -0
  78. package/dist/player/input.d.ts +14 -0
  79. package/dist/player/input.d.ts.map +1 -0
  80. package/dist/player/input.js +90 -0
  81. package/dist/player/input.js.map +1 -0
  82. package/dist/player/slot.d.ts +22 -0
  83. package/dist/player/slot.d.ts.map +1 -0
  84. package/dist/player/slot.js +43 -0
  85. package/dist/player/slot.js.map +1 -0
  86. package/dist/player/transitions/cut.d.ts +7 -0
  87. package/dist/player/transitions/cut.d.ts.map +1 -0
  88. package/dist/player/transitions/cut.js +9 -0
  89. package/dist/player/transitions/cut.js.map +1 -0
  90. package/dist/player/transitions/fade.d.ts +7 -0
  91. package/dist/player/transitions/fade.d.ts.map +1 -0
  92. package/dist/player/transitions/fade.js +18 -0
  93. package/dist/player/transitions/fade.js.map +1 -0
  94. package/dist/player/transitions/index.d.ts +4 -0
  95. package/dist/player/transitions/index.d.ts.map +1 -0
  96. package/dist/player/transitions/index.js +4 -0
  97. package/dist/player/transitions/index.js.map +1 -0
  98. package/dist/player/transitions/slide.d.ts +6 -0
  99. package/dist/player/transitions/slide.d.ts.map +1 -0
  100. package/dist/player/transitions/slide.js +35 -0
  101. package/dist/player/transitions/slide.js.map +1 -0
  102. package/dist/script/index.d.ts +2 -0
  103. package/dist/script/index.d.ts.map +1 -0
  104. package/dist/script/index.js +2 -0
  105. package/dist/script/index.js.map +1 -0
  106. package/dist/script/script.d.ts +10 -0
  107. package/dist/script/script.d.ts.map +1 -0
  108. package/dist/script/script.js +41 -0
  109. package/dist/script/script.js.map +1 -0
  110. package/dist/segment/SegmentRunner.d.ts +52 -0
  111. package/dist/segment/SegmentRunner.d.ts.map +1 -0
  112. package/dist/segment/SegmentRunner.js +187 -0
  113. package/dist/segment/SegmentRunner.js.map +1 -0
  114. package/dist/segment/defineConfig.d.ts +6 -0
  115. package/dist/segment/defineConfig.d.ts.map +1 -0
  116. package/dist/segment/defineConfig.js +7 -0
  117. package/dist/segment/defineConfig.js.map +1 -0
  118. package/dist/segment/defineSegment.d.ts +7 -0
  119. package/dist/segment/defineSegment.d.ts.map +1 -0
  120. package/dist/segment/defineSegment.js +25 -0
  121. package/dist/segment/defineSegment.js.map +1 -0
  122. package/dist/segment/index.d.ts +5 -0
  123. package/dist/segment/index.d.ts.map +1 -0
  124. package/dist/segment/index.js +4 -0
  125. package/dist/segment/index.js.map +1 -0
  126. package/dist/timeline/index.d.ts +73 -0
  127. package/dist/timeline/index.d.ts.map +1 -0
  128. package/dist/timeline/index.js +142 -0
  129. package/dist/timeline/index.js.map +1 -0
  130. package/dist/timeline/loadAudioTrack.d.ts +18 -0
  131. package/dist/timeline/loadAudioTrack.d.ts.map +1 -0
  132. package/dist/timeline/loadAudioTrack.js +44 -0
  133. package/dist/timeline/loadAudioTrack.js.map +1 -0
  134. package/dist/timeline/loadVoiceover.d.ts +18 -0
  135. package/dist/timeline/loadVoiceover.d.ts.map +1 -0
  136. package/dist/timeline/loadVoiceover.js +38 -0
  137. package/dist/timeline/loadVoiceover.js.map +1 -0
  138. package/dist/timeline/resolveTiming.d.ts +28 -0
  139. package/dist/timeline/resolveTiming.d.ts.map +1 -0
  140. package/dist/timeline/resolveTiming.js +63 -0
  141. package/dist/timeline/resolveTiming.js.map +1 -0
  142. package/dist/timeline/validateTiming.d.ts +29 -0
  143. package/dist/timeline/validateTiming.d.ts.map +1 -0
  144. package/dist/timeline/validateTiming.js +62 -0
  145. package/dist/timeline/validateTiming.js.map +1 -0
  146. package/dist/types.d.ts +216 -0
  147. package/dist/types.d.ts.map +1 -0
  148. package/dist/types.js +6 -0
  149. package/dist/types.js.map +1 -0
  150. package/package.json +47 -0
  151. package/skill/SKILL.md +64 -0
  152. package/skill/assets/hello_world/PLAN.md +31 -0
  153. package/skill/assets/hello_world/README.md +27 -0
  154. package/skill/assets/hello_world/audio/audio_plan.md +14 -0
  155. package/skill/assets/hello_world/segments/hello_intro.ts +69 -0
  156. package/skill/assets/hello_world/segments/hello_outro.ts +71 -0
  157. package/skill/assets/hello_world/timeline.ts +15 -0
  158. package/skill/assets/hello_world/voiceover_script/script.md +10 -0
  159. package/skill/assets/install/package.json +10 -0
  160. package/skill/assets/install/tsconfig.json +23 -0
  161. package/skill/assets/styles/editorial-mono/STYLE.md +124 -0
  162. package/skill/assets/styles/editorial-mono/brand.md +85 -0
  163. package/skill/assets/styles/editorial-mono/reference/animations.jsx +752 -0
  164. package/skill/assets/styles/editorial-mono/reference/scenes.html +563 -0
  165. package/skill/assets/styles/editorial-mono/sample/bullet.ts +101 -0
  166. package/skill/assets/styles/editorial-mono/sample/content.ts +104 -0
  167. package/skill/assets/styles/editorial-mono/sample/cta.ts +113 -0
  168. package/skill/assets/styles/editorial-mono/sample/feature.ts +111 -0
  169. package/skill/assets/styles/editorial-mono/sample/grid.ts +97 -0
  170. package/skill/assets/styles/editorial-mono/sample/kinetic.ts +96 -0
  171. package/skill/assets/styles/editorial-mono/sample/section.ts +101 -0
  172. package/skill/assets/styles/editorial-mono/sample/stat.ts +128 -0
  173. package/skill/assets/styles/editorial-mono/sample/title.ts +97 -0
  174. package/skill/assets/styles/editorial-mono/sample/ui-showcase.ts +159 -0
  175. package/skill/assets/styles/editorial-mono/tokens.css +44 -0
  176. package/skill/assets/styles/iso-diagram/STYLE.md +109 -0
  177. package/skill/assets/styles/iso-diagram/brand.md +32 -0
  178. package/skill/assets/styles/iso-diagram/reference/animations.jsx +673 -0
  179. package/skill/assets/styles/iso-diagram/reference/scenes.html +427 -0
  180. package/skill/assets/styles/iso-diagram/sample/bullet.ts +144 -0
  181. package/skill/assets/styles/iso-diagram/sample/content.ts +192 -0
  182. package/skill/assets/styles/iso-diagram/sample/cta.ts +162 -0
  183. package/skill/assets/styles/iso-diagram/sample/feature.ts +205 -0
  184. package/skill/assets/styles/iso-diagram/sample/grid.ts +181 -0
  185. package/skill/assets/styles/iso-diagram/sample/kinetic.ts +102 -0
  186. package/skill/assets/styles/iso-diagram/sample/section.ts +149 -0
  187. package/skill/assets/styles/iso-diagram/sample/stat.ts +164 -0
  188. package/skill/assets/styles/iso-diagram/sample/title.ts +173 -0
  189. package/skill/assets/styles/iso-diagram/sample/ui-showcase.ts +162 -0
  190. package/skill/assets/styles/iso-diagram/tokens.css +40 -0
  191. package/skill/assets/styles/motion-engineering/STYLE.md +106 -0
  192. package/skill/assets/styles/motion-engineering/brand.md +29 -0
  193. package/skill/assets/styles/motion-engineering/reference/animations.jsx +673 -0
  194. package/skill/assets/styles/motion-engineering/reference/scenes.html +513 -0
  195. package/skill/assets/styles/motion-engineering/sample/bullet.ts +176 -0
  196. package/skill/assets/styles/motion-engineering/sample/content.ts +228 -0
  197. package/skill/assets/styles/motion-engineering/sample/cta.ts +209 -0
  198. package/skill/assets/styles/motion-engineering/sample/feature.ts +299 -0
  199. package/skill/assets/styles/motion-engineering/sample/grid.ts +190 -0
  200. package/skill/assets/styles/motion-engineering/sample/kinetic.ts +159 -0
  201. package/skill/assets/styles/motion-engineering/sample/section.ts +196 -0
  202. package/skill/assets/styles/motion-engineering/sample/stat.ts +230 -0
  203. package/skill/assets/styles/motion-engineering/sample/title.ts +219 -0
  204. package/skill/assets/styles/motion-engineering/sample/ui-showcase.ts +267 -0
  205. package/skill/assets/styles/motion-engineering/tokens.css +40 -0
  206. package/skill/assets/styles/neon-terminal/STYLE.md +105 -0
  207. package/skill/assets/styles/neon-terminal/brand.md +27 -0
  208. package/skill/assets/styles/neon-terminal/reference/animations.jsx +673 -0
  209. package/skill/assets/styles/neon-terminal/reference/scenes.html +387 -0
  210. package/skill/assets/styles/neon-terminal/sample/bullet.ts +113 -0
  211. package/skill/assets/styles/neon-terminal/sample/content.ts +117 -0
  212. package/skill/assets/styles/neon-terminal/sample/cta.ts +131 -0
  213. package/skill/assets/styles/neon-terminal/sample/feature.ts +112 -0
  214. package/skill/assets/styles/neon-terminal/sample/grid.ts +128 -0
  215. package/skill/assets/styles/neon-terminal/sample/kinetic.ts +105 -0
  216. package/skill/assets/styles/neon-terminal/sample/section.ts +96 -0
  217. package/skill/assets/styles/neon-terminal/sample/stat.ts +123 -0
  218. package/skill/assets/styles/neon-terminal/sample/title.ts +122 -0
  219. package/skill/assets/styles/neon-terminal/sample/ui-showcase.ts +127 -0
  220. package/skill/assets/styles/neon-terminal/tokens.css +39 -0
  221. package/skill/assets/styles/risograph/STYLE.md +110 -0
  222. package/skill/assets/styles/risograph/brand.md +26 -0
  223. package/skill/assets/styles/risograph/reference/animations.jsx +673 -0
  224. package/skill/assets/styles/risograph/reference/scenes.html +403 -0
  225. package/skill/assets/styles/risograph/sample/bullet.ts +124 -0
  226. package/skill/assets/styles/risograph/sample/content.ts +135 -0
  227. package/skill/assets/styles/risograph/sample/cta.ts +149 -0
  228. package/skill/assets/styles/risograph/sample/feature.ts +152 -0
  229. package/skill/assets/styles/risograph/sample/grid.ts +123 -0
  230. package/skill/assets/styles/risograph/sample/kinetic.ts +125 -0
  231. package/skill/assets/styles/risograph/sample/section.ts +130 -0
  232. package/skill/assets/styles/risograph/sample/stat.ts +145 -0
  233. package/skill/assets/styles/risograph/sample/title.ts +132 -0
  234. package/skill/assets/styles/risograph/sample/ui-showcase.ts +147 -0
  235. package/skill/assets/styles/risograph/tokens.css +39 -0
  236. package/skill/assets/styles/swiss-console/STYLE.md +107 -0
  237. package/skill/assets/styles/swiss-console/brand.md +37 -0
  238. package/skill/assets/styles/swiss-console/reference/animations.jsx +673 -0
  239. package/skill/assets/styles/swiss-console/reference/scenes.html +420 -0
  240. package/skill/assets/styles/swiss-console/sample/bullet.ts +122 -0
  241. package/skill/assets/styles/swiss-console/sample/content.ts +137 -0
  242. package/skill/assets/styles/swiss-console/sample/cta.ts +109 -0
  243. package/skill/assets/styles/swiss-console/sample/feature.ts +163 -0
  244. package/skill/assets/styles/swiss-console/sample/grid.ts +145 -0
  245. package/skill/assets/styles/swiss-console/sample/kinetic.ts +117 -0
  246. package/skill/assets/styles/swiss-console/sample/section.ts +127 -0
  247. package/skill/assets/styles/swiss-console/sample/stat.ts +148 -0
  248. package/skill/assets/styles/swiss-console/sample/title.ts +148 -0
  249. package/skill/assets/styles/swiss-console/sample/ui-showcase.ts +198 -0
  250. package/skill/assets/styles/swiss-console/tokens.css +39 -0
  251. package/skill/install/INSTALL.md +400 -0
  252. package/skill/references/audio/audio_plan.md +199 -0
  253. package/skill/references/audio/build.md +208 -0
  254. package/skill/references/audio/cue_template.md +219 -0
  255. package/skill/references/audio/ffmpeg_cookbook.md +267 -0
  256. package/skill/references/audio/music/music.md +171 -0
  257. package/skill/references/audio/music/providers/elevenlabs.md +170 -0
  258. package/skill/references/audio/music/providers/manual.md +140 -0
  259. package/skill/references/audio/music/providers/openverse.md +265 -0
  260. package/skill/references/audio/sfx/providers/elevenlabs.md +152 -0
  261. package/skill/references/audio/sfx/providers/manual.md +117 -0
  262. package/skill/references/audio/sfx/providers/openverse.md +243 -0
  263. package/skill/references/audio/sfx/sfx.md +149 -0
  264. package/skill/references/audio/styles.md +102 -0
  265. package/skill/references/audio/sync.md +237 -0
  266. package/skill/references/audio/voiceover/animation_sync.md +142 -0
  267. package/skill/references/audio/voiceover/provider_script.md +153 -0
  268. package/skill/references/audio/voiceover/providers/elevenlabs.md +288 -0
  269. package/skill/references/audio/voiceover/providers/manual.md +100 -0
  270. package/skill/references/audio/voiceover/script_writing.md +100 -0
  271. package/skill/references/audio/voiceover/style_intake.md +56 -0
  272. package/skill/references/audio/voiceover/sync_algorithm.md +167 -0
  273. package/skill/references/audio/voiceover.md +296 -0
  274. package/skill/references/audio.md +135 -0
  275. package/skill/references/authoring_segment.md +446 -0
  276. package/skill/references/create_or_edit_video.md +232 -0
  277. package/skill/references/dev_server.md +157 -0
  278. package/skill/references/export.md +145 -0
  279. package/skill/references/new_video.md +117 -0
  280. package/skill/references/project_structure.md +144 -0
  281. package/skill/references/setup.md +109 -0
  282. package/skill/references/setup_new_style.md +158 -0
  283. package/skill/references/styles.md +154 -0
  284. package/skill/references/testing.md +115 -0
  285. package/skill/references/types.md +240 -0
  286. package/src/cli/entry/components/copy_button.ts +42 -0
  287. package/src/cli/entry/components/download_modal.ts +204 -0
  288. package/src/cli/entry/components/empty_state.ts +55 -0
  289. package/src/cli/entry/components/hide_hud_tab.ts +37 -0
  290. package/src/cli/entry/components/icons.ts +31 -0
  291. package/src/cli/entry/components/top_bar.ts +69 -0
  292. package/src/cli/entry/components/video_card.ts +57 -0
  293. package/src/cli/entry/dev_frame.ts +189 -0
  294. package/src/cli/entry/entry_index.ts +16 -0
  295. package/src/cli/entry/entry_video.ts +24 -0
  296. package/src/cli/entry/index.html +12 -0
  297. package/src/cli/entry/parse_slug.ts +14 -0
  298. package/src/cli/entry/render.html +17 -0
  299. package/src/cli/entry/render_entry.ts +121 -0
  300. package/src/cli/entry/styles/base.css +45 -0
  301. package/src/cli/entry/styles/components.css +605 -0
  302. package/src/cli/entry/styles/tokens.css +44 -0
  303. package/src/cli/entry/video.html +22 -0
  304. package/src/cli/entry/views/homepage.ts +66 -0
  305. package/src/cli/entry/views/video_view.ts +286 -0
  306. package/src/cli/entry/virtual.d.ts +8 -0
@@ -0,0 +1,427 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <title>Iso Diagram — Reference Scenes</title>
6
+ <link href="https://fonts.googleapis.com/css2?family=Caveat:wght@400;500;700&family=Nunito:wght@400;600;700&family=JetBrains+Mono:wght@400&display=swap" rel="stylesheet" />
7
+ <link rel="stylesheet" href="../tokens.css" />
8
+ <style>
9
+ html, body { margin: 0; padding: 0; height: 100%; background: #2A2620; font-family: var(--font-body); }
10
+ .iso-canvas { position: absolute; inset: 0; background: var(--color-bg); color: var(--color-fg); overflow: hidden; }
11
+ .iso-canvas::before {
12
+ content: ''; position: absolute; inset: 0; pointer-events: none;
13
+ background-image: radial-gradient(circle, rgba(42,38,32,0.10) 1px, transparent 1.6px);
14
+ background-size: 32px 32px;
15
+ }
16
+ .iso-display { font-family: var(--font-display); font-weight: 500; line-height: 1.0; letter-spacing: 0.005em; }
17
+ .iso-body { font-family: var(--font-body); }
18
+ .iso-mono { font-family: var(--font-mono); }
19
+ .iso-muted { color: var(--color-muted); }
20
+ .iso-accent { color: var(--color-accent); }
21
+ /* draw-on stroke util */
22
+ .iso-stroke { stroke: var(--color-fg); stroke-width: 2.4; fill: none; stroke-linecap: round; stroke-linejoin: round; }
23
+ .iso-stroke-accent { stroke: var(--color-accent); }
24
+ .iso-pencil { color: var(--color-fg); }
25
+ </style>
26
+ </head>
27
+ <body>
28
+ <script src="https://unpkg.com/react@18.3.1/umd/react.development.js" integrity="sha384-hD6/rw4ppMLGNu3tX5cjIb+uRZ7UkRJ6BPkLpg4hAu/6onKUg4lLsHAs9EBPT82L" crossorigin="anonymous"></script>
29
+ <script src="https://unpkg.com/react-dom@18.3.1/umd/react-dom.development.js" integrity="sha384-u6aeetuaXnQ38mYT8rp6sbXaQe3NL9t+IBXmnYxwkUI2Hw4bsp2Wvmx4yRQF1uAm" crossorigin="anonymous"></script>
30
+ <script src="https://unpkg.com/@babel/standalone@7.29.0/babel.min.js" integrity="sha384-m08KidiNqLdpJqLq95G/LEi8Qvjl/xUYll3QILypMoQ65QorJ9Lvtp2RXYGBFj1y" crossorigin="anonymous"></script>
31
+ <div id="root"></div>
32
+ <script type="text/babel" src="animations.jsx"></script>
33
+ <script type="text/babel">
34
+ const SCENE = 4.5;
35
+ const ease = Easing.easeOutCubic;
36
+
37
+ // Stroke-draw on a path/line/rect — animates stroke-dashoffset from len → 0
38
+ function DrawPath({ d, len = 1000, at = 0, dur = 0.7, stroke = 'var(--color-fg)', sw = 2.4, fill = 'none', dash = '' }) {
39
+ const { localTime } = useSprite();
40
+ const p = clamp((localTime - at) / dur, 0, 1);
41
+ const off = (1 - ease(p)) * len;
42
+ return <path d={d} stroke={stroke} strokeWidth={sw} fill={fill} strokeLinecap="round" strokeLinejoin="round" strokeDasharray={dash || len} strokeDashoffset={off} />;
43
+ }
44
+
45
+ // Flood-fill — fades a fill in
46
+ function Fill({ at = 0, dur = 0.4, children }) {
47
+ const { localTime } = useSprite();
48
+ const op = clamp((localTime - at) / dur, 0, 1);
49
+ return <g opacity={op}>{children}</g>;
50
+ }
51
+
52
+ // Slight rotate-in label
53
+ function LabelIn({ at = 0, dur = 0.35, style = {}, children, rot = -2 }) {
54
+ const { localTime } = useSprite();
55
+ const p = clamp((localTime - at) / dur, 0, 1);
56
+ const e = ease(p);
57
+ return <div style={{ ...style, opacity: e, transform: `rotate(${rot * (1 - e)}deg) translateY(${(1 - e) * 12}px)` }}>{children}</div>;
58
+ }
59
+
60
+ // Iso cube (front, top, right faces)
61
+ function IsoCube({ x = 0, y = 0, size = 80, at = 0, fill = 'var(--fill-blue)' }) {
62
+ const s = size, h = s * 0.5;
63
+ return <g transform={`translate(${x},${y})`}>
64
+ <Fill at={at + 0.4}><polygon points={`0,${h} ${s},0 ${s*2},${h} ${s},${s}`} fill={fill} opacity="0.95" /></Fill>
65
+ <Fill at={at + 0.5}><polygon points={`0,${h} ${s},${s} ${s},${s*2} 0,${s*1.5}`} fill={fill} opacity="0.7" /></Fill>
66
+ <Fill at={at + 0.5}><polygon points={`${s},${s} ${s*2},${h} ${s*2},${s*1.5} ${s},${s*2}`} fill={fill} opacity="0.55" /></Fill>
67
+ <DrawPath d={`M0,${h} L${s},0 L${s*2},${h} L${s},${s} Z`} at={at} dur={0.4} len={s*4} />
68
+ <DrawPath d={`M0,${h} L${s},${s} L${s},${s*2} L0,${s*1.5} Z`} at={at + 0.15} dur={0.4} len={s*4} />
69
+ <DrawPath d={`M${s},${s} L${s*2},${h} L${s*2},${s*1.5} L${s},${s*2} Z`} at={at + 0.15} dur={0.4} len={s*4} />
70
+ </g>;
71
+ }
72
+
73
+ // Curly leader line — bezier
74
+ function CurlyLeader({ x1, y1, x2, y2, at = 0, dur = 0.5, stroke = 'var(--color-fg)' }) {
75
+ const mx = (x1 + x2) / 2 + 30;
76
+ const my = (y1 + y2) / 2 - 30;
77
+ const d = `M ${x1} ${y1} Q ${mx} ${my} ${x2} ${y2}`;
78
+ const len = Math.hypot(x2-x1, y2-y1) * 1.5;
79
+ return <DrawPath d={d} len={len} at={at} dur={dur} stroke={stroke} />;
80
+ }
81
+
82
+ // Sketchy underline — wavy line under text
83
+ function Underline({ at = 0, width = 300, color = 'var(--color-accent)' }) {
84
+ const d = `M 0 6 q ${width*0.16} -8 ${width*0.33} 0 t ${width*0.33} 0 t ${width*0.33} 0`;
85
+ return <svg width={width} height="14" style={{ display: 'block' }}>
86
+ <DrawPath d={d} len={width * 1.2} at={at} dur={0.5} stroke={color} sw={2.5} />
87
+ </svg>;
88
+ }
89
+
90
+ // Sketchy ellipse around content
91
+ function SketchEllipse({ cx, cy, rx, ry, at = 0, stroke = 'var(--color-accent)' }) {
92
+ const d = `M ${cx-rx} ${cy} q 0 ${-ry*1.05} ${rx} ${-ry*1.05} t ${rx} ${ry*1.05} t ${-rx} ${ry*1.05} t ${-rx} ${-ry*1.05} Z`;
93
+ return <DrawPath d={d} len={(rx + ry) * 4} at={at} dur={0.7} stroke={stroke} sw={2.5} />;
94
+ }
95
+
96
+ // Ticker that counts up
97
+ function Ticker({ to, at = 0, dur = 1.0, suffix = '' }) {
98
+ const { localTime } = useSprite();
99
+ const p = clamp((localTime - at) / dur, 0, 1);
100
+ return <span>{Math.round(to * ease(p))}{suffix}</span>;
101
+ }
102
+
103
+ function Counter() {
104
+ const t = useTime();
105
+ const i = Math.min(9, Math.floor(t / SCENE));
106
+ return <div className="iso-display iso-muted" style={{ position: 'absolute', right: 96, top: 36, fontSize: 28, transform: 'rotate(-2deg)' }}>
107
+ ~ scene {i+1} of 10 ~
108
+ </div>;
109
+ }
110
+
111
+ // ─── Scene 1 — Title ─────────────────────────────────────────────
112
+ function S1() {
113
+ return <Sprite start={0} end={SCENE}>
114
+ {/* Small iso cube top-left */}
115
+ <svg style={{ position: 'absolute', left: 120, top: 100, width: 180, height: 180, overflow: 'visible' }}>
116
+ <g transform="translate(0,20)"><IsoCube x={0} y={0} size={60} at={0.2} fill="var(--fill-yellow)" /></g>
117
+ </svg>
118
+ <LabelIn at={0.8} style={{ position: 'absolute', left: 120, top: 320 }}>
119
+ <div className="iso-mono iso-muted" style={{ fontSize: 22, letterSpacing: '0.1em', marginBottom: 24 }}>BEACON · v0.4</div>
120
+ </LabelIn>
121
+ <LabelIn at={1.1} style={{ position: 'absolute', left: 120, top: 380 }}>
122
+ <div className="iso-display" style={{ fontSize: 320, color: 'var(--color-accent)' }}>Beacon.</div>
123
+ </LabelIn>
124
+ <LabelIn at={1.7} style={{ position: 'absolute', left: 120, top: 740 }}>
125
+ <div className="iso-body" style={{ fontSize: 44, fontWeight: 600 }}>Long-running agents,</div>
126
+ <div className="iso-body" style={{ fontSize: 44, fontWeight: 400, color: 'var(--color-muted)' }}>that don't forget where they were.</div>
127
+ </LabelIn>
128
+ <div style={{ position: 'absolute', left: 120, top: 870, width: 580 }}>
129
+ <Underline at={2.0} width={580} color="var(--color-accent)" />
130
+ </div>
131
+ <Counter />
132
+ </Sprite>;
133
+ }
134
+
135
+ // ─── Scene 2 — Section ───────────────────────────────────────────
136
+ function S2() {
137
+ return <Sprite start={SCENE} end={SCENE*2}>
138
+ <LabelIn at={0.3} style={{ position: 'absolute', left: 120, top: 180 }} rot={-3}>
139
+ <div className="iso-display iso-muted" style={{ fontSize: 80 }}>Chapter</div>
140
+ </LabelIn>
141
+ <LabelIn at={0.7} style={{ position: 'absolute', left: 120, top: 260 }}>
142
+ <div className="iso-display iso-accent" style={{ fontSize: 460, lineHeight: 0.9 }}>02</div>
143
+ </LabelIn>
144
+ <LabelIn at={1.3} style={{ position: 'absolute', left: 520, top: 380 }}>
145
+ <div className="iso-display" style={{ fontSize: 180 }}>The architecture.</div>
146
+ </LabelIn>
147
+ <div style={{ position: 'absolute', left: 520, top: 600, width: 720 }}>
148
+ <Underline at={1.8} width={720} />
149
+ </div>
150
+ <LabelIn at={2.0} style={{ position: 'absolute', left: 520, top: 660 }} rot={-1}>
151
+ <div className="iso-body iso-muted" style={{ fontSize: 32 }}>three primitives → memory · reasoning · recovery</div>
152
+ </LabelIn>
153
+ <Counter />
154
+ </Sprite>;
155
+ }
156
+
157
+ // ─── Scene 3 — Kinetic ───────────────────────────────────────────
158
+ function S3() {
159
+ const words = ['Most','agents','fail','because','they','forget.'];
160
+ return <Sprite start={SCENE*2} end={SCENE*3}>
161
+ <div style={{ position: 'absolute', left: 140, top: 260, width: 1640 }}>
162
+ {words.map((w, i) => (
163
+ <LabelIn key={i} at={0.4 + i * 0.22} rot={(i%2===0?-1.5:1.5)} style={{ display: 'inline-block', marginRight: 32 }}>
164
+ <span className="iso-display" style={{ fontSize: 220, color: i === words.length-1 ? 'var(--color-accent)' : 'var(--color-fg)', position: 'relative', display: 'inline-block' }}>
165
+ {w}
166
+ {i === words.length - 1 && (
167
+ <svg style={{ position: 'absolute', inset: -28, width: 'calc(100% + 56px)', height: 'calc(100% + 56px)', overflow: 'visible' }}>
168
+ <SketchEllipse cx="50%" cy="50%" rx={w.length * 55 + 20} ry={130} at={0.4 + i * 0.22 + 0.4} />
169
+ </svg>
170
+ )}
171
+ </span>
172
+ </LabelIn>
173
+ ))}
174
+ </div>
175
+ <Counter />
176
+ </Sprite>;
177
+ }
178
+
179
+ // ─── Scene 4 — Bullet ────────────────────────────────────────────
180
+ function S4() {
181
+ const items = [
182
+ 'Context windows decay.',
183
+ 'Tool selection drifts.',
184
+ 'Plans go stale.',
185
+ 'Errors compound.',
186
+ 'Recovery requires restart.',
187
+ ];
188
+ return <Sprite start={SCENE*3} end={SCENE*4}>
189
+ <LabelIn at={0.3} style={{ position: 'absolute', left: 140, top: 140 }}>
190
+ <div className="iso-display" style={{ fontSize: 130 }}>Where agents break.</div>
191
+ <div style={{ width: 580, marginTop: 8 }}><Underline at={0.7} width={580} /></div>
192
+ </LabelIn>
193
+ <div style={{ position: 'absolute', left: 180, top: 420, right: 180 }}>
194
+ {items.map((t, i) => (
195
+ <LabelIn key={i} at={0.9 + i*0.2} rot={0} style={{ display: 'flex', alignItems: 'center', gap: 36, marginBottom: 40 }}>
196
+ {/* Hand-drawn checkbox */}
197
+ <svg width="64" height="64" style={{ flexShrink: 0 }}>
198
+ <DrawPath d="M 8 8 L 56 8 L 56 56 L 8 56 Z" len={200} at={0.95 + i*0.2} dur={0.4} sw={2.4} />
199
+ <DrawPath d="M 16 32 L 28 48 L 52 16" len={80} at={1.25 + i*0.2} dur={0.4} stroke="var(--color-accent)" sw={3} />
200
+ </svg>
201
+ <span className="iso-body" style={{ fontSize: 50, fontWeight: 600 }}>{t}</span>
202
+ <span className="iso-display iso-muted" style={{ fontSize: 44, marginLeft: 'auto' }}>0{i+1}.</span>
203
+ </LabelIn>
204
+ ))}
205
+ </div>
206
+ <Counter />
207
+ </Sprite>;
208
+ }
209
+
210
+ // ─── Scene 5 — Stat ──────────────────────────────────────────────
211
+ function S5() {
212
+ return <Sprite start={SCENE*4} end={SCENE*5}>
213
+ <LabelIn at={0.3} style={{ position: 'absolute', left: 140, top: 160 }}>
214
+ <div className="iso-body iso-muted" style={{ fontSize: 28, letterSpacing: '0.05em' }}>BEACON · failure data, Q1 2026</div>
215
+ </LabelIn>
216
+ <div style={{ position: 'absolute', left: 0, right: 0, top: 280, textAlign: 'center' }}>
217
+ <svg style={{ position: 'absolute', left: '50%', top: 0, transform: 'translateX(-50%)', width: 800, height: 800, overflow: 'visible' }}>
218
+ {/* Hand-drawn concentric circles */}
219
+ <DrawPath d="M 400 100 a 300 300 0 1 0 0.1 0 Z" len={1900} at={0.5} dur={1.0} stroke="var(--color-accent)" sw={2.5} />
220
+ <DrawPath d="M 400 160 a 240 240 0 1 0 0.1 0 Z" len={1520} at={0.8} dur={1.0} stroke="var(--color-muted)" sw={1.8} />
221
+ </svg>
222
+ <LabelIn at={0.9} style={{ position: 'relative', zIndex: 1, paddingTop: 200 }}>
223
+ <div className="iso-display iso-accent" style={{ fontSize: 380, lineHeight: 0.9 }}>
224
+ <Ticker to={84} at={1.0} dur={1.0} />%
225
+ </div>
226
+ </LabelIn>
227
+ </div>
228
+ <LabelIn at={2.0} style={{ position: 'absolute', left: 0, right: 0, bottom: 200, textAlign: 'center' }} rot={-1}>
229
+ <div className="iso-body" style={{ fontSize: 36, maxWidth: 1200, margin: '0 auto', fontWeight: 600 }}>
230
+ of agent failures happen <span style={{ color: 'var(--color-accent)' }}>after</span> the 4th tool call.
231
+ </div>
232
+ <div className="iso-mono iso-muted" style={{ fontSize: 18, marginTop: 14 }}>n=12,840 sessions</div>
233
+ </LabelIn>
234
+ <Counter />
235
+ </Sprite>;
236
+ }
237
+
238
+ // ─── Scene 6 — Feature ───────────────────────────────────────────
239
+ function S6() {
240
+ return <Sprite start={SCENE*5} end={SCENE*6}>
241
+ {/* Left: iso illustration — stack of memory blocks */}
242
+ <svg style={{ position: 'absolute', left: 140, top: 200, width: 800, height: 700, overflow: 'visible' }}>
243
+ {/* Stack of cubes */}
244
+ {[0,1,2,3].map(i => (
245
+ <g key={i} transform={`translate(${200},${500 - i*90})`}>
246
+ <Fill at={0.6 + i*0.2}><polygon points={`0,40 160,-40 320,40 160,120`} fill={i === 3 ? 'var(--fill-pink)' : 'var(--fill-blue)'} opacity="0.9" /></Fill>
247
+ <Fill at={0.7 + i*0.2}><polygon points={`0,40 160,120 160,180 0,100`} fill={i === 3 ? 'var(--fill-pink)' : 'var(--fill-blue)'} opacity="0.65" /></Fill>
248
+ <Fill at={0.7 + i*0.2}><polygon points={`160,120 320,40 320,100 160,180`} fill={i === 3 ? 'var(--fill-pink)' : 'var(--fill-blue)'} opacity="0.5" /></Fill>
249
+ <DrawPath d="M 0 40 L 160 -40 L 320 40 L 160 120 Z" len={750} at={0.5 + i*0.2} dur={0.5} />
250
+ <DrawPath d="M 0 40 L 160 120 L 160 180 L 0 100 Z" len={750} at={0.6 + i*0.2} dur={0.5} />
251
+ <DrawPath d="M 160 120 L 320 40 L 320 100 L 160 180 Z" len={750} at={0.6 + i*0.2} dur={0.5} />
252
+ </g>
253
+ ))}
254
+ {/* Rewind arrow */}
255
+ <CurlyLeader x1={600} y1={180} x2={300} y2={140} at={2.0} stroke="var(--color-accent)" />
256
+ <Fill at={2.5}>
257
+ <polygon points="300,140 318,128 318,152" fill="var(--color-accent)" />
258
+ <text x="470" y="120" fontFamily="var(--font-display)" fontSize="40" fill="var(--color-accent)" transform="rotate(-6 470 120)">rewind ↶</text>
259
+ </Fill>
260
+ </svg>
261
+ {/* Right: copy */}
262
+ <LabelIn at={0.4} style={{ position: 'absolute', right: 120, top: 180, width: 720 }}>
263
+ <div className="iso-mono iso-muted" style={{ fontSize: 18, letterSpacing: '0.1em', marginBottom: 16 }}>FEATURE 01 / 03</div>
264
+ </LabelIn>
265
+ <LabelIn at={0.7} style={{ position: 'absolute', right: 120, top: 240, width: 720 }}>
266
+ <div className="iso-display" style={{ fontSize: 130, color: 'var(--color-accent)', marginBottom: 16 }}>Checkpoint memory.</div>
267
+ <Underline at={1.3} width={520} />
268
+ </LabelIn>
269
+ <LabelIn at={1.4} style={{ position: 'absolute', right: 120, top: 540, width: 720 }}>
270
+ <div className="iso-body" style={{ fontSize: 28, lineHeight: 1.5, marginBottom: 32 }}>
271
+ Snapshots the agent's reasoning at each tool boundary. When a plan goes stale, the agent rewinds to the last point its world model was correct.
272
+ </div>
273
+ </LabelIn>
274
+ <LabelIn at={1.8} style={{ position: 'absolute', right: 120, top: 780, width: 720, display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 24, paddingTop: 24, borderTop: '2px solid var(--color-fg)' }}>
275
+ <div><div className="iso-mono iso-muted" style={{ fontSize: 13, letterSpacing: '0.15em' }}>SNAP/MIN</div><div className="iso-display" style={{ fontSize: 80 }}><Ticker to={120} at={1.9} /></div></div>
276
+ <div><div className="iso-mono iso-muted" style={{ fontSize: 13, letterSpacing: '0.15em' }}>RESTORE</div><div className="iso-display" style={{ fontSize: 80 }}><Ticker to={9} at={1.9} suffix=" ms" /></div></div>
277
+ </LabelIn>
278
+ <Counter />
279
+ </Sprite>;
280
+ }
281
+
282
+ // ─── Scene 7 — 3 mini iso cards ──────────────────────────────────
283
+ function S7() {
284
+ const cards = [
285
+ { name: 'Memory', fill: 'var(--fill-blue)', desc: 'remembers' },
286
+ { name: 'Reasoning', fill: 'var(--fill-yellow)', desc: 'revises' },
287
+ { name: 'Recovery', fill: 'var(--fill-green)', desc: 'rewinds' },
288
+ ];
289
+ return <Sprite start={SCENE*6} end={SCENE*7}>
290
+ <LabelIn at={0.3} style={{ position: 'absolute', left: 140, top: 140 }}>
291
+ <div className="iso-display" style={{ fontSize: 130 }}>Three primitives.</div>
292
+ <Underline at={0.7} width={520} />
293
+ </LabelIn>
294
+ <div style={{ position: 'absolute', left: 140, right: 140, top: 420, display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 56 }}>
295
+ {cards.map((c, i) => (
296
+ <LabelIn key={c.name} at={0.9 + i*0.2}>
297
+ <div style={{ position: 'relative', height: 540 }}>
298
+ {/* Hand-drawn frame */}
299
+ <svg style={{ position: 'absolute', inset: 0, width: '100%', height: '100%' }}>
300
+ <DrawPath d="M 8 8 L 528 12 L 524 528 L 12 524 Z" len={2100} at={1.0 + i*0.2} dur={0.6} sw={2.4} />
301
+ </svg>
302
+ {/* Iso cube */}
303
+ <svg width="100%" height="280" style={{ overflow: 'visible' }}>
304
+ <g transform="translate(170,80)"><IsoCube x={0} y={0} size={90} at={1.2 + i*0.2} fill={c.fill} /></g>
305
+ </svg>
306
+ <div className="iso-display" style={{ fontSize: 80, textAlign: 'center', marginTop: 24 }}>{c.name}</div>
307
+ <div className="iso-display iso-muted" style={{ fontSize: 40, textAlign: 'center', marginTop: -4 }}>~ {c.desc} ~</div>
308
+ </div>
309
+ </LabelIn>
310
+ ))}
311
+ </div>
312
+ <Counter />
313
+ </Sprite>;
314
+ }
315
+
316
+ // ─── Scene 8 — UI exploded view ──────────────────────────────────
317
+ function S8() {
318
+ return <Sprite start={SCENE*7} end={SCENE*8}>
319
+ <LabelIn at={0.3} style={{ position: 'absolute', left: 140, top: 140 }}>
320
+ <div className="iso-display" style={{ fontSize: 110 }}>Beacon Console.</div>
321
+ <Underline at={0.7} width={480} />
322
+ </LabelIn>
323
+ {/* Exploded iso layers */}
324
+ <svg style={{ position: 'absolute', left: 240, top: 360, width: 1100, height: 700, overflow: 'visible' }}>
325
+ {[
326
+ { y: 360, fill: 'var(--fill-blue)', label: 'TIMELINE' },
327
+ { y: 240, fill: 'var(--fill-yellow)', label: 'MEMORY' },
328
+ { y: 120, fill: 'var(--fill-pink)', label: 'TRACES' },
329
+ { y: 0, fill: 'var(--fill-green)', label: 'AGENT' },
330
+ ].map((layer, i) => (
331
+ <g key={i} transform={`translate(80,${layer.y})`}>
332
+ <Fill at={0.8 + i*0.25}><polygon points="0,80 360,-80 720,80 360,240" fill={layer.fill} opacity="0.85" /></Fill>
333
+ <DrawPath d="M 0 80 L 360 -80 L 720 80 L 360 240 Z" len={1800} at={0.7 + i*0.25} dur={0.5} />
334
+ <Fill at={1.0 + i*0.25}>
335
+ <text x={760} y={70} fontFamily="var(--font-display)" fontSize="44" fill="var(--color-fg)">{layer.label}</text>
336
+ </Fill>
337
+ <DrawPath d={`M 740 60 L 800 60`} len={60} at={1.0 + i*0.25} dur={0.3} stroke="var(--color-accent)" />
338
+ </g>
339
+ ))}
340
+ </svg>
341
+ <LabelIn at={2.5} style={{ position: 'absolute', right: 140, bottom: 180, width: 480 }} rot={-2}>
342
+ <div className="iso-body" style={{ fontSize: 26, fontWeight: 600 }}>
343
+ Four layers, one console.
344
+ </div>
345
+ <div className="iso-body iso-muted" style={{ fontSize: 22, marginTop: 8 }}>
346
+ Each layer is independently inspectable — and pinnable to the timeline.
347
+ </div>
348
+ </LabelIn>
349
+ <Counter />
350
+ </Sprite>;
351
+ }
352
+
353
+ // ─── Scene 9 — Content with inline drawing ───────────────────────
354
+ function S9() {
355
+ return <Sprite start={SCENE*8} end={SCENE*9}>
356
+ <LabelIn at={0.3} style={{ position: 'absolute', left: 140, top: 180 }}>
357
+ <div className="iso-display" style={{ fontSize: 130 }}>What we believe.</div>
358
+ <Underline at={0.7} width={520} />
359
+ </LabelIn>
360
+ <div style={{ position: 'absolute', left: 140, right: 140, top: 440, display: 'grid', gridTemplateColumns: '1.4fr 1fr', gap: 64 }}>
361
+ <LabelIn at={0.8}>
362
+ <div className="iso-body" style={{ fontSize: 34, lineHeight: 1.5 }}>
363
+ The next decade of AI is not about <span style={{ position: 'relative', display: 'inline-block' }}>
364
+ bigger models<svg style={{ position: 'absolute', left: 0, right: 0, bottom: -10, width: '100%' }} height="14"><DrawPath d="M 4 8 q 60 -8 120 0 t 120 0" len={300} at={1.3} stroke="var(--color-accent)" sw={2.8} /></svg>
365
+ </span>.
366
+ </div>
367
+ <div className="iso-body iso-muted" style={{ fontSize: 30, lineHeight: 1.5, marginTop: 32 }}>
368
+ It's about agents that can <span style={{ color: 'var(--color-accent)' }}>run long enough to matter</span> — and the unglamorous infrastructure that lets them.
369
+ </div>
370
+ </LabelIn>
371
+ {/* small iso illustration */}
372
+ <LabelIn at={1.4}>
373
+ <svg width="100%" height="380" style={{ overflow: 'visible' }}>
374
+ <g transform="translate(120,80)">
375
+ <IsoCube x={0} y={0} size={80} at={1.4} fill="var(--fill-blue)" />
376
+ <IsoCube x={80} y={40} size={80} at={1.6} fill="var(--fill-yellow)" />
377
+ <IsoCube x={-80} y={40} size={80} at={1.6} fill="var(--fill-pink)" />
378
+ </g>
379
+ <CurlyLeader x1={280} y1={260} x2={420} y2={340} at={2.2} stroke="var(--color-accent)" />
380
+ <Fill at={2.7}>
381
+ <text x="430" y="345" fontFamily="var(--font-display)" fontSize="40" fill="var(--color-accent)" transform="rotate(-3 430 345)">us, building.</text>
382
+ </Fill>
383
+ </svg>
384
+ </LabelIn>
385
+ </div>
386
+ <Counter />
387
+ </Sprite>;
388
+ }
389
+
390
+ // ─── Scene 10 — CTA ──────────────────────────────────────────────
391
+ function S10() {
392
+ return <Sprite start={SCENE*9} end={SCENE*10}>
393
+ <LabelIn at={0.2} style={{ position: 'absolute', left: 0, right: 0, top: 240, textAlign: 'center' }} rot={-1}>
394
+ <div className="iso-display iso-muted" style={{ fontSize: 56 }}>Try it →</div>
395
+ </LabelIn>
396
+ <div style={{ position: 'absolute', left: '50%', top: 400, transform: 'translateX(-50%)' }}>
397
+ <svg width="1100" height="280" style={{ overflow: 'visible' }}>
398
+ <DrawPath d="M 20 40 L 1080 30 L 1060 240 L 30 230 Z" len={3000} at={0.5} dur={0.9} stroke="var(--color-accent)" sw={3.5} />
399
+ </svg>
400
+ <LabelIn at={1.2} style={{ position: 'absolute', inset: 0, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
401
+ <div className="iso-display" style={{ fontSize: 160, color: 'var(--color-accent)' }}>beacon.run</div>
402
+ </LabelIn>
403
+ </div>
404
+ {/* small iso below */}
405
+ <svg style={{ position: 'absolute', left: '50%', top: 740, transform: 'translateX(-50%)', width: 240, height: 240, overflow: 'visible' }}>
406
+ <g transform="translate(40,40)"><IsoCube x={0} y={0} size={70} at={1.6} fill="var(--fill-yellow)" /></g>
407
+ </svg>
408
+ <LabelIn at={2.2} style={{ position: 'absolute', left: 0, right: 0, bottom: 100, textAlign: 'center' }} rot={-2}>
409
+ <div className="iso-display iso-muted" style={{ fontSize: 40 }}>~ beacon · may 2026 ~</div>
410
+ </LabelIn>
411
+ <Counter />
412
+ </Sprite>;
413
+ }
414
+
415
+ function App() {
416
+ return (
417
+ <Stage width={1920} height={1080} duration={SCENE*10} background="var(--color-bg)" persistKey="iso-diagram-ref">
418
+ <div className="iso-canvas">
419
+ <S1 /><S2 /><S3 /><S4 /><S5 /><S6 /><S7 /><S8 /><S9 /><S10 />
420
+ </div>
421
+ </Stage>
422
+ );
423
+ }
424
+ ReactDOM.createRoot(document.getElementById('root')).render(<App />);
425
+ </script>
426
+ </body>
427
+ </html>
@@ -0,0 +1,144 @@
1
+ import { defineSegment } from "videowright";
2
+
3
+ let host: HTMLElement | null = null;
4
+
5
+ export default defineSegment({
6
+ id: "iso-diagram-sample-bullet",
7
+ advances: [2.5, 5.0],
8
+ voiceover:
9
+ "Bullet reveals in Iso Diagram. Each item has a hand-drawn checkbox that draws itself, then a checkmark appears inside.",
10
+
11
+ mount(el) {
12
+ host = el;
13
+
14
+ const items = [
15
+ "Context windows decay.",
16
+ "Tool selection drifts.",
17
+ "Plans go stale.",
18
+ "Errors compound.",
19
+ "Recovery requires restart.",
20
+ ];
21
+
22
+ const itemHtml = items
23
+ .map(
24
+ (text, i) => `
25
+ <div data-ref="item${i}" style="
26
+ display: flex;
27
+ align-items: center;
28
+ gap: 36px;
29
+ margin-bottom: 40px;
30
+ opacity: 0;
31
+ ">
32
+ <svg width="64" height="64" style="flex-shrink: 0; overflow: visible;">
33
+ <path data-ref="box${i}" d="M 8 8 L 56 8 L 56 56 L 8 56 Z" stroke="var(--color-fg)" stroke-width="2.4" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="200" stroke-dashoffset="200" />
34
+ <path data-ref="check${i}" d="M 16 32 L 28 48 L 52 16" stroke="var(--color-accent)" stroke-width="3" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="80" stroke-dashoffset="80" />
35
+ </svg>
36
+ <span style="font-family: var(--font-body); font-size: 50px; font-weight: 600;">${text}</span>
37
+ <span style="font-family: var(--font-display); font-size: 44px; color: var(--color-muted); margin-left: auto;">0${i + 1}.</span>
38
+ </div>`,
39
+ )
40
+ .join("");
41
+
42
+ el.innerHTML = `
43
+ <div style="
44
+ position: relative;
45
+ height: 100%;
46
+ background: var(--color-bg);
47
+ color: var(--color-fg);
48
+ font-family: var(--font-body);
49
+ overflow: hidden;
50
+ ">
51
+ <div style="
52
+ position: absolute; inset: 0; pointer-events: none;
53
+ background-image: radial-gradient(circle, rgba(42,38,32,0.10) 1px, transparent 1.6px);
54
+ background-size: 32px 32px;
55
+ "></div>
56
+
57
+ <div data-ref="heading" style="
58
+ position: absolute;
59
+ left: 140px;
60
+ top: 120px;
61
+ opacity: 0;
62
+ ">
63
+ <div style="font-family: var(--font-display); font-size: 130px;">Where agents break.</div>
64
+ <svg style="margin-top: 8px;" width="580" height="14">
65
+ <path data-ref="headingUnderline" d="M 0 6 q 96 -8 192 0 t 192 0 t 192 0" stroke="var(--color-accent)" stroke-width="2.5" fill="none" stroke-linecap="round" stroke-dasharray="700" stroke-dashoffset="700" />
66
+ </svg>
67
+ </div>
68
+
69
+ <div style="position: absolute; left: 180px; top: 400px; right: 180px;">
70
+ ${itemHtml}
71
+ </div>
72
+
73
+ <div data-ref="counter" style="
74
+ position: absolute;
75
+ right: var(--safe-x);
76
+ top: 36px;
77
+ font-family: var(--font-display);
78
+ font-size: 28px;
79
+ color: var(--color-muted);
80
+ transform: rotate(-2deg);
81
+ opacity: 0;
82
+ ">~ scene 4 of 10 ~</div>
83
+ </div>
84
+ `;
85
+ },
86
+
87
+ async play(ctx) {
88
+ const heading = host?.querySelector('[data-ref="heading"]') as HTMLElement;
89
+ const headingUnderline = host?.querySelector('[data-ref="headingUnderline"]') as SVGPathElement;
90
+ const counter = host?.querySelector('[data-ref="counter"]') as HTMLElement;
91
+
92
+ const ease = "cubic-bezier(0.34, 1.2, 0.64, 1)";
93
+ const opts = { fill: "forwards" as const, easing: ease };
94
+
95
+ counter.animate([{ opacity: 0 }, { opacity: 1 }], { ...opts, duration: 300 });
96
+
97
+ // Heading swings in
98
+ heading.animate(
99
+ [
100
+ { opacity: 0, transform: "translateY(12px)" },
101
+ { opacity: 1, transform: "translateY(0)" },
102
+ ],
103
+ { ...opts, duration: 350, delay: 300 },
104
+ );
105
+
106
+ // Heading underline draws on
107
+ headingUnderline.animate([{ strokeDashoffset: "700" }, { strokeDashoffset: "0" }], {
108
+ ...opts,
109
+ duration: 500,
110
+ delay: 700,
111
+ });
112
+
113
+ // Each item appears with checkbox drawing on, then checkmark
114
+ for (let i = 0; i < 5; i++) {
115
+ const item = host?.querySelector(`[data-ref="item${i}"]`) as HTMLElement;
116
+ const box = host?.querySelector(`[data-ref="box${i}"]`) as SVGPathElement;
117
+ const check = host?.querySelector(`[data-ref="check${i}"]`) as SVGPathElement;
118
+
119
+ item.animate([{ opacity: 0 }, { opacity: 1 }], {
120
+ ...opts,
121
+ duration: 250,
122
+ delay: 900 + i * 200,
123
+ });
124
+
125
+ box.animate([{ strokeDashoffset: "200" }, { strokeDashoffset: "0" }], {
126
+ ...opts,
127
+ duration: 400,
128
+ delay: 950 + i * 200,
129
+ });
130
+
131
+ check.animate([{ strokeDashoffset: "80" }, { strokeDashoffset: "0" }], {
132
+ ...opts,
133
+ duration: 400,
134
+ delay: 1250 + i * 200,
135
+ });
136
+ }
137
+
138
+ await ctx.waitForNext();
139
+ },
140
+
141
+ unmount() {
142
+ host = null;
143
+ },
144
+ });