dsh-drawai 0.3.3 → 0.4.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 (533) hide show
  1. package/LICENSE +4142 -0
  2. package/README.md +43 -10
  3. package/lib/audio-media.js +147 -0
  4. package/lib/audio-service.js +66 -0
  5. package/lib/canvas-format.js +134 -0
  6. package/lib/client.js +5283 -223
  7. package/lib/comfyui-default-workflow.mjs +1825 -0
  8. package/lib/comfyui-workflow.mjs +426 -0
  9. package/lib/document-commands.js +12 -0
  10. package/lib/document-core.js +29 -0
  11. package/lib/drawing-tasks.js +26 -0
  12. package/lib/imagegen.mjs +596 -0
  13. package/lib/index.js +1758 -129
  14. package/lib/media.js +26 -13
  15. package/lib/mgstyle-core.js +75 -0
  16. package/lib/motion/apply.js +180 -0
  17. package/lib/motion/asset-pack-lock.js +10 -0
  18. package/lib/motion/asset-pack.js +335 -0
  19. package/lib/motion/assets.js +290 -0
  20. package/lib/motion/audio.js +103 -0
  21. package/lib/motion/audiolevel.js +63 -0
  22. package/lib/motion/audiomix.js +144 -0
  23. package/lib/motion/beats.js +337 -0
  24. package/lib/motion/builtin-assets.js +62 -0
  25. package/lib/motion/canvas-spec.js +442 -0
  26. package/lib/motion/capture.js +95 -0
  27. package/lib/motion/cards.js +80 -0
  28. package/lib/motion/collect.js +447 -0
  29. package/lib/motion/delivery.js +112 -0
  30. package/lib/motion/edge-speech.py +90 -0
  31. package/lib/motion/entry.jsx +73 -0
  32. package/lib/motion/export.js +200 -0
  33. package/lib/motion/ffmpeg.js +42 -0
  34. package/lib/motion/fonts.js +71 -0
  35. package/lib/motion/jobs.js +113 -0
  36. package/lib/motion/json.js +4 -0
  37. package/lib/motion/media-catalog.js +59 -0
  38. package/lib/motion/mgstyles/BRIEF-TEMPLATE.md +63 -0
  39. package/lib/motion/mgstyles/briefs/01-flat-vector.md +104 -0
  40. package/lib/motion/mgstyles/briefs/02-line-art.md +97 -0
  41. package/lib/motion/mgstyles/briefs/03-isometric.md +108 -0
  42. package/lib/motion/mgstyles/briefs/05-cel-boil.md +145 -0
  43. package/lib/motion/mgstyles/briefs/06-collage.md +149 -0
  44. package/lib/motion/mgstyles/briefs/07-liquid.md +106 -0
  45. package/lib/motion/mgstyles/briefs/08-morph.md +111 -0
  46. package/lib/motion/mgstyles/briefs/09-bauhaus.md +150 -0
  47. package/lib/motion/mgstyles/briefs/10-synthwave.md +151 -0
  48. package/lib/motion/mgstyles/briefs/12-aurora-glass.md +147 -0
  49. package/lib/motion/mgstyles/briefs/18-hanazi.md +140 -0
  50. package/lib/motion/mgstyles/briefs/19-paperclip.md +156 -0
  51. package/lib/motion/mgstyles/briefs/20-pixel.md +118 -0
  52. package/lib/motion/mgstyles/briefs/22-hud.md +118 -0
  53. package/lib/motion/mgstyles/catalog.d/01-flat-vector.json +34 -0
  54. package/lib/motion/mgstyles/catalog.d/02-line-art.json +34 -0
  55. package/lib/motion/mgstyles/catalog.d/03-isometric.json +35 -0
  56. package/lib/motion/mgstyles/catalog.d/05-cel-boil.json +28 -0
  57. package/lib/motion/mgstyles/catalog.d/06-collage.json +28 -0
  58. package/lib/motion/mgstyles/catalog.d/07-liquid.json +28 -0
  59. package/lib/motion/mgstyles/catalog.d/08-morph.json +28 -0
  60. package/lib/motion/mgstyles/catalog.d/09-bauhaus.json +28 -0
  61. package/lib/motion/mgstyles/catalog.d/10-synthwave.json +28 -0
  62. package/lib/motion/mgstyles/catalog.d/12-aurora-glass.json +28 -0
  63. package/lib/motion/mgstyles/catalog.d/18-hanazi.json +28 -0
  64. package/lib/motion/mgstyles/catalog.d/19-paperclip.json +28 -0
  65. package/lib/motion/mgstyles/catalog.d/20-pixel.json +28 -0
  66. package/lib/motion/mgstyles/catalog.d/22-hud.json +28 -0
  67. package/lib/motion/mgstyles/catalog.json +1356 -0
  68. package/lib/motion/mgstyles/upstream/README.md +147 -0
  69. package/lib/motion/mgstyles/upstream/README.zh-CN.md +147 -0
  70. package/lib/motion/mgstyles/upstream/demos/01-flat-vector/cues.json +1 -0
  71. package/lib/motion/mgstyles/upstream/demos/01-flat-vector/index.html +712 -0
  72. package/lib/motion/mgstyles/upstream/demos/02-line-art/cues.json +331 -0
  73. package/lib/motion/mgstyles/upstream/demos/02-line-art/film.js +676 -0
  74. package/lib/motion/mgstyles/upstream/demos/02-line-art/index.html +18 -0
  75. package/lib/motion/mgstyles/upstream/demos/02-line-art/tools/dump_timing.mjs +26 -0
  76. package/lib/motion/mgstyles/upstream/demos/03-isometric/cues.json +109 -0
  77. package/lib/motion/mgstyles/upstream/demos/03-isometric/index.html +25 -0
  78. package/lib/motion/mgstyles/upstream/demos/03-isometric/js/anim.js +82 -0
  79. package/lib/motion/mgstyles/upstream/demos/03-isometric/js/main.js +219 -0
  80. package/lib/motion/mgstyles/upstream/demos/03-isometric/js/night5.js +180 -0
  81. package/lib/motion/mgstyles/upstream/demos/03-isometric/js/render.js +202 -0
  82. package/lib/motion/mgstyles/upstream/demos/03-isometric/js/title.js +167 -0
  83. package/lib/motion/mgstyles/upstream/demos/03-isometric/js/title5.js +294 -0
  84. package/lib/motion/mgstyles/upstream/demos/03-isometric/js/ui.js +141 -0
  85. package/lib/motion/mgstyles/upstream/demos/03-isometric/js/world.js +1399 -0
  86. package/lib/motion/mgstyles/upstream/demos/05-cel-boil/cues.json +157 -0
  87. package/lib/motion/mgstyles/upstream/demos/05-cel-boil/index.html +34 -0
  88. package/lib/motion/mgstyles/upstream/demos/05-cel-boil/js/engine.js +326 -0
  89. package/lib/motion/mgstyles/upstream/demos/05-cel-boil/js/scene.js +2036 -0
  90. package/lib/motion/mgstyles/upstream/demos/06-collage/assets/gen/layout.json +157 -0
  91. package/lib/motion/mgstyles/upstream/demos/06-collage/cues.json +80 -0
  92. package/lib/motion/mgstyles/upstream/demos/06-collage/index.html +889 -0
  93. package/lib/motion/mgstyles/upstream/demos/07-liquid/cues.json +100 -0
  94. package/lib/motion/mgstyles/upstream/demos/07-liquid/index.html +108 -0
  95. package/lib/motion/mgstyles/upstream/demos/07-liquid/js/engine.js +153 -0
  96. package/lib/motion/mgstyles/upstream/demos/07-liquid/js/links.js +64 -0
  97. package/lib/motion/mgstyles/upstream/demos/07-liquid/js/lookdev.js +46 -0
  98. package/lib/motion/mgstyles/upstream/demos/07-liquid/js/scene.js +963 -0
  99. package/lib/motion/mgstyles/upstream/demos/07-liquid/js/shaders.js +647 -0
  100. package/lib/motion/mgstyles/upstream/demos/07-liquid/js/textsdf.js +114 -0
  101. package/lib/motion/mgstyles/upstream/demos/07-liquid/js/type.js +50 -0
  102. package/lib/motion/mgstyles/upstream/demos/08-morph/cues.json +131 -0
  103. package/lib/motion/mgstyles/upstream/demos/08-morph/glyphs.json +1 -0
  104. package/lib/motion/mgstyles/upstream/demos/08-morph/index.html +901 -0
  105. package/lib/motion/mgstyles/upstream/demos/09-bauhaus/cues.json +270 -0
  106. package/lib/motion/mgstyles/upstream/demos/09-bauhaus/film.js +697 -0
  107. package/lib/motion/mgstyles/upstream/demos/09-bauhaus/index.html +12 -0
  108. package/lib/motion/mgstyles/upstream/demos/10-synthwave/cues.json +44 -0
  109. package/lib/motion/mgstyles/upstream/demos/10-synthwave/index.html +1028 -0
  110. package/lib/motion/mgstyles/upstream/demos/12-aurora-glass/cues.json +67 -0
  111. package/lib/motion/mgstyles/upstream/demos/12-aurora-glass/index.html +16 -0
  112. package/lib/motion/mgstyles/upstream/demos/12-aurora-glass/main.js +967 -0
  113. package/lib/motion/mgstyles/upstream/demos/12-aurora-glass/shaders.js +468 -0
  114. package/lib/motion/mgstyles/upstream/demos/18-hanazi/cues.json +117 -0
  115. package/lib/motion/mgstyles/upstream/demos/18-hanazi/index.html +541 -0
  116. package/lib/motion/mgstyles/upstream/demos/19-paperclip/assets/countries-110m.json +1 -0
  117. package/lib/motion/mgstyles/upstream/demos/19-paperclip/assets/stickers/meta.json +34 -0
  118. package/lib/motion/mgstyles/upstream/demos/19-paperclip/cues.json +285 -0
  119. package/lib/motion/mgstyles/upstream/demos/19-paperclip/index.html +599 -0
  120. package/lib/motion/mgstyles/upstream/demos/20-pixel/cues.json +42 -0
  121. package/lib/motion/mgstyles/upstream/demos/20-pixel/film.js +914 -0
  122. package/lib/motion/mgstyles/upstream/demos/20-pixel/index.html +18 -0
  123. package/lib/motion/mgstyles/upstream/demos/22-hud/cues.json +108 -0
  124. package/lib/motion/mgstyles/upstream/demos/22-hud/index.html +125 -0
  125. package/lib/motion/mgstyles/upstream/demos/22-hud/js/holo.js +275 -0
  126. package/lib/motion/mgstyles/upstream/demos/22-hud/js/hud.js +855 -0
  127. package/lib/motion/mgstyles/upstream/demos/22-hud/js/post.js +150 -0
  128. package/lib/motion/mgstyles/upstream/demos/22-hud/js/util.js +65 -0
  129. package/lib/motion/mgstyles/upstream/demos/22-hud/js/v5.js +142 -0
  130. package/lib/motion/mgstyles/upstream/harness/render.mjs +319 -0
  131. package/lib/motion/mgstyles/upstream/manifest.json +269 -0
  132. package/lib/motion/mgstyles/upstream/prompts/01-flat-vector.md +98 -0
  133. package/lib/motion/mgstyles/upstream/prompts/02-line-art.md +95 -0
  134. package/lib/motion/mgstyles/upstream/prompts/03-isometric.md +97 -0
  135. package/lib/motion/mgstyles/upstream/prompts/05-cel-boil.md +95 -0
  136. package/lib/motion/mgstyles/upstream/prompts/06-collage.md +95 -0
  137. package/lib/motion/mgstyles/upstream/prompts/07-liquid.md +98 -0
  138. package/lib/motion/mgstyles/upstream/prompts/08-morph.md +95 -0
  139. package/lib/motion/mgstyles/upstream/prompts/09-bauhaus.md +96 -0
  140. package/lib/motion/mgstyles/upstream/prompts/10-synthwave.md +95 -0
  141. package/lib/motion/mgstyles/upstream/prompts/12-aurora-glass.md +96 -0
  142. package/lib/motion/mgstyles/upstream/prompts/18-hanazi.md +95 -0
  143. package/lib/motion/mgstyles/upstream/prompts/19-paperclip.md +96 -0
  144. package/lib/motion/mgstyles/upstream/prompts/20-pixel.md +96 -0
  145. package/lib/motion/mgstyles/upstream/prompts/22-hud.md +95 -0
  146. package/lib/motion/mgstyles/upstream/rubric.md +51 -0
  147. package/lib/motion/mgstyles/upstream/template.md +36 -0
  148. package/lib/motion/pixelqa.js +198 -0
  149. package/lib/motion/preview.js +95 -0
  150. package/lib/motion/project.js +395 -0
  151. package/lib/motion/qa.js +290 -0
  152. package/lib/motion/reference-media.jsx +10 -0
  153. package/lib/motion/render-settings.js +112 -0
  154. package/lib/motion/render.js +159 -0
  155. package/lib/motion/scene-kit.js +183 -0
  156. package/lib/motion/sfx.js +232 -0
  157. package/lib/motion/shotcraft/ClipCard.tsx +204 -0
  158. package/lib/motion/shotcraft/catalog.json +2461 -0
  159. package/lib/motion/shotcraft/upstream/SKILL.md +286 -0
  160. package/lib/motion/shotcraft/upstream/assets/lib/Caption.tsx +54 -0
  161. package/lib/motion/shotcraft/upstream/assets/lib/ClipCard.tsx +200 -0
  162. package/lib/motion/shotcraft/upstream/assets/lib/DigitRoll.tsx +45 -0
  163. package/lib/motion/shotcraft/upstream/assets/lib/FlashCut.tsx +20 -0
  164. package/lib/motion/shotcraft/upstream/assets/lib/FlatPanel.tsx +65 -0
  165. package/lib/motion/shotcraft/upstream/assets/lib/PageCam.tsx +151 -0
  166. package/lib/motion/shotcraft/upstream/assets/lib/VerticalTicker.tsx +129 -0
  167. package/lib/motion/shotcraft/upstream/assets/lib/helpers/camera.tsx +75 -0
  168. package/lib/motion/shotcraft/upstream/assets/lib/helpers/motion.ts +40 -0
  169. package/lib/motion/shotcraft/upstream/assets/lib/helpers/rand.ts +11 -0
  170. package/lib/motion/shotcraft/upstream/assets/lib/helpers/shake.ts +10 -0
  171. package/lib/motion/shotcraft/upstream/assets/scripts/__pycache__/sfx-source.cpython-313.pyc +0 -0
  172. package/lib/motion/shotcraft/upstream/assets/scripts/build-readme-cover.mjs +49 -0
  173. package/lib/motion/shotcraft/upstream/assets/scripts/capture-template.mjs +210 -0
  174. package/lib/motion/shotcraft/upstream/assets/scripts/sfx-source.py +423 -0
  175. package/lib/motion/shotcraft/upstream/assets/scripts/smoke-render-demos.py +170 -0
  176. package/lib/motion/shotcraft/upstream/assets/scripts/test_sfx_source.py +157 -0
  177. package/lib/motion/shotcraft/upstream/demos/README.md +120 -0
  178. package/lib/motion/shotcraft/upstream/demos/_fixtures/Brand.tsx +112 -0
  179. package/lib/motion/shotcraft/upstream/demos/_fixtures/Fixtures.tsx +647 -0
  180. package/lib/motion/shotcraft/upstream/demos/_fixtures/Look.tsx +316 -0
  181. package/lib/motion/shotcraft/upstream/demos/_fixtures/Motion.tsx +82 -0
  182. package/lib/motion/shotcraft/upstream/demos/_fixtures/PageCam2D.tsx +115 -0
  183. package/lib/motion/shotcraft/upstream/demos/_fixtures/Polish.tsx +243 -0
  184. package/lib/motion/shotcraft/upstream/demos/_textures/live-layout.json +389 -0
  185. package/lib/motion/shotcraft/upstream/demos/camera/basic-3d-scene/Basic3DScene.tsx +250 -0
  186. package/lib/motion/shotcraft/upstream/demos/camera/crash-zoom-punch/CrashImpactReal.tsx +305 -0
  187. package/lib/motion/shotcraft/upstream/demos/camera/crash-zoom-punch/CrashZoomReal.tsx +246 -0
  188. package/lib/motion/shotcraft/upstream/demos/camera/cursor-flyover/CursorFlyover.tsx +390 -0
  189. package/lib/motion/shotcraft/upstream/demos/camera/depth-layer-moves/MultiplaneReal.tsx +138 -0
  190. package/lib/motion/shotcraft/upstream/demos/camera/graze-face-tour/GrazeFaceTour.tsx +542 -0
  191. package/lib/motion/shotcraft/upstream/demos/camera/overhead-camera-moves/OverheadTabletopDrop.tsx +315 -0
  192. package/lib/motion/shotcraft/upstream/demos/camera/overhead-camera-moves/TiltReveal.tsx +299 -0
  193. package/lib/motion/shotcraft/upstream/demos/camera/space-camera-moves/DroneDiveLanding.tsx +173 -0
  194. package/lib/motion/shotcraft/upstream/demos/camera/space-camera-moves/ExplodedView.tsx +348 -0
  195. package/lib/motion/shotcraft/upstream/demos/camera/steep-tilt-glide/SteepTiltGlide.tsx +366 -0
  196. package/lib/motion/shotcraft/upstream/demos/camera/tension-camera-moves/BulletTimeFreezeOrbit.tsx +279 -0
  197. package/lib/motion/shotcraft/upstream/demos/camera/tension-camera-moves/PullBackIsolation.tsx +184 -0
  198. package/lib/motion/shotcraft/upstream/demos/camera/terminal-3d/Terminal3D.tsx +390 -0
  199. package/lib/motion/shotcraft/upstream/demos/data/before-after-slider-scrub/BeforeAfterSliderScrub.tsx +155 -0
  200. package/lib/motion/shotcraft/upstream/demos/data/chart-live-moves/AxisRescaleShockV2.tsx +303 -0
  201. package/lib/motion/shotcraft/upstream/demos/data/chart-live-moves/OscilloscopeStreamV2.tsx +290 -0
  202. package/lib/motion/shotcraft/upstream/demos/data/chart-live-moves/UnitDotSwarmRegroupV2.tsx +278 -0
  203. package/lib/motion/shotcraft/upstream/demos/data/gauge-readout-moves/NeedleSweepSelftest.tsx +307 -0
  204. package/lib/motion/shotcraft/upstream/demos/data/gauge-readout-moves/TapeScrollFixedPointer.tsx +215 -0
  205. package/lib/motion/shotcraft/upstream/demos/data/scroll-brake-moves/BrakeReticleLock.tsx +299 -0
  206. package/lib/motion/shotcraft/upstream/demos/data/scroll-brake-moves/ChangelogScrollBrake.tsx +232 -0
  207. package/lib/motion/shotcraft/upstream/demos/data/timeline-travel/TimelineTravel.tsx +290 -0
  208. package/lib/motion/shotcraft/upstream/demos/effects/assemble-then-type-flyin/AssembleThenTypeFlyin.tsx +273 -0
  209. package/lib/motion/shotcraft/upstream/demos/effects/aurora-bloom-bg-flip/AuroraBloomBgFlip.tsx +167 -0
  210. package/lib/motion/shotcraft/upstream/demos/effects/brand-frame-snap/BrandFrameSnap.tsx +536 -0
  211. package/lib/motion/shotcraft/upstream/demos/effects/fui-hud-moves/ReticleLockOn.tsx +164 -0
  212. package/lib/motion/shotcraft/upstream/demos/effects/glow-flyline-moves/FlylineArc.tsx +175 -0
  213. package/lib/motion/shotcraft/upstream/demos/effects/glow-flyline-moves/GlowOrbAmbient.tsx +197 -0
  214. package/lib/motion/shotcraft/upstream/demos/effects/glow-flyline-moves/OrbFlylineRelay.tsx +241 -0
  215. package/lib/motion/shotcraft/upstream/demos/effects/icon-performance-moves/AttentionBounce.tsx +279 -0
  216. package/lib/motion/shotcraft/upstream/demos/effects/impact-feedback/AnimeImpact.tsx +230 -0
  217. package/lib/motion/shotcraft/upstream/demos/effects/impact-feedback/HitCounter.tsx +345 -0
  218. package/lib/motion/shotcraft/upstream/demos/effects/light-play-moves/SheenSweepRetry.tsx +275 -0
  219. package/lib/motion/shotcraft/upstream/demos/effects/light-play-moves/SpotlightSweepReveal.tsx +229 -0
  220. package/lib/motion/shotcraft/upstream/demos/effects/radial-ripple-phone-chips/RadialRipplePhoneChips.tsx +312 -0
  221. package/lib/motion/shotcraft/upstream/demos/effects/riso-print-hits/RisoMisregistrationHit.tsx +222 -0
  222. package/lib/motion/shotcraft/upstream/demos/effects/scan-bracket-sweep/ScanBracketSweep.tsx +295 -0
  223. package/lib/motion/shotcraft/upstream/demos/effects/scanline-annotate-focus/ScanlineAnnotateFocus.tsx +329 -0
  224. package/lib/motion/shotcraft/upstream/demos/effects/scanline-assemble-flyin/ScanlineAssembleFlyin.tsx +315 -0
  225. package/lib/motion/shotcraft/upstream/demos/effects/slam-entrance-moves/ImpactBurstKit.tsx +264 -0
  226. package/lib/motion/shotcraft/upstream/demos/effects/slam-entrance-moves/KanadaPerspectiveSnap.tsx +260 -0
  227. package/lib/motion/shotcraft/upstream/demos/effects/slam-entrance-moves/ScoreSlam.tsx +263 -0
  228. package/lib/motion/shotcraft/upstream/demos/effects/spotlight-sweep-moves/CornerSpotlightReveal.tsx +123 -0
  229. package/lib/motion/shotcraft/upstream/demos/effects/spotlight-sweep-moves/GlowWakeSleepPanel.tsx +187 -0
  230. package/lib/motion/shotcraft/upstream/demos/effects/spotlight-sweep-moves/SlideSpotlightPan.tsx +175 -0
  231. package/lib/motion/shotcraft/upstream/demos/interaction/autolayout-gap-dial/AutolayoutGapDial.tsx +305 -0
  232. package/lib/motion/shotcraft/upstream/demos/interaction/canvas-materialize-moves/DiagramCascadeBuild.tsx +350 -0
  233. package/lib/motion/shotcraft/upstream/demos/interaction/canvas-materialize-moves/PanelToCanvasMaterialize.tsx +352 -0
  234. package/lib/motion/shotcraft/upstream/demos/interaction/chip-grid-single-select-blackout/ChipGridSingleSelectBlackout.tsx +218 -0
  235. package/lib/motion/shotcraft/upstream/demos/interaction/collab-cursor-moves/CursorCastEnsemble.tsx +297 -0
  236. package/lib/motion/shotcraft/upstream/demos/interaction/glass-pill-dictation-typing/GlassPillDictationTyping.tsx +157 -0
  237. package/lib/motion/shotcraft/upstream/demos/interaction/hashtag-to-pill-materialize/HashtagToPillMaterialize.tsx +218 -0
  238. package/lib/motion/shotcraft/upstream/demos/interaction/picker-carousel-feature-cycle/PickerCarouselFeatureCycle.tsx +202 -0
  239. package/lib/motion/shotcraft/upstream/demos/interaction/segmented-thumb-hero/SegmentedThumbHero.tsx +265 -0
  240. package/lib/motion/shotcraft/upstream/demos/interaction/theme-switch-moves/PaletteThemeRipple.tsx +457 -0
  241. package/lib/motion/shotcraft/upstream/demos/interaction/theme-switch-moves/ThemeSweepToggle.tsx +446 -0
  242. package/lib/motion/shotcraft/upstream/demos/interaction/type-and-filter/TypeAndFilter.tsx +249 -0
  243. package/lib/motion/shotcraft/upstream/demos/interaction/voice-waveform-live/VoiceWaveformLive.tsx +278 -0
  244. package/lib/motion/shotcraft/upstream/demos/opening/crane-rise-reveal/CraneRiseReveal.tsx +332 -0
  245. package/lib/motion/shotcraft/upstream/demos/opening/dataviz-landscape-open/DatavizLandscapeOpen.tsx +358 -0
  246. package/lib/motion/shotcraft/upstream/demos/opening/fracture/Fracture.tsx +201 -0
  247. package/lib/motion/shotcraft/upstream/demos/opening/icon-field-colorize/IconFieldColorize.tsx +200 -0
  248. package/lib/motion/shotcraft/upstream/demos/opening/letterspace-materialize/LetterspaceMaterialize.tsx +208 -0
  249. package/lib/motion/shotcraft/upstream/demos/opening/magician-card-flourish/MagicianCardFlourish.tsx +341 -0
  250. package/lib/motion/shotcraft/upstream/demos/opening/orbit-ring-title-open/OrbitRingTitleOpen.tsx +389 -0
  251. package/lib/motion/shotcraft/upstream/demos/opening/spotlight-hero-card/SpotlightHeroCard.tsx +293 -0
  252. package/lib/motion/shotcraft/upstream/demos/opening/stroke-segment-build/StrokeSegmentBuild.tsx +183 -0
  253. package/lib/motion/shotcraft/upstream/demos/opening/text-as-mask/TextAsMask.tsx +245 -0
  254. package/lib/motion/shotcraft/upstream/demos/outro/grain-dissolve/GrainDissolve.tsx +222 -0
  255. package/lib/motion/shotcraft/upstream/demos/outro/logo-shrink-wordmark-lockup/LogoShrinkWordmarkLockup.tsx +223 -0
  256. package/lib/motion/shotcraft/upstream/demos/outro/neon-triple-marquee/NeonTripleMarquee.tsx +217 -0
  257. package/lib/motion/shotcraft/upstream/demos/outro/outro-group-photo-launch/OutroGroupPhotoLaunch.tsx +343 -0
  258. package/lib/motion/shotcraft/upstream/demos/outro/ui-strip-away-outro/UiStripAwayOutro.tsx +357 -0
  259. package/lib/motion/shotcraft/upstream/demos/outro/ui-to-brand-morph/IconFlipBloomLogo.tsx +187 -0
  260. package/lib/motion/shotcraft/upstream/demos/outro/ui-to-brand-morph/InputMorphsIntoLogo.tsx +333 -0
  261. package/lib/motion/shotcraft/upstream/demos/rhythm/beat-cut-moves/PaparazziFlash.tsx +274 -0
  262. package/lib/motion/shotcraft/upstream/demos/rhythm/beat-step-list-theme-cycle/BeatStepListThemeCycle.tsx +270 -0
  263. package/lib/motion/shotcraft/upstream/demos/rhythm/panel-grid-moves/ComicPanelSplit.tsx +430 -0
  264. package/lib/motion/shotcraft/upstream/demos/rhythm/quad-split-parallel-scenes/QuadSplitParallelScenes.tsx +252 -0
  265. package/lib/motion/shotcraft/upstream/demos/rhythm/rhythm-interrupt-moves/JumpCutPunchIn.tsx +106 -0
  266. package/lib/motion/shotcraft/upstream/demos/rhythm/sakuga-timing-shift/SakugaTimingShift.tsx +288 -0
  267. package/lib/motion/shotcraft/upstream/demos/rhythm/speed-ramp-freeze/FreezeAnnotateReal.tsx +362 -0
  268. package/lib/motion/shotcraft/upstream/demos/rhythm/trailer-grammar-moves/TrailerBumper.tsx +168 -0
  269. package/lib/motion/shotcraft/upstream/demos/transition/bottom-push-stack-wipe/BottomPushStackWipe.tsx +310 -0
  270. package/lib/motion/shotcraft/upstream/demos/transition/card-flip-reveal/CardFlipReveal.tsx +261 -0
  271. package/lib/motion/shotcraft/upstream/demos/transition/card-flock-tumble/CardFlockTumble.tsx +459 -0
  272. package/lib/motion/shotcraft/upstream/demos/transition/circle-match-iris/CircleMatchIris.tsx +294 -0
  273. package/lib/motion/shotcraft/upstream/demos/transition/gradient-transition/GradientTransition.tsx +161 -0
  274. package/lib/motion/shotcraft/upstream/demos/transition/line-carry-transition/LineCarryTransition.tsx +248 -0
  275. package/lib/motion/shotcraft/upstream/demos/transition/mosaic-reframe/MosaicReframe.tsx +359 -0
  276. package/lib/motion/shotcraft/upstream/demos/transition/page-turn-transitions/BarnDoorSplit.tsx +227 -0
  277. package/lib/motion/shotcraft/upstream/demos/transition/print-texture-transitions/InkBleedReveal.tsx +162 -0
  278. package/lib/motion/shotcraft/upstream/demos/transition/shot-transitions/PortalWipeV2.tsx +248 -0
  279. package/lib/motion/shotcraft/upstream/demos/transition/shot-transitions/WhipBrakeReal.tsx +186 -0
  280. package/lib/motion/shotcraft/upstream/demos/transition/tear-streak-transitions/GlitchDisplace.tsx +355 -0
  281. package/lib/motion/shotcraft/upstream/demos/transition/transition-hidden-cut/InvisibleCut.tsx +272 -0
  282. package/lib/motion/shotcraft/upstream/demos/transition/transition-hidden-cut/LightLeakBurn.tsx +262 -0
  283. package/lib/motion/shotcraft/upstream/demos/transition/transition-hidden-cut/VersusSlam.tsx +217 -0
  284. package/lib/motion/shotcraft/upstream/demos/transition/transition-travel/LetterformZoom.tsx +285 -0
  285. package/lib/motion/shotcraft/upstream/demos/transition/white-flash-logo-simplify-cut/WhiteFlashLogoSimplifyCut.tsx +205 -0
  286. package/lib/motion/shotcraft/upstream/demos/transition/wipe-transitions/BlindsSlice.tsx +178 -0
  287. package/lib/motion/shotcraft/upstream/demos/typography/blur-slide/BlurSlide.tsx +147 -0
  288. package/lib/motion/shotcraft/upstream/demos/typography/brace-expand/BraceExpand.tsx +143 -0
  289. package/lib/motion/shotcraft/upstream/demos/typography/brand-ink-open/BrandInkOpen.tsx +134 -0
  290. package/lib/motion/shotcraft/upstream/demos/typography/countdown-arc-scatter/CountdownArcScatter.tsx +217 -0
  291. package/lib/motion/shotcraft/upstream/demos/typography/flying-words/FlyingWords.tsx +198 -0
  292. package/lib/motion/shotcraft/upstream/demos/typography/glitch-cycle/GlitchCycle.tsx +221 -0
  293. package/lib/motion/shotcraft/upstream/demos/typography/gradient-word-sweep/GradientWordSweep.tsx +312 -0
  294. package/lib/motion/shotcraft/upstream/demos/typography/lead-word-zoom-assemble/LeadWordZoomAssemble.tsx +261 -0
  295. package/lib/motion/shotcraft/upstream/demos/typography/marker-underline-title/MarkerUnderlineTitle.tsx +121 -0
  296. package/lib/motion/shotcraft/upstream/demos/typography/outline-word-fill/OutlineWordFill.tsx +153 -0
  297. package/lib/motion/shotcraft/upstream/demos/typography/paper-title-card/PaperTitleCard.tsx +179 -0
  298. package/lib/motion/shotcraft/upstream/demos/typography/pill-chip-slot-cycle-handled/PillChipSlotCycleHandled.tsx +140 -0
  299. package/lib/motion/shotcraft/upstream/demos/typography/pill-slot-cycle/PillSlotCycle.tsx +309 -0
  300. package/lib/motion/shotcraft/upstream/demos/typography/text-column-converge/TextColumnConverge.tsx +191 -0
  301. package/lib/motion/shotcraft/upstream/demos/typography/title-demote-to-label/TitleDemoteToLabel.tsx +292 -0
  302. package/lib/motion/shotcraft/upstream/demos/typography/type-assembly-moves/LetterformDriftAssembly.tsx +135 -0
  303. package/lib/motion/shotcraft/upstream/demos/typography/type-assembly-moves/SplitTextStagger.tsx +124 -0
  304. package/lib/motion/shotcraft/upstream/demos/typography/type-assembly-moves/TextOnPath.tsx +255 -0
  305. package/lib/motion/shotcraft/upstream/demos/typography/type-entrance-moves/LetterDropPhysics.tsx +173 -0
  306. package/lib/motion/shotcraft/upstream/demos/typography/type-rhythm-sync/FontWeightPump.tsx +225 -0
  307. package/lib/motion/shotcraft/upstream/demos/typography/type-rhythm-sync/KaraokeFillSync.tsx +141 -0
  308. package/lib/motion/shotcraft/upstream/demos/typography/typewriter-moves/TypewriterErrorRetype.tsx +137 -0
  309. package/lib/motion/shotcraft/upstream/demos/typography/vertical-word-roll-blur-cycle/VerticalWordRollBlurCycle.tsx +147 -0
  310. package/lib/motion/shotcraft/upstream/demos/typography/word-relay-filmstrip/WordRelayFilmstrip.tsx +409 -0
  311. package/lib/motion/shotcraft/upstream/demos/typography/word-relay-geometry/WordRelayGeometry.tsx +280 -0
  312. package/lib/motion/shotcraft/upstream/demos/ui-entrance/avatar-bracket-carousel/AvatarBracketCarousel.tsx +271 -0
  313. package/lib/motion/shotcraft/upstream/demos/ui-entrance/bezier-source-converge-merge/BezierSourceConvergeMerge.tsx +385 -0
  314. package/lib/motion/shotcraft/upstream/demos/ui-entrance/card-stack/CardStack.tsx +312 -0
  315. package/lib/motion/shotcraft/upstream/demos/ui-entrance/carousel-3d/Carousel3D.tsx +303 -0
  316. package/lib/motion/shotcraft/upstream/demos/ui-entrance/clipcard-looping/ClipCardLooping.tsx +77 -0
  317. package/lib/motion/shotcraft/upstream/demos/ui-entrance/cloner-depth-echo/ClonerDepthEcho.tsx +311 -0
  318. package/lib/motion/shotcraft/upstream/demos/ui-entrance/deck-deal-flyin/DeckDealFlyin.tsx +307 -0
  319. package/lib/motion/shotcraft/upstream/demos/ui-entrance/doc-park-left-pill-deal/DocParkLeftPillDeal.tsx +349 -0
  320. package/lib/motion/shotcraft/upstream/demos/ui-entrance/document-typewriter-reveal/DocumentTypewriterReveal.tsx +255 -0
  321. package/lib/motion/shotcraft/upstream/demos/ui-entrance/draw-svg-trace/DrawSvgTrace.tsx +244 -0
  322. package/lib/motion/shotcraft/upstream/demos/ui-entrance/element-body-moves/ContactShadowLift.tsx +217 -0
  323. package/lib/motion/shotcraft/upstream/demos/ui-entrance/integration-hub-map/IntegrationHubMap.tsx +652 -0
  324. package/lib/motion/shotcraft/upstream/demos/ui-entrance/list-reveal/ListReveal.tsx +153 -0
  325. package/lib/motion/shotcraft/upstream/demos/ui-entrance/list-stack-press/ListStackPress.tsx +238 -0
  326. package/lib/motion/shotcraft/upstream/demos/ui-entrance/neon-frame-forerun/NeonFrameForerun.tsx +324 -0
  327. package/lib/motion/shotcraft/upstream/demos/ui-entrance/neon-frame-orbit-drop/NeonFrameForerunOrbit.tsx +354 -0
  328. package/lib/motion/shotcraft/upstream/demos/ui-entrance/page-waterfall-wall/PageWaterfallWall.tsx +101 -0
  329. package/lib/motion/shotcraft/upstream/demos/ui-entrance/page-waterfall-wall/VerticalTicker.tsx +129 -0
  330. package/lib/motion/shotcraft/upstream/demos/ui-entrance/product-card-progressive-assemble/ProductCardProgressiveAssemble.tsx +297 -0
  331. package/lib/motion/shotcraft/upstream/demos/ui-entrance/radial-wave/RadialWave.tsx +57 -0
  332. package/lib/motion/shotcraft/upstream/demos/ui-entrance/research-card-stack-scroll/ResearchCardStackScroll.tsx +155 -0
  333. package/lib/motion/shotcraft/upstream/demos/ui-entrance/row-embed/RowEmbed.tsx +175 -0
  334. package/lib/motion/shotcraft/upstream/demos/ui-entrance/value-stagger-gradient/ValueStaggerGradient.tsx +153 -0
  335. package/lib/motion/shotcraft/upstream/demos/ui-entrance/wall-reveal-moves/BentoLightUp.tsx +146 -0
  336. package/lib/motion/shotcraft/upstream/demos/ui-entrance/wall-reveal-moves/WireframeDrawOn.tsx +226 -0
  337. package/lib/motion/shotcraft/upstream/gallery/api/library.json +1 -0
  338. package/lib/motion/shotcraft/upstream/manifest.json +2715 -0
  339. package/lib/motion/shotcraft/upstream/references/aesthetic-rules.md +193 -0
  340. package/lib/motion/shotcraft/upstream/references/final-review.md +121 -0
  341. package/lib/motion/shotcraft/upstream/references/guided-free-creation.md +199 -0
  342. package/lib/motion/shotcraft/upstream/references/jianying-export.md +183 -0
  343. package/lib/motion/shotcraft/upstream/references/music-beat-sync.md +257 -0
  344. package/lib/motion/shotcraft/upstream/references/pipeline.md +382 -0
  345. package/lib/motion/shotcraft/upstream/references/publish-upload-post.md +111 -0
  346. package/lib/motion/shotcraft/upstream/references/sequences/promo-energy-arc.md +50 -0
  347. package/lib/motion/shotcraft/upstream/references/sfx-sourcing.md +95 -0
  348. package/lib/motion/shotcraft/upstream/references/shots/camera/basic-3d-scene.md +41 -0
  349. package/lib/motion/shotcraft/upstream/references/shots/camera/crash-zoom-punch.md +52 -0
  350. package/lib/motion/shotcraft/upstream/references/shots/camera/cursor-flyover.md +39 -0
  351. package/lib/motion/shotcraft/upstream/references/shots/camera/depth-layer-moves.md +39 -0
  352. package/lib/motion/shotcraft/upstream/references/shots/camera/graze-face-tour.md +57 -0
  353. package/lib/motion/shotcraft/upstream/references/shots/camera/overhead-camera-moves.md +51 -0
  354. package/lib/motion/shotcraft/upstream/references/shots/camera/space-camera-moves.md +46 -0
  355. package/lib/motion/shotcraft/upstream/references/shots/camera/steep-tilt-glide.md +52 -0
  356. package/lib/motion/shotcraft/upstream/references/shots/camera/tension-camera-moves.md +43 -0
  357. package/lib/motion/shotcraft/upstream/references/shots/camera/terminal-3d.md +42 -0
  358. package/lib/motion/shotcraft/upstream/references/shots/data/before-after-slider-scrub.md +48 -0
  359. package/lib/motion/shotcraft/upstream/references/shots/data/chart-live-moves.md +52 -0
  360. package/lib/motion/shotcraft/upstream/references/shots/data/gauge-readout-moves.md +45 -0
  361. package/lib/motion/shotcraft/upstream/references/shots/data/scroll-brake-moves.md +46 -0
  362. package/lib/motion/shotcraft/upstream/references/shots/data/timeline-travel.md +44 -0
  363. package/lib/motion/shotcraft/upstream/references/shots/effects/assemble-then-type-flyin.md +63 -0
  364. package/lib/motion/shotcraft/upstream/references/shots/effects/aurora-bloom-bg-flip.md +54 -0
  365. package/lib/motion/shotcraft/upstream/references/shots/effects/brand-frame-snap.md +58 -0
  366. package/lib/motion/shotcraft/upstream/references/shots/effects/fui-hud-moves.md +35 -0
  367. package/lib/motion/shotcraft/upstream/references/shots/effects/glow-flyline-moves.md +53 -0
  368. package/lib/motion/shotcraft/upstream/references/shots/effects/icon-performance-moves.md +42 -0
  369. package/lib/motion/shotcraft/upstream/references/shots/effects/impact-feedback.md +50 -0
  370. package/lib/motion/shotcraft/upstream/references/shots/effects/light-play-moves.md +46 -0
  371. package/lib/motion/shotcraft/upstream/references/shots/effects/radial-ripple-phone-chips.md +56 -0
  372. package/lib/motion/shotcraft/upstream/references/shots/effects/riso-print-hits.md +49 -0
  373. package/lib/motion/shotcraft/upstream/references/shots/effects/scan-bracket-sweep.md +50 -0
  374. package/lib/motion/shotcraft/upstream/references/shots/effects/scanline-annotate-focus.md +53 -0
  375. package/lib/motion/shotcraft/upstream/references/shots/effects/scanline-assemble-flyin.md +53 -0
  376. package/lib/motion/shotcraft/upstream/references/shots/effects/slam-entrance-moves.md +55 -0
  377. package/lib/motion/shotcraft/upstream/references/shots/effects/spotlight-sweep-moves.md +52 -0
  378. package/lib/motion/shotcraft/upstream/references/shots/interaction/autolayout-gap-dial.md +60 -0
  379. package/lib/motion/shotcraft/upstream/references/shots/interaction/canvas-materialize-moves.md +57 -0
  380. package/lib/motion/shotcraft/upstream/references/shots/interaction/chip-grid-single-select-blackout.md +51 -0
  381. package/lib/motion/shotcraft/upstream/references/shots/interaction/collab-cursor-moves.md +41 -0
  382. package/lib/motion/shotcraft/upstream/references/shots/interaction/glass-pill-dictation-typing.md +53 -0
  383. package/lib/motion/shotcraft/upstream/references/shots/interaction/hashtag-to-pill-materialize.md +60 -0
  384. package/lib/motion/shotcraft/upstream/references/shots/interaction/picker-carousel-feature-cycle.md +55 -0
  385. package/lib/motion/shotcraft/upstream/references/shots/interaction/segmented-thumb-hero.md +57 -0
  386. package/lib/motion/shotcraft/upstream/references/shots/interaction/theme-switch-moves.md +47 -0
  387. package/lib/motion/shotcraft/upstream/references/shots/interaction/type-and-filter.md +37 -0
  388. package/lib/motion/shotcraft/upstream/references/shots/interaction/voice-waveform-live.md +54 -0
  389. package/lib/motion/shotcraft/upstream/references/shots/opening/brand-ink-open.md +36 -0
  390. package/lib/motion/shotcraft/upstream/references/shots/opening/crane-rise-reveal.md +56 -0
  391. package/lib/motion/shotcraft/upstream/references/shots/opening/dataviz-landscape-open.md +56 -0
  392. package/lib/motion/shotcraft/upstream/references/shots/opening/fracture.md +40 -0
  393. package/lib/motion/shotcraft/upstream/references/shots/opening/icon-field-colorize.md +60 -0
  394. package/lib/motion/shotcraft/upstream/references/shots/opening/letterspace-materialize.md +57 -0
  395. package/lib/motion/shotcraft/upstream/references/shots/opening/magician-card-flourish.md +73 -0
  396. package/lib/motion/shotcraft/upstream/references/shots/opening/orbit-ring-title-open.md +87 -0
  397. package/lib/motion/shotcraft/upstream/references/shots/opening/spotlight-hero-card.md +55 -0
  398. package/lib/motion/shotcraft/upstream/references/shots/opening/stroke-segment-build.md +50 -0
  399. package/lib/motion/shotcraft/upstream/references/shots/opening/text-as-mask.md +59 -0
  400. package/lib/motion/shotcraft/upstream/references/shots/outro/grain-dissolve.md +38 -0
  401. package/lib/motion/shotcraft/upstream/references/shots/outro/logo-shrink-wordmark-lockup.md +38 -0
  402. package/lib/motion/shotcraft/upstream/references/shots/outro/neon-triple-marquee.md +59 -0
  403. package/lib/motion/shotcraft/upstream/references/shots/outro/outro-group-photo-launch.md +39 -0
  404. package/lib/motion/shotcraft/upstream/references/shots/outro/ui-strip-away-outro.md +61 -0
  405. package/lib/motion/shotcraft/upstream/references/shots/outro/ui-to-brand-morph.md +52 -0
  406. package/lib/motion/shotcraft/upstream/references/shots/rhythm/beat-cut-moves.md +50 -0
  407. package/lib/motion/shotcraft/upstream/references/shots/rhythm/beat-step-list-theme-cycle.md +66 -0
  408. package/lib/motion/shotcraft/upstream/references/shots/rhythm/panel-grid-moves.md +43 -0
  409. package/lib/motion/shotcraft/upstream/references/shots/rhythm/quad-split-parallel-scenes.md +38 -0
  410. package/lib/motion/shotcraft/upstream/references/shots/rhythm/rhythm-interrupt-moves.md +38 -0
  411. package/lib/motion/shotcraft/upstream/references/shots/rhythm/sakuga-timing-shift.md +56 -0
  412. package/lib/motion/shotcraft/upstream/references/shots/rhythm/speed-ramp-freeze.md +44 -0
  413. package/lib/motion/shotcraft/upstream/references/shots/rhythm/trailer-grammar-moves.md +38 -0
  414. package/lib/motion/shotcraft/upstream/references/shots/transition/bottom-push-stack-wipe.md +63 -0
  415. package/lib/motion/shotcraft/upstream/references/shots/transition/card-flip-reveal.md +57 -0
  416. package/lib/motion/shotcraft/upstream/references/shots/transition/card-flock-tumble.md +63 -0
  417. package/lib/motion/shotcraft/upstream/references/shots/transition/circle-match-iris.md +58 -0
  418. package/lib/motion/shotcraft/upstream/references/shots/transition/gradient-transition.md +45 -0
  419. package/lib/motion/shotcraft/upstream/references/shots/transition/line-carry-transition.md +55 -0
  420. package/lib/motion/shotcraft/upstream/references/shots/transition/mosaic-reframe.md +49 -0
  421. package/lib/motion/shotcraft/upstream/references/shots/transition/page-turn-transitions.md +42 -0
  422. package/lib/motion/shotcraft/upstream/references/shots/transition/print-texture-transitions.md +42 -0
  423. package/lib/motion/shotcraft/upstream/references/shots/transition/shot-transitions.md +45 -0
  424. package/lib/motion/shotcraft/upstream/references/shots/transition/tear-streak-transitions.md +48 -0
  425. package/lib/motion/shotcraft/upstream/references/shots/transition/transition-hidden-cut.md +55 -0
  426. package/lib/motion/shotcraft/upstream/references/shots/transition/transition-travel.md +43 -0
  427. package/lib/motion/shotcraft/upstream/references/shots/transition/white-flash-logo-simplify-cut.md +41 -0
  428. package/lib/motion/shotcraft/upstream/references/shots/transition/wipe-transitions.md +42 -0
  429. package/lib/motion/shotcraft/upstream/references/shots/typography/blur-slide.md +59 -0
  430. package/lib/motion/shotcraft/upstream/references/shots/typography/brace-expand.md +55 -0
  431. package/lib/motion/shotcraft/upstream/references/shots/typography/countdown-arc-scatter.md +65 -0
  432. package/lib/motion/shotcraft/upstream/references/shots/typography/document-typewriter-reveal.md +42 -0
  433. package/lib/motion/shotcraft/upstream/references/shots/typography/flying-words.md +64 -0
  434. package/lib/motion/shotcraft/upstream/references/shots/typography/glitch-cycle.md +64 -0
  435. package/lib/motion/shotcraft/upstream/references/shots/typography/gradient-word-sweep.md +54 -0
  436. package/lib/motion/shotcraft/upstream/references/shots/typography/lead-word-zoom-assemble.md +91 -0
  437. package/lib/motion/shotcraft/upstream/references/shots/typography/marker-underline-title.md +51 -0
  438. package/lib/motion/shotcraft/upstream/references/shots/typography/outline-word-fill.md +66 -0
  439. package/lib/motion/shotcraft/upstream/references/shots/typography/paper-title-card.md +38 -0
  440. package/lib/motion/shotcraft/upstream/references/shots/typography/pill-chip-slot-cycle-handled.md +66 -0
  441. package/lib/motion/shotcraft/upstream/references/shots/typography/pill-slot-cycle.md +58 -0
  442. package/lib/motion/shotcraft/upstream/references/shots/typography/text-column-converge.md +61 -0
  443. package/lib/motion/shotcraft/upstream/references/shots/typography/title-demote-to-label.md +64 -0
  444. package/lib/motion/shotcraft/upstream/references/shots/typography/type-assembly-moves.md +45 -0
  445. package/lib/motion/shotcraft/upstream/references/shots/typography/type-entrance-moves.md +41 -0
  446. package/lib/motion/shotcraft/upstream/references/shots/typography/type-rhythm-sync.md +51 -0
  447. package/lib/motion/shotcraft/upstream/references/shots/typography/typewriter-moves.md +42 -0
  448. package/lib/motion/shotcraft/upstream/references/shots/typography/vertical-word-roll-blur-cycle.md +63 -0
  449. package/lib/motion/shotcraft/upstream/references/shots/typography/word-relay-filmstrip.md +57 -0
  450. package/lib/motion/shotcraft/upstream/references/shots/typography/word-relay-geometry.md +75 -0
  451. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/avatar-bracket-carousel.md +59 -0
  452. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/bezier-source-converge-merge.md +67 -0
  453. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/card-stack.md +60 -0
  454. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/carousel-3d.md +66 -0
  455. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/cloner-depth-echo.md +40 -0
  456. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/deck-deal-flyin.md +45 -0
  457. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/doc-park-left-pill-deal.md +62 -0
  458. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/draw-svg-trace.md +55 -0
  459. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/element-body-moves.md +37 -0
  460. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/integration-hub-map.md +55 -0
  461. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/list-reveal.md +50 -0
  462. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/list-stack-press.md +43 -0
  463. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/neon-frame-forerun.md +55 -0
  464. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/neon-frame-orbit-drop.md +51 -0
  465. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/page-waterfall-wall.md +51 -0
  466. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/product-card-progressive-assemble.md +56 -0
  467. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/radial-wave.md +49 -0
  468. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/research-card-stack-scroll.md +57 -0
  469. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/row-embed.md +36 -0
  470. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/value-stagger-gradient.md +64 -0
  471. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/wall-reveal-moves.md +47 -0
  472. package/lib/motion/shotcraft/upstream/references/sound-design.md +170 -0
  473. package/lib/motion/shotcraft/upstream/references/workbench.md +157 -0
  474. package/lib/motion/shotcraft/upstream/template/TEMPLATE.md +103 -0
  475. package/lib/motion/shotcraft/upstream/template/THEMES.md +44 -0
  476. package/lib/motion/shotcraft/upstream/template/package.json +20 -0
  477. package/lib/motion/shotcraft/upstream/template/remotion.config.ts +6 -0
  478. package/lib/motion/shotcraft/upstream/template/src/Root.tsx +17 -0
  479. package/lib/motion/shotcraft/upstream/template/src/aifl/Caption.tsx +65 -0
  480. package/lib/motion/shotcraft/upstream/template/src/aifl/DigitRoll.tsx +48 -0
  481. package/lib/motion/shotcraft/upstream/template/src/aifl/FlashCut.tsx +24 -0
  482. package/lib/motion/shotcraft/upstream/template/src/aifl/Main.tsx +192 -0
  483. package/lib/motion/shotcraft/upstream/template/src/aifl/PaperTitleCard.tsx +111 -0
  484. package/lib/motion/shotcraft/upstream/template/src/aifl/live/PageCam.tsx +150 -0
  485. package/lib/motion/shotcraft/upstream/template/src/aifl/live/SceneDetail.tsx +158 -0
  486. package/lib/motion/shotcraft/upstream/template/src/aifl/live/SceneFlyIn.tsx +468 -0
  487. package/lib/motion/shotcraft/upstream/template/src/aifl/live/SceneOpen.tsx +552 -0
  488. package/lib/motion/shotcraft/upstream/template/src/aifl/live/SceneOutroLive.tsx +411 -0
  489. package/lib/motion/shotcraft/upstream/template/src/aifl/live/ScenePapers.tsx +200 -0
  490. package/lib/motion/shotcraft/upstream/template/src/aifl/live/SceneWbr.tsx +324 -0
  491. package/lib/motion/shotcraft/upstream/template/src/aifl/live-layout.json +389 -0
  492. package/lib/motion/shotcraft/upstream/template/src/index.ts +4 -0
  493. package/lib/motion/shotcraft/upstream/template/src/themes/ThemedFilm.tsx +7 -0
  494. package/lib/motion/shotcraft/upstream/template/src/themes/manifest.tsx +68 -0
  495. package/lib/motion/shotcraft/upstream/template/src/themes/palette-assets.json +1 -0
  496. package/lib/motion/shotcraft/upstream/template/src/themes/palette-assets.ts +24 -0
  497. package/lib/motion/shotcraft/upstream/template/src/themes/palettes.json +74 -0
  498. package/lib/motion/shotcraft/upstream/template/src/themes/visual-theme.tsx +91 -0
  499. package/lib/motion/shotcraft/upstream/template/src/workbench.ts +213 -0
  500. package/lib/motion/shotcraft/upstream/template/tsconfig.json +15 -0
  501. package/lib/motion/spec.js +113 -0
  502. package/lib/motion/style-engine.js +153 -0
  503. package/lib/motion/style-frames.js +268 -0
  504. package/lib/motion/sync.js +214 -0
  505. package/lib/motion/theme.js +121 -0
  506. package/lib/motion/voice.js +417 -0
  507. package/lib/motion-agent-files.js +82 -0
  508. package/lib/motion-agent-protocol.js +66 -0
  509. package/lib/motion-agent-tools.js +196 -0
  510. package/lib/motion-agent.js +171 -0
  511. package/lib/motion-capture.js +92 -0
  512. package/lib/motion-core.js +238 -0
  513. package/lib/motion-film-review.js +71 -0
  514. package/lib/motion-library.js +210 -0
  515. package/lib/motion-planner.js +254 -0
  516. package/lib/motion-project.js +80 -0
  517. package/lib/motion-render-worker.js +145 -0
  518. package/lib/motion-session.js +32 -0
  519. package/lib/motion-source.js +49 -0
  520. package/lib/motion-tasks.js +452 -0
  521. package/lib/motion-vision.js +43 -0
  522. package/lib/mxfile.js +27 -7
  523. package/lib/routing/node-runtime.js +14 -0
  524. package/lib/routing/router.js +211 -0
  525. package/lib/routing/vendor/README.md +96 -0
  526. package/lib/routing/vendor/libavoid-routing.js +753 -0
  527. package/lib/routing/vendor/libavoid.min.js +59 -0
  528. package/lib/shotcard-core.js +39 -0
  529. package/lib/shotcard-reference.js +56 -0
  530. package/lib/style-kernel.js +37 -3
  531. package/lib/task-patches.js +26 -2
  532. package/lib/video-export.js +17 -2
  533. package/package.json +29 -4
@@ -0,0 +1,963 @@
1
+ // 07-liquid — choreography. Every primitive is a closed-form function of t (seeded, no state).
2
+ import { baseState } from './lookdev.js';
3
+
4
+ const PI = Math.PI;
5
+ export const clamp = (x, a = 0, b = 1) => Math.min(b, Math.max(a, x));
6
+ export const lerp = (a, b, t) => a + (b - a) * t;
7
+ export const inv = (a, b, x) => clamp((x - a) / (b - a));
8
+ export const smooth = (t) => t * t * (3 - 2 * t);
9
+ export function bezier(x1, y1, x2, y2) {
10
+ const cx = 3 * x1, bx = 3 * (x2 - x1) - cx, ax = 1 - cx - bx;
11
+ const cy = 3 * y1, by = 3 * (y2 - y1) - cy, ay = 1 - cy - by;
12
+ const X = (s) => ((ax * s + bx) * s + cx) * s, Y = (s) => ((ay * s + by) * s + cy) * s, dX = (s) => (3 * ax * s + 2 * bx) * s + cx;
13
+ return (x) => {
14
+ if (x <= 0) return 0; if (x >= 1) return 1;
15
+ let lo = 0, hi = 1, s = x;
16
+ for (let i = 0; i < 12; i++) {
17
+ const e = X(s) - x;
18
+ if (Math.abs(e) < 1e-7) break;
19
+ if (e > 0) hi = s; else lo = s;
20
+ const d = dX(s);
21
+ s = Math.abs(d) > 1e-6 ? s - e / d : (lo + hi) / 2;
22
+ if (s <= lo || s >= hi) s = (lo + hi) / 2;
23
+ }
24
+ return Y(s);
25
+ };
26
+ }
27
+ export const flung = bezier(0.22, 1, 0.36, 1); // the brief's "thrown" ease
28
+ const inOut = bezier(0.65, 0, 0.35, 1);
29
+ const easeIn = bezier(0.5, 0, 0.9, 0.4);
30
+ const outSoft = bezier(0.33, 0.9, 0.4, 1);
31
+ const frontEase = bezier(0.28, 0.55, 0.42, 1);
32
+ // damped step response 0 -> 1 (overshoots)
33
+ function spring(t, f = 4, z = 0.3) {
34
+ if (t <= 0) return 0;
35
+ const w = 2 * PI * f, wd = w * Math.sqrt(1 - z * z);
36
+ return 1 - Math.exp(-z * w * t) * (Math.cos(wd * t) + (z * w / wd) * Math.sin(wd * t));
37
+ }
38
+ function wob(t, f, decay) { return t <= 0 ? 0 : Math.exp(-t / decay) * Math.sin(2 * PI * f * t); }
39
+ function rng(seed) { let a = seed >>> 0; return () => { a |= 0; a = (a + 0x6d2b79f5) | 0; let t = Math.imul(a ^ (a >>> 15), 1 | a); t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t; return ((t ^ (t >>> 14)) >>> 0) / 4294967296; }; }
40
+
41
+ // camera keys: t, zoom, centre x, centre y (Catmull-Rom, continuous velocity)
42
+ const CAM = [
43
+ // round 1: open in macro on the pour bulb (~560 px wide), tilt down with the fall, crown fills ~38 % of the frame at
44
+ // 0.40, then pull back across P2-P4 to the wide framing at 1.70
45
+ [-0.5, 5.4, 640, 382], [0.0, 5.3, 640, 398], [0.167, 4.9, 640, 452], [0.30, 3.8, 642, 690], [0.42, 3.3, 648, 800],
46
+ [0.8, 2.6, 1010, 832], [1.2, 2.15, 745, 812], // r2 s2 P8: ~1.15-1.2x tighter over P2-P3
47
+ [1.7, 1.6, 880, 758], [2.4, 1.64, 925, 764], [3.05, 1.72, 960, 764], [3.6, 1.46, 960, 590],
48
+ [4.0, 1.42, 960, 640], [4.6, 1.2, 960, 560], [5.0, 1.13, 960, 548], [5.9, 1.0, 960, 540], [8.0, 1.0, 960, 540],
49
+ [10.0, 1.0, 960, 540], [10.5, 1.0, 960, 540],
50
+ ];
51
+ function camAt(t) {
52
+ let i = 1;
53
+ while (i < CAM.length - 2 && t > CAM[i + 1][0]) i++;
54
+ const k0 = CAM[i - 1], k1 = CAM[i], k2 = CAM[i + 1], k3 = CAM[i + 2];
55
+ const u = Math.min(1, Math.max(0, (t - k1[0]) / (k2[0] - k1[0])));
56
+ const out = [];
57
+ for (let c = 1; c < 4; c++) {
58
+ // non-uniform Catmull-Rom tangents
59
+ const m1 = (k2[c] - k0[c]) / (k2[0] - k0[0]) * (k2[0] - k1[0]);
60
+ const m2 = (k3[c] - k1[c]) / (k3[0] - k1[0]) * (k2[0] - k1[0]);
61
+ const u2 = u * u, u3 = u2 * u;
62
+ out.push((2 * u3 - 3 * u2 + 1) * k1[c] + (u3 - 2 * u2 + u) * m1 + (-2 * u3 + 3 * u2) * k2[c] + (u3 - u2) * m2);
63
+ }
64
+ return out;
65
+ }
66
+ export const FY = 872, HOR = 786; // contact line, horizon
67
+ const G = 5000; // gravity px/s^2 (droplets, leaps, drips)
68
+
69
+ // ball helper: vertical stretch sv (>1 tall, <1 flat), resting on floor if onFloor
70
+ function ball(S, x, y, r, u, sv = 1, ang = PI / 2) { if (r > 0.3) S.balls.push(S._lk ? { x, y, r, u, s: sv, a: ang, lk: S._lk, main: S._main, la: S._la, note: S._note } : { x, y, r, u, s: sv, a: ang }); }
71
+ function seg(S, ax, ay, bx, by, ra, rb, u) { if (Math.max(ra, rb) > 0.35) S.segs.push(S._lk ? { ax, ay, bx, by, ra, rb, u, lk: S._lk } : { ax, ay, bx, by, ra, rb, u }); }
72
+ // reel v5 link tags: tag(S, ['dot'], 'dot', a, note) marks the next primitives; the one pushed while main is set is the principal
73
+ function tag(S, lk, main = null, a = 1, note = '') { if (!S.lnk) return; S._lk = lk; S._main = main; S._la = a; S._note = note; }
74
+ function untag(S) { if (S.lnk) { S._lk = null; S._main = null; } }
75
+
76
+ // ballistic droplet that lands on the floor and becomes a bead (or melts into a puddle)
77
+ function makeDroplet(o) {
78
+ // o: x0 y0 vx vy r u tEmit [tGrow] [melt(xHit) -> bool] [life]
79
+ o.fy = o.fy ?? 0; o.r *= 1 + o.fy / 180; // r2 s2: depth scale 0.8-1.17
80
+ const dy = FY + o.fy - o.r - o.y0;
81
+ const tf = (o.vy + Math.sqrt(o.vy * o.vy + 2 * G * dy)) / G; // vy is upward positive here
82
+ o.tHit = o.tEmit + tf; o.xHit = o.x0 + o.vx * tf;
83
+ o.melt = o.melt ? o.melt(o.xHit, o.tHit) : false;
84
+ o.vHit = -o.vy + G * tf;
85
+ return o;
86
+ }
87
+ function emitDroplet(S, d, t, extra = {}) {
88
+ const grow = d.tGrow ?? 0.066;
89
+ if (t < d.tEmit - grow) return;
90
+ let r = d.r;
91
+ if (t < d.tEmit) { // crown bulging out of the parent before it detaches
92
+ const k = inv(d.tEmit - grow, d.tEmit, t);
93
+ r *= 0.4 + 0.6 * k;
94
+ ball(S, d.x0, d.y0 + (1 - k) * 10, r, d.u, 1);
95
+ return;
96
+ }
97
+ if (t < d.tHit) {
98
+ const tt = t - d.tEmit;
99
+ const x = d.x0 + d.vx * tt, y = d.y0 - d.vy * tt + 0.5 * G * tt * tt;
100
+ const vx = d.vx, vy = -d.vy + G * tt, sp = Math.hypot(vx, vy);
101
+ const sv = 1 + Math.min(0.45, sp / 5200);
102
+ const ang = Math.atan2(vy, vx);
103
+ ball(S, x, y, r, d.u, sv, ang);
104
+ return;
105
+ }
106
+ const tl = t - d.tHit;
107
+ if (d.melt) { r *= 1 - smooth(inv(0, 0.12, tl)); if (r < 0.5) return; }
108
+ if (d.absorb !== undefined && t > d.absorb) r *= 1 - smooth(inv(d.absorb, d.absorb + 0.12, t));
109
+ if (r < 0.5) return;
110
+ const sv = 1.25 + (0.62 - 1.25) * spring(tl, 7, 0.38);
111
+ let x = d.xHit;
112
+ if (d.follow) x += d.follow(t);
113
+ ball(S, x, FY + d.fy - r * sv + 0.5, r, d.u, sv);
114
+ }
115
+
116
+ export function createFilm(cues, word, opts = {}) {
117
+ const V5 = !!opts.v5;
118
+ const R = rng(20260927);
119
+ const rr = (a, b) => a + (b - a) * R();
120
+
121
+ // ================= RAIN: four drops =================
122
+ const drops = [
123
+ { id: 'P1', x: 640, r: 56, u: 0.05, tLand: cues.landings[0].t, z: 0.5 },
124
+ { id: 'P2', x: 1250, r: 46, u: 0.62, tLand: cues.landings[1].t, z: 0.5 },
125
+ { id: 'P3', x: 405, r: 42, u: 0.95, tLand: cues.landings[2].t, z: 0.5 },
126
+ { id: 'P4', x: 960, r: 74, u: 0.36, tLand: cues.landings[3].t, z: 0.62 },
127
+ ];
128
+ const SV_PUD = 0.5; // resting puddle stretch (flat lens)
129
+ // landing squash: contact frame already squashing; overshoot below the resting lens is halved (no "hot-dog" pancake)
130
+ const svLand = (d, tl) => { const s = 1.3 + (SV_PUD - 1.3) * spring(tl + 1.3 / 30, 6, d.z); return s < SV_PUD ? SV_PUD - (SV_PUD - s) * 0.45 : s; };
131
+ // splash crown: a fan of tapered tendrils flung out of the splat (edges lean out, centre stands up). Each tip beads
132
+ // up on a thinning neck and pinches off 2-3 frames after impact (secondary droplets); the stub recoils with a rebound bump.
133
+ const TEN_T = 0.27;
134
+ const tendrils = drops.map((d) => {
135
+ const big = d.id === 'P4', n = big ? 7 : 5, out = [];
136
+ for (let k = 0; k < n; k++) {
137
+ const c = (k / (n - 1)) * 2 - 1 + rr(-0.07, 0.07);
138
+ const ang = c * 0.95 + rr(-0.12, 0.12); // radians from vertical
139
+ const L = d.r * (0.9 + 0.5 * Math.abs(c)) * rr(0.72, 1.22) * (big ? 1.08 : 1);
140
+ const tDet = 0.07 + rr(0, 1.1 / 30) + 0.02 * Math.abs(c);
141
+ out.push({ c, ang, L, tDet, rb: d.r * rr(0.1, 0.135) * rr(0.6, 1.4) });
142
+ }
143
+ return out;
144
+ });
145
+ function tendrilState(d, w, tl) {
146
+ if (tl < 0 || tl > TEN_T) return null;
147
+ const sv = svLand(d, tl), ax = d.r / sv, ay = d.r * sv, cy = FY - ay + 1;
148
+ const bx = d.x + w.c * ax * 0.46;
149
+ const by = cy - ay * Math.sqrt(Math.max(0, 1 - (w.c * 0.46) ** 2)) + ay * 0.45; // rooted just inside the splat top
150
+ let len, pinch = 0, bead = 0;
151
+ if (tl < w.tDet) {
152
+ const k = inv(0, w.tDet, tl);
153
+ len = w.L * flung(k); pinch = smooth(inv(0.45, 1, k)); bead = 0.55 + 0.45 * k;
154
+ } else {
155
+ const q = 1 - spring(tl - w.tDet, 5, 0.34); // recoil; negative lobe = rebound bump
156
+ len = w.L * 0.62 * (q >= 0 ? q : -0.45 * q) * (1 - smooth(inv(0.17, TEN_T, tl)));
157
+ }
158
+ const dx = Math.sin(w.ang), dy = -Math.cos(w.ang);
159
+ const droop = 0.22 * len * Math.abs(dx); // gravity bends leaning tendrils into arcs
160
+ const mx = bx + dx * len * 0.5, my = by + dy * len * 0.5 + droop * 0.25;
161
+ const tx = bx + dx * len, ty = by + dy * len + droop;
162
+ const ra = d.r * 0.16 * (1 - 0.3 * Math.abs(w.c)), rm = lerp(ra * 0.55, ra * 0.42, pinch), rt = lerp(rm * 0.8, d.r * 0.035, pinch);
163
+ return { bx, by, mx, my, tx, ty, ra, rm, rt, bead: bead * w.rb, len };
164
+ }
165
+ const crown = [];
166
+ drops.forEach((d, di) => {
167
+ const big = d.id === 'P4';
168
+ const melt = (xh) => Math.abs(xh - d.x) < d.r / SV_PUD * 0.85;
169
+ tendrils[di].forEach((w) => { // tip beads -> ballistic droplets
170
+ const s = tendrilState(d, w, w.tDet - 1e-4);
171
+ const ang = w.ang + rr(-0.21, 0.21), sp = rr(480, 760) * rr(0.75, 1.25) * (big ? 1.15 : 1);
172
+ const x0 = s.tx + Math.sin(w.ang) * w.rb, y0 = s.ty - Math.cos(w.ang) * w.rb, fy = rr(-36, 30); // r2 s2 P8: scattered in depth
173
+ const T = rr(6, 8.5) / 30 * (big ? 1.15 : 1), vy = (0.5 * G * T * T - (FY + fy - w.rb - y0)) / T;
174
+ crown.push(makeDroplet({
175
+ x0, y0, vx: Math.sin(ang) * sp * 1.1, vy, fy,
176
+ r: w.rb, u: d.u + rr(-0.05, 0.05), tEmit: d.tLand + w.tDet, tGrow: 0.001, owner: di, melt,
177
+ }));
178
+ });
179
+ for (let i = 0; i < (big ? 3 : 2); i++) { // fine spray from the splat rim
180
+ const side = i % 2 ? 1 : -1;
181
+ crown.push(makeDroplet({
182
+ x0: d.x + side * d.r * rr(0.9, 1.4), y0: FY - d.r * 0.55, vx: side * rr(320, big ? 820 : 640), vy: rr(520, big ? 1100 : 900),
183
+ r: rr(3.5, big ? 8 : 6), u: d.u + rr(-0.05, 0.05), tEmit: d.tLand + 2 / 30 + rr(0, 1 / 30), tGrow: 0.03, owner: di, melt, fy: rr(-36, 30),
184
+ }));
185
+ }
186
+ });
187
+ // landed beads closer than 15 px merge (the later one soaks into the earlier one) -> no dashed rows
188
+ { const land = crown.filter((c) => !c.melt).sort((a, b) => a.tHit - b.tHit);
189
+ land.forEach((c, i) => { for (let j = 0; j < i; j++) { const o = land[j]; if (!o.melt && Math.hypot(c.xHit - o.xHit, c.fy - o.fy) < 15 + c.r + o.r) { c.melt = true; break; } } }); }
190
+
191
+ // ================= FUSE: puddles hop into the centre mass =================
192
+ const MX = 960, GH = 5000, TH = 0.34;
193
+ const hops = [
194
+ { who: 0, tc: cues.merges[0] }, // P1 from the left, triggered by P4's landing
195
+ { who: 2, tc: cues.merges[1] }, // P3 from far left
196
+ { who: 1, tc: cues.merges[2] }, // P2 from the right, trailing bead tied by a thread that snaps
197
+ ];
198
+ hops.forEach((h) => { const d = drops[h.who]; h.side = Math.sign(d.x - MX); h.tTake = h.tc - TH; h.x0 = d.x; h.x1 = MX + h.side * 42; h.vy = GH * TH / 2 + 60 / TH; });
199
+ const hopOf = (i) => hops.find((h) => h.who === i);
200
+ function mergeK(i, t) { const h = hopOf(i); return smooth(inv(h.tc - 0.02, h.tc + 0.16, t)); }
201
+ function massShift(t) { let s = 0; for (const h of hops) s += h.side * 20 * wob(t - h.tc, 2.8, 0.22); return s; }
202
+ function massR(t) { let a = drops[3].r ** 2; for (const h of hops) a += drops[h.who].r ** 2 * mergeK(h.who, t); return Math.sqrt(a); }
203
+ // merged drops keep swirling inside the mass as coloured 'ghosts' (marbled interior, silhouette unchanged)
204
+ function ghosts(S, t, cx, cy, R, sv, fade = 1) {
205
+ hops.forEach((h, j) => {
206
+ const k = smooth(inv(h.tc + 0.02, h.tc + 0.3, t)) * fade;
207
+ if (k <= 0.01) return;
208
+ const a = j * 2.1 + 1.5 * (t - h.tc) + h.side * 0.9;
209
+ const rg = Math.min(drops[h.who].r * 0.9, R * 0.62);
210
+ // streak along the orbit tangent -> marbled swirl
211
+ const gx = cx + Math.cos(a) * 0.40 * R / sv, gy = cy + Math.sin(a) * 0.34 * R * sv;
212
+ const gb = { x: gx, y: gy, r: rg, u: drops[h.who].u, s: 1.7, a: a + PI / 2 + (sv < 0.8 ? 0 : 0), ghost: true, amt: 0.85 * k };
213
+ if (S._lk) gb.lk = S._lk;
214
+ S.balls.push(gb);
215
+ });
216
+ }
217
+ // ballistic hop: take-off centre height yb, lands onto the mass (slightly lower) at tc
218
+ const hLow = (h) => ({ ...h, vy: h.vy * 0.8 }); // s3: trailing bead on a flatter arc
219
+ function hopPos(h, r, tt) {
220
+ const yb = FY - r * 0.92, yl = FY - 50;
221
+ const x = lerp(h.x0, h.x1, tt / TH);
222
+ const y = yb - h.vy * tt + 0.5 * GH * tt * tt + (yl - yb) * (tt / TH);
223
+ const vx = (h.x1 - h.x0) / TH, vy = -h.vy + GH * tt + (yl - yb) / TH;
224
+ return { x, y, vx, vy };
225
+ }
226
+ // floor beads get pulled into their owner as it gathers itself to jump (P4's at the big leap)
227
+ crown.forEach((c) => {
228
+ if (c.melt) return;
229
+ const i = c.owner, d = drops[i];
230
+ const tA = i === 3 ? cues.leap - 0.3 : hopOf(i).tTake - 0.14;
231
+ c.follow = (t) => (d.x + Math.sign(c.xHit - d.x) * d.r * 1.2 - c.xHit) * flung(inv(tA, tA + 0.16, t));
232
+ c.absorb = tA + 0.1;
233
+ if (Math.abs(c.xHit - d.x) > 420) { c.follow = null; c.absorb = 99; }
234
+ });
235
+ const sat = { tSnap: hopOf(1).tTake + 0.13 };
236
+
237
+ // ================= LEAP: the merged mass jumps, thread snaps, lands =================
238
+ const tLeap = cues.leap, tApex = cues.apex, tImpact = 4.0;
239
+ const GL = 3750; // leap gravity (surface-tension jump reads floatier)
240
+ const vLeap = GL * (tApex - tLeap); // apex exactly at tApex
241
+ const massRLeap = 118, heroR = 132;
242
+ const leapY0 = FY - massRLeap * 1.0;
243
+ function sphereY(t) { const tt = t - tLeap; return leapY0 - vLeap * tt + 0.5 * GL * tt * tt; }
244
+ const tThread = cues.thread_snap;
245
+ const remR = 36;
246
+ // impact crown: big spray that leads the flood
247
+ const spray = [];
248
+ for (let i = 0; i < 14; i++) {
249
+ const side = i % 2 ? 1 : -1;
250
+ spray.push({ x0: MX + side * rr(40, 260), y0: FY - rr(30, 90), vx: side * rr(80, 700), vy: rr(1500, 2700), r: rr(9, 26), u: rr(0.2, 0.9), t0: tImpact + rr(1, 3) / 30 });
251
+ }
252
+
253
+ // ================= FLOOD =================
254
+ // round 1: three diagonal sheets launched from the impact point, alternating L->R / R->L / L->R; faster fronts and
255
+ // earlier drain so full immersion lasts ~0.45 s
256
+ const mkDir = (dx) => { const l = Math.hypot(dx, 1); return [dx / l, -1 / l]; };
257
+ const dir = mkDir(0.42); // violet (main) sheet: drives the blob reveal
258
+ const aOf = (x, y) => x * dir[0] + y * dir[1];
259
+ const bOf = (x, y) => -dir[1] * x + dir[0] * y;
260
+ function sheet(delay, rdelay, u, amp, freq, ph0, lean, dx, dur = 0.46) {
261
+ const d = dx === undefined ? dir : mkDir(dx);
262
+ const A = (x, y) => x * d[0] + y * d[1], B = (x, y) => -d[1] * x + d[0] * y;
263
+ const vis = [[220, 260], [1700, 260], [220, 1040], [1700, 1040]];
264
+ const all = [[-40, -80], [1960, -80], [-40, 1200], [1960, 1200]];
265
+ const aStart = Math.min(...vis.map(([x, y]) => A(x, y))) - 60, aImp = A(MX, FY - 20);
266
+ const aEnd = Math.max(...all.map(([x, y]) => A(x, y))) + 330, aLow = Math.min(...all.map(([x, y]) => A(x, y))) - 400;
267
+ const bs = all.map(([x, y]) => B(x, y)), bLo = Math.min(...bs) + 150, bHi = Math.max(...bs) - 150;
268
+ const bump0 = Math.max(380, aImp - aStart + 170), bumpC = B(MX, FY);
269
+ const t0 = tImpact + 0.035 + delay, t1 = t0 + dur;
270
+ const r0 = cues.reveal_start + rdelay, r1 = r0 + 0.6;
271
+ const frontAt = (t) => lerp(aStart, aEnd, frontEase(inv(t0, t1, t))) + 700 * smooth(inv(t1, t1 + 0.3, t));
272
+ const backAt = (t) => lerp(aLow, aEnd + 260, inOut(inv(r0, r1, t)));
273
+ return (t) => {
274
+ if (t < t0 || t > r1 + 0.02) return { on: false, front: 0, back: 0, phase: 0, amp: 0, freq: 0, lean: 0, u };
275
+ const front = frontAt(t), back = backAt(t);
276
+ const bumpAt = (t) => bump0 * smooth(inv(t0 - 0.01, t0 + 0.13, t)) * (1 - smooth(inv(t0 + 0.1, t0 + 0.45, t))); // s4: mound rises over ~4 f
277
+ const bump = bumpAt(t);
278
+ const vel = Math.max(Math.abs(frontAt(t + 1 / 60) - frontAt(t - 1 / 60)) + Math.abs(bumpAt(t + 1 / 60) - bumpAt(t - 1 / 60)),
279
+ Math.abs(backAt(t + 1 / 60) - backAt(t - 1 / 60))) * 0.5 + 6; // px per frame (+ wave travel)
280
+ return { on: true, front, back, phase: ph0 + t * 5.2, amp, freq, lean, u, dx: d[0], dy: d[1], bump, bumpC, vel, bLo, bHi };
281
+ };
282
+ }
283
+ const sheetV = sheet(0.0, 0.14, 0.9, 80, 0.0115, 0.3, 0.02, 0.42, 0.62); // s4: slower front (peak diff /1.3) // main field (violet) L->R, fuses with blobs
284
+ const sheetO = sheet(0.07, 0.07, 0.06, 85, 0.0102, 2.1, -0.03, -0.55); // tangerine R->L
285
+ const sheetP = sheet(0.14, 0.0, 0.46, 90, 0.0125, 4.4, 0.04, 0.3); // hot pink L->R, top sheet (crisp)
286
+ // droplets flung ahead of each sheet's front (coloured like their sheet, drawn in the main field)
287
+ const lead = [];
288
+ [[0.0, 0.88], [0.1, 0.08], [0.2, 0.48]].forEach(([delay, u], li) => {
289
+ for (let i = 0; i < 7; i++) lead.push({ delay, u: u + rr(-0.05, 0.05), bf: rr(0.02, 0.98), ahead: rr(60, 220), r: rr(8, 22), ph: rr(0, 6) });
290
+ });
291
+
292
+ // ================= LOGO =================
293
+ const L = word.letters;
294
+ const letterU = [0.04, 0.26, 0.5, 0.86];
295
+ const tSnap = cues.logo_snap;
296
+ // blobs left behind by the receding violet sheet, one cluster per letter
297
+ const clusters = L.map((l, i) => {
298
+ const w = l.bbox[2] - l.bbox[0], h = l.bbox[3] - l.bbox[1];
299
+ const cx = (l.bbox[0] + l.bbox[2]) / 2, cy = i === 1 || i === 2 ? (l.bbox[1] + l.bbox[3]) / 2 : (word.massY ?? word.baseY - 100);
300
+ const main = [74, 52, 64, 64][i];
301
+ return {
302
+ i, cx, cy: i === 3 ? cy + 10 : cy, u: letterU[i],
303
+ parts: [
304
+ { dx: 0, dy: 0, r: main, sx: rr(-70, 70), sy: rr(-60, 20) },
305
+ { dx: rr(-0.3, 0.3) * w, dy: -h * 0.28, r: main * 0.46, sx: rr(-120, 120), sy: rr(-150, -60) },
306
+ { dx: rr(-0.3, 0.3) * w, dy: h * 0.22, r: main * 0.36, sx: rr(-120, 120), sy: rr(-40, 60) },
307
+ ],
308
+ tFree: 0,
309
+ };
310
+ });
311
+ clusters.forEach((c) => {
312
+ const aTop = aOf(c.cx, c.cy - c.parts[0].r) + 150;
313
+ for (let tt = cues.reveal_start; tt < cues.reveal_start + 1.2; tt += 1 / 240) { const sh = sheetV(tt); if (sh.on && sh.back > aTop) { c.tFree = tt; break; } }
314
+ if (!c.tFree) c.tFree = cues.reveal_start + 0.6;
315
+ });
316
+ // droplets thrown off the letters at the snap (secondary, 2-3 frames late)
317
+ // the fused mass (6.2-6.4): four seed blobs squeezed into one horizontal bar centred on the x-height band
318
+ const massY = word.massY ?? word.baseY - 100, SLOT = 0.55; // v5 sets word.massY (CJK ink centre)
319
+ const slotX = L.map((l) => 960 + (l.sx0 - 960) * SLOT);
320
+ const MASS = { hx: 104, vy: 60 }; // semi-axes of each slot's ellipse at the squat
321
+ // r2 P1: on frames 189-191 the bar pinches three necks at the future letter gaps (each slot becomes a near-round bead)
322
+ const hxP = slotX.map((x, i) => 0.5 * Math.min(i > 0 ? x - slotX[i - 1] : 1e9, i < 3 ? slotX[i + 1] - x : 1e9) * 0.96);
323
+ const vyP = MASS.vy * 1.18;
324
+ const snapDrops = [];
325
+ for (let i = 0; i < 15; i++) {
326
+ const li = i % 4, side = li < 2 ? (R() < 0.8 ? -1 : 1) : (R() < 0.8 ? 1 : -1);
327
+ const x0 = lerp(L[li].bbox[0], L[li].bbox[2], rr(0.15, 0.85)), y0 = massY + rr(-60, 70), r = rr(4, 11);
328
+ const T = rr(8, 14) / 30, dy = FY - r - y0, vy = (0.5 * G * T * T - dy) / T;
329
+ snapDrops.push(makeDroplet({ x0, y0, vx: side * rr(260, 980), vy, r, u: letterU[li], tEmit: tSnap + rr(1, 2.2) / 30, tGrow: 0.03, melt: () => true, fy: rr(-4, 22) }));
330
+ }
331
+ // gooey bridges between neighbouring letters, stretched by the burst, snapping on the cue
332
+ const tBridge = cues.letter_bridge_snap;
333
+ const inkRow = (li, y, fromRight) => {
334
+ const b = L[li].bbox;
335
+ if (fromRight) { for (let x = Math.round(b[2]); x > b[0]; x--) if (word.sd(li, x, y) < -6) return x; }
336
+ else { for (let x = Math.round(b[0]); x < b[2]; x++) if (word.sd(li, x, y) < -6) return x; }
337
+ return fromRight ? b[2] : b[0];
338
+ };
339
+ const bridges = [0, 1, 2].map((i) => ({ i, j: i + 1, y: massY + [20, -10, 15][i], xa: inkRow(i, massY + [20, -10, 15][i], true) - 16, xb: inkRow(i + 1, massY + [20, -10, 15][i], false) + 16, r: [21, 18, 20][i] }));
340
+ // hanging drips under letters (living micro-motion) + the hero drip from the p's descender
341
+ const dripSpots = [
342
+ { li: 0, fx: 0.40, len: 26, r: 10, t0: 7.0 },
343
+ { li: 1, fx: 0.30, len: 16, r: 8, t0: 7.25 },
344
+ { li: 2, fx: 0.52, len: 30, r: 11, t0: 7.1 },
345
+ ].filter((d) => !(V5 && d.li === 1)).map((d) => { // v5.1: 态's spot becomes the fat drip
346
+ const l = L[d.li]; const x = lerp(l.bbox[0], l.bbox[2], d.fx);
347
+ return { ...d, x, y: (word.bottomAt(d.li, x) ?? l.bbox[3]) - 4 };
348
+ });
349
+ const pL = L[3];
350
+ let pxBest = (pL.bbox[0] + pL.bbox[2]) / 2, pyBest = -1;
351
+ for (let x = pL.bbox[0]; x < pL.bbox[2]; x += 2) { const y = word.bottomAt(3, x); if (y !== null && y > pyBest) { pyBest = y; pxBest = x; } }
352
+ // descender foot: find centre of the lowest run
353
+ let x0r = pxBest, x1r = pxBest;
354
+ while (word.bottomAt(3, x0r - 2) !== null && word.bottomAt(3, x0r - 2) >= pyBest - 3) x0r -= 2;
355
+ while (word.bottomAt(3, x1r + 2) !== null && word.bottomAt(3, x1r + 2) >= pyBest - 3) x1r += 2;
356
+ const hero = { x: (x0r + x1r) / 2, y: pyBest - 6, rMax: 25, tLand: cues.drip_land };
357
+ // bulb position at snap, solve free fall so the drip lands exactly on the cue
358
+ hero.ySnap = hero.y + 78; hero.vSnap = 240;
359
+ { const dy = FY - hero.rMax * 1.1 - hero.ySnap; const tf = (-hero.vSnap + Math.sqrt(hero.vSnap ** 2 + 2 * G * dy)) / G; hero.tSnap = hero.tLand - tf; }
360
+ hero.tStart = hero.tSnap - 0.85;
361
+ // reel v5.1: the OUT hand-off is a fat glossy drop (>= 120 px across at the snap) that swells under 态's 心 hook
362
+ // (x 760, where the next film's disc falls in), snaps at 8.27 and falls straight out through the bottom of the frame
363
+ // (no puddle); the reel detaches it at ~8.35-8.45 while it is still falling on screen
364
+ const fat = { x: 760, rMax: 64, tStart: 7.2, tSnap: 8.27, vSnap: 180, u: letterU[1] };
365
+ fat.y = (word.bottomAt(1, fat.x) ?? L[1].bbox[3]) - 6;
366
+ fat.ySnap = fat.y + 118; // bulb centre at the snap
367
+ fat.at = (tt) => fat.ySnap + fat.vSnap * tt + 0.5 * G * tt * tt;
368
+ fat.tFloor = fat.tSnap + (-fat.vSnap + Math.sqrt(fat.vSnap ** 2 + 2 * G * (FY - fat.ySnap))) / G; // passes the floor line
369
+ const splat = [];
370
+ for (let i = 0; i < 4; i++) {
371
+ const side = i % 2 ? 1 : -1;
372
+ splat.push(makeDroplet({ x0: hero.x + side * rr(10, 30), y0: FY - 10, vx: side * rr(120, 330), vy: rr(260, 520), r: rr(3, 5.5), u: 0.84, tEmit: hero.tLand + 2 / 30, tGrow: 0.05 }));
373
+ }
374
+ // time each sheet's front / back edge crosses the frame centre (for sound transients)
375
+ const aC = aOf(960, 560);
376
+ const crossT = (fn, key) => { for (let tt = 3.9; tt < 6.2; tt += 1 / 240) { const sh = fn(tt); if (sh.on && sh[key] > aC) return tt; } return null; };
377
+ const sheetCross = [sheetV, sheetO, sheetP].map((fn) => ({ in: crossT(fn, 'front'), out: crossT(fn, 'back') }));
378
+ const floodBub = [];
379
+ for (let i = 0; i < 30; i++) floodBub.push({ x: rr(260, 1660), y0: rr(560, 1120), sp: rr(240, 520), r: rr(1.6, 4.6), t0: rr(4.14, 4.62), dur: rr(0.45, 0.8), wob: rr(3, 10), wf: rr(8, 16), ph: rr(0, 6) });
380
+ // soda carbonation: bubbles rising inside the letter stems, popping at the top
381
+ const bubbles = [];
382
+ L.forEach((l, li) => {
383
+ const cols = [];
384
+ for (let x = l.bbox[0] + 6; x < l.bbox[2] - 6; x += 3) {
385
+ let y = l.bbox[3] - 4, bottom = null, top = null;
386
+ for (; y > l.bbox[1]; y -= 2) { const d = word.sd(li, x, y); if (d < -9) { if (bottom === null) bottom = y; top = y; } else if (bottom !== null) break; }
387
+ if (bottom !== null && bottom - top > 90) cols.push({ x, bottom, top });
388
+ }
389
+ for (let j = 0; j < 7 && cols.length; j++) {
390
+ const c = cols[Math.floor(R() * cols.length)];
391
+ bubbles.push({ li, x: c.x, y0: c.bottom - 4, y1: c.top + 10, sp: rr(38, 62), r: rr(3.4, 7.2), ph: R(), t0: tSnap + 0.35 + rr(0, 0.9), wob: rr(1.5, 3.5), wf: rr(5, 9) });
392
+ }
393
+ });
394
+ const KICKS = [7.2, 7.4, 8.0];
395
+ const JELLY_HITS = [[7.2, 0.07, 1], [7.6, 0.035, 1], [8.0, 0.05, 1], [8.4, 0.045, -1], [8.8, 0.03, 1], [9.2, 0.018, 1]]; // half-time snare 7.2, kicks 7.4 / 8.0 in the drop
396
+ window.__cuesOut = {
397
+ sheetCross, jelly: [7.2, 8.0],
398
+ drip_snap: hero.tSnap, drip_land: hero.tLand, heroX: hero.x,
399
+ crown: crown.map((c) => ({ t: c.tHit, x: c.xHit, r: c.r, melt: c.melt })),
400
+ snapDrops: snapDrops.map((c) => ({ t: c.tHit, x: c.xHit, r: c.r })),
401
+ splat: splat.map((c) => ({ t: c.tHit, x: c.xHit, r: c.r })),
402
+ clusterFree: clusters.map((c) => c.tFree), satSnap: sat.tSnap, impact: tImpact,
403
+ };
404
+
405
+ // =====================================================================
406
+ return function film(t) {
407
+ const S = baseState(t);
408
+ S.floorY = FY; S.horizon = HOR;
409
+ if (V5) S.lnk = {};
410
+ // v5.1 grade (shader #define V5): the set sinks to near-black plum during the gather / held breath, the title liquid
411
+ // (lighter body, brighter diffuse, hotter speculars; 动 violet -> bright pink-violet) switches on under the split flash
412
+ if (V5) { const lift = smooth(inv(tSnap - 1 / 60, tSnap + 1.5 / 30, t));
413
+ // w: the fat drip falls in front of the set, so its floor reflection fades out once it lets go (band around its x)
414
+ S.v5 = [smooth(inv(6.05, 6.38, t)), lift, lift, smooth(inv(fat.tSnap, fat.tSnap + 0.1, t))]; S.v5b = [fat.x, 130, 0, 0]; }
415
+
416
+ // ---------------- camera ----------------
417
+ const cz = camAt(t);
418
+ let punch = 0.016 * wob(t - tImpact, 3.0, 0.2);
419
+ drops.forEach((d, i) => { punch += (i === 3 ? 0.022 : 0.013) * wob(t - d.tLand, 3.2, 0.16); });
420
+ let zmul = 1;
421
+ if (t >= tSnap) {
422
+ const ts = t - tSnap;
423
+ zmul = ts < 2 / 30 ? lerp(1, 1.07, outSoft(ts * 15)) : ts < 14 / 30 ? lerp(1.07, 1.02, inOut(inv(2 / 30, 14 / 30, ts))) : 1.02; // s4: harder punch
424
+ zmul += 0.045 * (0.5 - 0.5 * Math.cos(PI * inv(tSnap + 14 / 30, 10.0, t))); // r2 P3: push to ~1.065 // easeInOutSine push
425
+ }
426
+ S.cam = { zoom: cz[0] * (1 + punch) * zmul, px: cz[1], py: cz[2] - 60 * (zmul - 1), ox: 0, oy: 0 };
427
+
428
+ // ---------------- goo ----------------
429
+ S.k = 0.1;
430
+ if (t > 5.2) S.k = lerp(0.062, 0.105, smooth(inv(tSnap, tSnap + 0.2, t)));
431
+ if (t > 5.2 && t < 5.9) S.k = lerp(0.1, 0.062, smooth(inv(5.2, 5.9, t)));
432
+
433
+ // ================= POUR (P1) =================
434
+ const P1 = drops[0];
435
+ const tPS = cues.pour_snap;
436
+ const pourGeo = (tt) => {
437
+ const bottom = 454 + 45 * tt + 2500 * tt * tt, v = 45 + 5000 * tt;
438
+ const neckPull = tt < tPS ? Math.pow(tt / tPS, 1.6) : 0;
439
+ const sv = 1 + 0.26 * clamp(v / 2045) + 0.16 * neckPull;
440
+ const cy = bottom - P1.r * sv, top = cy - P1.r * sv * 0.82;
441
+ return { bottom, v, neckPull, sv, cy, top, pinchY: top - 34 - 30 * neckPull };
442
+ };
443
+ if (t < P1.tLand) {
444
+ const bottom = 454 + 45 * t + 2500 * t * t, v = 45 + 5000 * t;
445
+ const neckPull = t < tPS ? Math.pow(t / tPS, 1.6) : 0;
446
+ const sv = 1 + 0.26 * clamp(v / 2045) + 0.16 * neckPull;
447
+ const cy = bottom - P1.r * sv;
448
+ tag(S, ['dot'], 'dot', 1, 'pour bulb (macro drop, ball lens)');
449
+ ball(S, P1.x, cy, P1.r, P1.u, sv);
450
+ tag(S, ['dot']);
451
+ // round 1: the bulb is a ball lens — a small inverted 'drop' wordmark is refracted inside it (logo in frame 1)
452
+ const wb = word.bbox, ww = wb[2] - wb[0];
453
+ S.lens = [P1.x, cy, P1.r / sv, P1.r * sv, (wb[0] + wb[2]) / 2 - word.offX, (wb[1] + wb[3]) / 2 - word.offY, ww / 0.92, 0.72 * (1 - smooth(inv(0.25, 0.35, t)))];
454
+ if (t < tPS) {
455
+ // stretched neck: thick thread from above thinning into a pinch just above the bulb
456
+ const top = cy - P1.r * sv * 0.82;
457
+ const pinchY = top - 34 - 30 * neckPull;
458
+ untag(S); seg(S, P1.x, -90, P1.x, pinchY, 25, lerp(8, 1.2, neckPull), P1.u + 0.02); tag(S, ['dot']);
459
+ seg(S, P1.x, pinchY, P1.x, top + 8, lerp(8, 1.2, neckPull), lerp(16, 12, neckPull), P1.u);
460
+ } else {
461
+ // lower stub recoils into the bulb (2-3 frame rebound) — teardrop tail
462
+ const k = spring(t - tPS, 7, 0.45);
463
+ const top = cy - P1.r * sv * 0.8;
464
+ const len = 42 * (1 - k);
465
+ if (len > 1) seg(S, P1.x, top - len, P1.x, top + 6, 3.5 * (1 - k) + 1, 13, P1.u);
466
+ ball(S, P1.x, top - len, 5 * (1 - k * 0.8), P1.u);
467
+ }
468
+ untag(S);
469
+ }
470
+ if (t >= tPS && t < 1.4) {
471
+ // upper thread: snaps back up with overshoot, then the pour is pulled out of frame
472
+ const endSnap = pourGeo(tPS - 1e-4).pinchY;
473
+ const k = spring(t - tPS, 5.5, 0.32);
474
+ const end = endSnap - 58 * k - 1500 * easeIn(inv(0.34, 1.2, t));
475
+ if (end > -80) {
476
+ seg(S, P1.x, -90, P1.x, end, 25 - 6 * inv(0.3, 1.2, t), 7 + 3 * k, P1.u + 0.02);
477
+ ball(S, P1.x, end, 9 + 3 * k, P1.u + 0.02);
478
+ }
479
+ }
480
+
481
+ // ================= drops falling, landing, hopping =================
482
+ drops.forEach((d, i) => {
483
+ if (i === 0 && t < d.tLand) return;
484
+ if (t < d.tLand) {
485
+ const tau = d.tLand - t, vL = 2500, Gd = 4200;
486
+ if (tau > 0.52) return;
487
+ const bottom = FY - vL * tau + 0.5 * Gd * tau * tau, v = vL - Gd * tau;
488
+ const sv = 1 + 0.30 * clamp(v / vL);
489
+ const cy = bottom - d.r * sv;
490
+ if (i === 3) tag(S, ['dot'], 'dot', 1, 'falling drop P4 (becomes the mass)');
491
+ ball(S, d.x, cy, d.r, d.u, sv);
492
+ if (i === 3) tag(S, ['dot']);
493
+ const lag = 0.03 * v;
494
+ ball(S, d.x, cy - d.r * sv * 0.9 - lag, d.r * 0.32, d.u, 1.3);
495
+ seg(S, d.x, cy - d.r * 0.3, d.x, cy - d.r * sv * 0.9 - lag, d.r * 0.5, d.r * 0.18, d.u);
496
+ untag(S);
497
+ return;
498
+ }
499
+ if (t >= 4.0) return;
500
+ const tl = t - d.tLand;
501
+ if (i === 3 && t <= tLeap - 0.25) tag(S, ['dot']); // v5: P4's own crown goes with the dot
502
+ // crown arms
503
+ tendrils[i].forEach((w) => {
504
+ const a = tendrilState(d, w, tl); if (!a || a.len < 1) return;
505
+ seg(S, a.bx, a.by, a.mx, a.my, a.ra, a.rm, d.u); seg(S, a.mx, a.my, a.tx, a.ty, a.rm, a.rt, d.u);
506
+ if (a.bead > 0.5) ball(S, a.tx + Math.sin(w.ang) * a.bead * 0.8, a.ty - Math.cos(w.ang) * a.bead * 0.8, a.bead, d.u, 1.12, w.ang - PI / 2);
507
+ });
508
+ untag(S);
509
+ if (i === 3) {
510
+ if (t > tLeap - 0.25) return; // the mass is handled by the leap block
511
+ const r = massR(t);
512
+ let sv = svLand(d, tl);
513
+ for (const h of hops) sv += 0.16 * wob(t - h.tc, 3.6, 0.2);
514
+ tag(S, ['dot'], 'dot', 1, 'the mass: P4 puddle swallowing the hoppers (flat lens)');
515
+ ball(S, MX + massShift(t), FY - r * sv + 1.0, r, d.u, sv);
516
+ tag(S, ['dot']);
517
+ ghosts(S, t, MX + massShift(t), FY - r * sv + 1.0, r, sv);
518
+ untag(S);
519
+ return;
520
+ }
521
+ const h = hopOf(i);
522
+ const r = d.r;
523
+ // trailing secondary droplet: same arc, 3 frames late, a third of the size
524
+ const emitHopper = (rs, delay, uOff) => {
525
+ const tt = t - delay - h.tTake, rr_ = r * rs;
526
+ if (V5 && i === 1 && rs === 1 && tt >= 0) tag(S, ['hop'], 'hop', tt < TH ? 1 : 1 - smooth(inv(0, 0.12, t - delay - h.tc)), 'hopper P2 (ballistic blob merging into the mass)');
527
+ const res = hopBody(rs, tt, rr_, uOff, delay);
528
+ untag(S);
529
+ return res;
530
+ };
531
+ const hopBody = (rs, tt, rr_, uOff, delay) => {
532
+ if (tt < -0.1) { // resting puddle
533
+ if (rs < 1) return; // bead is part of the puddle until it launches
534
+ const sv = svLand(d, tl);
535
+ ball(S, h.x0, FY - rr_ * sv + 1, rr_, d.u, sv);
536
+ return;
537
+ }
538
+ if (tt < 0) { // anticipation: squat, gather toward the jump
539
+ const k = inv(-0.1, 0, tt);
540
+ const sv = lerp(rs < 1 ? 0.62 : SV_PUD, 0.40, smooth(k));
541
+ const x = rs < 1 ? h.x0 - h.side * r * 1.25 : h.x0 + h.side * 8 * k;
542
+ ball(S, x, FY - rr_ * sv + 1, rr_, d.u + uOff, sv);
543
+ return;
544
+ }
545
+ if (tt < TH) { // flight
546
+ const P = hopPos(rs < 1 ? hLow(h) : h, rr_, tt);
547
+ const sp = Math.hypot(P.vx, P.vy);
548
+ let sv = 1 + 0.26 * Math.min(1, sp / 1400) + 0.35 * Math.exp(-tt / 0.05);
549
+ sv = lerp(sv, 0.74, smooth(inv(TH - 0.06, TH, tt))); // s3: flattens against the host on the way in
550
+ ball(S, P.x, P.y, rr_, d.u + uOff, sv, Math.atan2(P.vy, P.vx));
551
+ if (tt < 0.09 && rs === 1) { // take-off neck to the floor, snapping
552
+ const k = tt / 0.09;
553
+ seg(S, h.x0, FY - 6, lerp(h.x0, P.x, 0.5), lerp(FY - 6, P.y, 0.5), lerp(rr_ * 0.5, 8, k), lerp(rr_ * 0.4, 0.6, k), d.u);
554
+ }
555
+ return;
556
+ }
557
+ const tm = t - delay - h.tc; // merging into the mass
558
+ // round 1: contact squash to 0.6 h in 2 f, sink into the host over 4-5 f (no head-and-beak bump on top), and a
559
+ // bump travelling across the host's top (~33 px/frame)
560
+ const k = smooth(inv(0, 0.15, tm)), ks = smooth(inv(0, 0.05, tm)), kd = smooth(inv(0.0, 0.15, tm));
561
+ const P = hopPos(rs < 1 ? hLow(h) : h, rr_, TH);
562
+ const x = lerp(P.x, MX + massShift(t), k * 0.8), y = lerp(P.y + rr_ * 0.35 * ks, FY - massR(t) * 0.42, kd);
563
+ const rm = rr_ * Math.sqrt(1 - k);
564
+ ball(S, x, y, rm, d.u + uOff, lerp(0.74, 0.55, ks) * (1 - 0.1 * k));
565
+ if (rs === 1 && tm > 0.02 && tm < 0.2) {
566
+ const kb = inv(0.02, 0.2, tm);
567
+ ball(S, lerp(P.x, MX - h.side * 90, kb) + massShift(t), P.y + rr_ * 0.45 + 6 * kb, rr_ * 0.55 * Math.sin(PI * kb), d.u + uOff, 0.7);
568
+ }
569
+ };
570
+ emitHopper(1, 0, 0);
571
+ emitHopper(0.27, 0.17, 0.03);
572
+ // P2: thread from the trailing bead to the jumping puddle stretches and snaps (2-3 frame rebound)
573
+ if (i === 1) {
574
+ const tt = t - h.tTake;
575
+ if (V5) tag(S, ['hop']);
576
+ if (tt > 0 && tt < 0.4) {
577
+ const P = hopPos(h, r, Math.min(tt, TH));
578
+ const bx = h.x0 - h.side * r * 1.25, by = FY - r * 0.34 * 0.5;
579
+ if (t < sat.tSnap) {
580
+ const k = inv(h.tTake, sat.tSnap, t);
581
+ const mx = lerp(bx, P.x, 0.5), my = lerp(by, P.y, 0.5) + 10;
582
+ const rm = lerp(6, 0.6, Math.pow(k, 0.8));
583
+ seg(S, bx, by, mx, my, 7, rm, d.u + 0.03);
584
+ seg(S, mx, my, P.x - h.side * -r * 0.3, P.y + r * 0.3, rm, 10, d.u);
585
+ } else {
586
+ const k = spring(t - sat.tSnap, 7, 0.4);
587
+ const f = 0.45 * (1 - k);
588
+ if (f > 0.02) {
589
+ seg(S, bx, by, lerp(bx, P.x, f), lerp(by, P.y, f), 6, 2.5, d.u + 0.03);
590
+ seg(S, P.x, P.y + r * 0.4, lerp(P.x, bx, f), lerp(P.y, by, f), 9, 2.5, d.u);
591
+ }
592
+ }
593
+ }
594
+ untag(S);
595
+ }
596
+ });
597
+ // crown droplets / beads
598
+ crown.forEach((c) => { if (t < 4.05) { if (c.owner === 3) tag(S, ['dot']); emitDroplet(S, c, t); untag(S); } });
599
+
600
+ // ================= LEAP =================
601
+ if (t >= tLeap - 0.25 && t < tImpact + 0.3) {
602
+ const u = 0.36;
603
+ if (t < tLeap) {
604
+ // anticipation: gather into a dome, then a quick squat
605
+ const k = inOut(inv(tLeap - 0.25, tLeap - 0.07, t));
606
+ let sv = lerp(SV_PUD, 0.86, k);
607
+ sv = lerp(sv, 0.55, smooth(inv(tLeap - 0.08, tLeap, t)));
608
+ const r = lerp(massR(t), massRLeap, k);
609
+ tag(S, ['dot'], 'dot', 1, 'the mass gathering into a dome');
610
+ ball(S, MX, FY - r * sv + 1, r, u, sv);
611
+ tag(S, ['dot']);
612
+ ghosts(S, t, MX, FY - r * sv + 1, r, sv);
613
+ untag(S);
614
+ } else if (t < tImpact) {
615
+ const tt = t - tLeap;
616
+ const y = sphereY(t);
617
+ const vy = -vLeap + GL * tt;
618
+ const r = lerp(massRLeap, heroR, smooth(inv(tLeap, tApex, t)));
619
+ let sv = 1 + 0.36 * Math.exp(-tt / 0.12) * Math.cos(tt * 18) + 0.10 * wob(tt - 0.08, 3.4, 0.35) + 0.14 * smooth(inv(tApex + 0.1, tImpact, t));
620
+ // s3 P8: +-5 % jelly wobble at 3 Hz around the apex
621
+ sv += 0.05 * Math.sin(2 * PI * 3 * (t - tApex)) * smooth(inv(tApex - 0.35, tApex - 0.12, t)) * (1 - smooth(inv(tApex + 0.2, tApex + 0.32, t)));
622
+ // s3 P8: the four palette colours swirl through the interior as 3-4 curling bands (shader uSw)
623
+ S.sw = [MX, y, r * 1.02, 0.82 * smooth(inv(tLeap + 0.04, tLeap + 0.3, t)) * (1 - smooth(inv(tImpact - 0.03, tImpact + 0.02, t)))];
624
+ tag(S, ['dot'], 'dot', 1, 'hero sphere leaping (swirl bands inside)');
625
+ ball(S, MX, y, r, u, sv);
626
+ tag(S, ['dot']);
627
+ ghosts(S, t, MX, y, r * 0.95, sv);
628
+ // jelly wobble lobes (surface modes)
629
+ const wa = 0.5 + 0.5 * Math.exp(-tt / 0.4);
630
+ for (let j = 0; j < 3; j++) {
631
+ const ang = j * 2.1 + tt * (1.6 + j * 0.4);
632
+ ball(S, MX + Math.cos(ang) * r * 0.55, y + Math.sin(ang) * r * 0.55, r * 0.42 * wa, u + 0.04 * (j - 1), 1);
633
+ }
634
+ untag(S);
635
+ // remnant puddle + stretched thread
636
+ const remSv = 0.5 + 0.25 * wob(t - tThread, 6, 0.2);
637
+ const remR2 = remR * (1 - smooth(inv(tImpact - 0.08, tImpact, t)));
638
+ ball(S, MX, FY - remR2 * remSv + 1, remR2, u + 0.05, remSv);
639
+ const sb = y + r * sv * 0.85, rt = FY - remR2 * remSv * 1.6;
640
+ if (t < tThread) {
641
+ const k = inv(tLeap, tThread, t);
642
+ const my = lerp(sb, rt, 0.55);
643
+ const rm = lerp(14, 0.7, Math.pow(k, 0.9));
644
+ tag(S, ['dot']); seg(S, MX, sb - 10, MX, my, lerp(38, 16, k), rm, u); untag(S);
645
+ seg(S, MX, my, MX, rt + 6, rm, lerp(22, 12, k), u + 0.04);
646
+ } else {
647
+ const k = spring(t - tThread, 6.5, 0.36);
648
+ const up = (rt - sb) * 0.45 * (1 - k);
649
+ if (up > 2) {
650
+ tag(S, ['dot']); seg(S, MX, sb - 8, MX, sb + up, 14, 3, u); untag(S);
651
+ seg(S, MX, rt - up, MX, rt + 6, 3, 12, u + 0.04);
652
+ ball(S, MX, rt - up, 4, u + 0.04);
653
+ }
654
+ }
655
+ } else {
656
+ // impact: splat that feeds the rising flood
657
+ const tl = t - tImpact;
658
+ const sv = 1.15 + (0.3 - 1.15) * spring(tl, 5, 0.5);
659
+ const drain = 1 - smooth(inv(0.05, 0.19, tl)); // s4: splat drains into the flood (no ellipse outline under the sheets)
660
+ tag(S, ['dot'], 'dot', drain, 'impact splat draining into the flood');
661
+ if (drain > 0.02) ball(S, MX, FY - heroR * sv * drain, heroR * (1 + 0.3 * smooth(inv(0, 0.2, tl))) * drain, 0.4, sv);
662
+ untag(S);
663
+ }
664
+ }
665
+ // impact spray
666
+ spray.forEach((d) => {
667
+ if (t < d.t0 || t > 5.0) return;
668
+ const tt = t - d.t0;
669
+ const x = d.x0 + d.vx * tt, y = d.y0 - d.vy * tt + 0.5 * G * tt * tt;
670
+ const vy = -d.vy + G * tt;
671
+ const r = d.r * smooth(inv(0, 0.05, tt));
672
+ ball(S, x, y, r, d.u, 1 + Math.min(0.5, Math.hypot(d.vx, vy) / 5000), Math.atan2(vy, d.vx));
673
+ });
674
+
675
+ // ================= FLOOD =================
676
+ S.flood = { on: false, front: 0, back: 0, phase: 0, amp: 0, freq: 0, lean: 0, u: 0 };
677
+ const shV = sheetV(t);
678
+ S.overlays = [shV, sheetO(t), sheetP(t)];
679
+ // droplets leading each front
680
+ lead.forEach((d) => {
681
+ const sh = [sheetV, sheetO, sheetP][d.delay === 0 ? 0 : d.delay < 0.15 ? 1 : 2](t);
682
+ if (!sh.on) return;
683
+ const t0 = tImpact - 0.03 + d.delay;
684
+ const tt = t - t0;
685
+ if (tt < 0.05 || tt > 0.6) return;
686
+ const b = lerp(sh.bLo, sh.bHi, d.bf);
687
+ const ahead = d.ahead * Math.sin(PI * inv(0.05, 0.6, tt));
688
+ const a = sh.front + ahead + 60;
689
+ const x = a * sh.dx - b * sh.dy, y = a * sh.dy + b * sh.dx;
690
+ ball(S, x, y, d.r * Math.sin(PI * inv(0.05, 0.6, tt)), d.u, 1.35, Math.atan2(sh.dy, sh.dx));
691
+ });
692
+
693
+ // ================= LOGO BLOBS (left behind by the violet sheet) =================
694
+ const tRevealV = cues.reveal_start; // violet back edge starts rising
695
+ if (t > 4.12 && t < tSnap) {
696
+ const fk = inOut(inv(tSnap - 0.3, tSnap - 1 / 30, t)); // fuse into the bar
697
+ const squat = smooth(inv(tSnap - 0.2, tSnap - 1 / 30, t));
698
+ clusters.forEach((c) => {
699
+ const tFree = c.tFree;
700
+ const gk = flung(inv(tFree - 0.05, tSnap - 0.2, t)); // gather
701
+ const squeeze = smooth(inv(tSnap - 0.22, tSnap - 0.02, t));
702
+ const vanish = smooth(inv(tSnap - 0.01, tSnap + 0.11, t));
703
+ c.parts.forEach((p, j) => {
704
+ const lagk = flung(inv(tFree - 0.05 + j * 0.08, tSnap - 0.2 + j * 0.03, t)); // secondary blobs 2-3 frames late
705
+ const k = j === 0 ? gk : lagk;
706
+ let x = c.cx + lerp(p.sx + p.dx, p.dx * 0.25, k);
707
+ let y = c.cy + lerp(p.sy + p.dy, p.dy * 0.25, k);
708
+ // tug upward by the receding sheet, then rebound
709
+ const tug = t < tFree ? 1 : 0;
710
+ const reb = t >= tFree ? wob(t - tFree, 3.2, 0.22) : 0;
711
+ y += -30 * tug * inv(tFree - 0.3, tFree, t) - 22 * reb;
712
+ let r = p.r * (1 - 0.14 * squeeze * 0) * (1 - vanish * 0);
713
+ // round 1: the seeds are the subject of the immersion — they condense inside the violet sheet once its front
714
+ // has passed, and drift up toward their letter slots, seen backlit through the sheets above
715
+ if (t < tFree) {
716
+ const shv = sheetV(t);
717
+ r *= shv.on ? smooth(inv(0, 220, shv.front - aOf(c.cx, c.cy))) : 0;
718
+ const pre = 1 - smooth(inv(4.12, tFree, t));
719
+ x += (p.sx * 1.3 + (c.i - 1.5) * 70) * pre; y += 190 * pre;
720
+ }
721
+ let sv = 1 + 0.35 * tug * inv(tFree - 0.3, tFree, t) - 0.18 * reb - 0.16 * squeeze;
722
+ if (fk > 0) {
723
+ const hx0 = r / sv, vy0 = r * sv;
724
+ const tx = slotX[c.i] + (j ? p.dx * 0.1 : 0), ty = massY;
725
+ x = lerp(x, tx, fk); y = lerp(y, ty, fk);
726
+ let hx = j ? hx0 * (1 - fk) : lerp(hx0, MASS.hx * (1 + 0.06 * squat), fk), vy = j ? vy0 * (1 - fk) : lerp(vy0, MASS.vy * (1 - 0.15 * squat), fk);
727
+ const pn = inOut(inv(tSnap - 3.5 / 30, tSnap - 0.6 / 30, t)); // r2: pinch the necks
728
+ if (!j && pn > 0) { hx = lerp(hx, hxP[c.i], pn); vy = lerp(vy, vyP, pn); }
729
+ if (hx < 0.5) return;
730
+ r = Math.sqrt(hx * vy); sv = Math.sqrt(vy / hx);
731
+ }
732
+ if (V5 && j === 0) {
733
+ const lk = [];
734
+ if (c.i === 3 && fk < 0.5) lk.push('dot');
735
+ if (fk > 0) lk.push('bar');
736
+ if (lk.length) tag(S, lk, c.i === 3 && fk < 0.5 ? 'dot' : null,
737
+ lerp(0.4, 1, smooth(inv(tFree - 0.15, tFree + 0.05, t))) * (1 - smooth(inv(0.1, 0.5, fk))), 'seed blob of 动 (freed by the violet sheet)');
738
+ if (fk > 0) S.lnk.bar = { a: smooth(inv(0.35, 0.8, fk)), note: 'fused liquid bar that splits into the title' };
739
+ }
740
+ if (V5 && j > 0) { // the seed's satellite lobes go with it (and with the bar while fusing)
741
+ const lk = [];
742
+ if (c.i === 3 && fk < 0.5) lk.push('dot');
743
+ if (fk > 0) lk.push('bar~'); // '~' = hidden with the bar, not part of its reported geometry
744
+ if (lk.length) tag(S, lk);
745
+ }
746
+ ball(S, x, y, r, c.u, sv);
747
+ if (S.lnk && j === 0 && !(c.i === 3 && fk < 0.5)) untag(S);
748
+ if (S.lnk && j === 0 && c.i === 3 && fk < 0.5) S._main = null;
749
+ if (j === 0 && t < tFree + 0.02 && t > tFree - 0.35) {
750
+ // neck to the receding sheet above
751
+ const sh = sheetV(t);
752
+ if (sh.on) {
753
+ const k2 = inv(tFree - 0.35, tFree, t);
754
+ const topY = y - r * sv * 0.8;
755
+ const edgeY = topY - lerp(20, 130, k2);
756
+ const rm = lerp(18, 0.6, Math.pow(k2, 0.8));
757
+ seg(S, x, topY + 6, x + 4, (topY + edgeY) / 2, lerp(26, 12, k2), rm, c.u);
758
+ seg(S, x + 4, (topY + edgeY) / 2, x + 6, edgeY - 40, rm, lerp(30, 16, k2), 0.85);
759
+ }
760
+ }
761
+ untag(S);
762
+ });
763
+ });
764
+ }
765
+
766
+ // ================= LETTERS =================
767
+ // letterless until the drop. On frame 192 the bar splits: each slot's ellipse morphs into its letter SDF over 3 frames
768
+ // (1-frame stagger L->R) while the letters are flung out to their places on a spring (overshoot 112% w / 92% h).
769
+ if (t >= tSnap) {
770
+ const ts = t - tSnap;
771
+ const pos = [];
772
+ L.forEach((l, i) => {
773
+ // r2 P1 "split, don't grow": the word unfolds about (960, massY) from the bar (glyph scale = spacing scale, so glyphs
774
+ // never overlap); each letter is born from its bead in 2 f (0.6 f stagger) -> frame 194 reads 'drop'
775
+ const tl = ts; // one shared unfold (a stagger would let o run into p)
776
+ const m = clamp((ts * 30 - 0.6 * i + 1.5) / 2);
777
+ const P = tl > 0 ? spring(tl, 4, 0.35) : 0;
778
+ const x = 960 + (l.sx0 - 960) * lerp(SLOT, 1, P);
779
+ const gs = lerp(SLOT, 1, P <= 1 ? P : 1 + 0.25 * (P - 1));
780
+ const sq = m * smooth(clamp((P - 0.95) / 0.36)); // 112 % w / 92 % h about the letter's own centre, once apart
781
+ // r2 s2 P3: the jelly word answers the groove: a wobble wave on each beat of the outro, L->R (R->L from the p when
782
+ // the drip lands at 8.40), decaying toward the final hold -> the end card has events, not just slow drift
783
+ let jelly = 0;
784
+ for (const [tk, ak, dk] of JELLY_HITS) jelly += ak * wob(t - tk - (dk > 0 ? i : 3 - i) * 0.04, 3.4, 0.22);
785
+ const breathe = 0.025 * Math.sin(2 * PI * 0.7 * (t - 6.9) - i * PI / 2) * smooth(inv(6.9, 7.6, t)); // r2 P3: +-2.5 %
786
+ const sxJ = 1 + 0.12 * sq + jelly + breathe, syJ = 1 - 0.08 * sq + breathe - jelly * 0.6;
787
+ let dy = tl > 0 ? -18 * (1 - spring(tl, 3.4, 0.45)) * m : 0;
788
+ if (V5 && i === 1) dy += t < fat.tSnap ? 5 * smooth(inv(7.5, fat.tSnap, t)) : 5 * (1 - spring(t - fat.tSnap, 3.2, 0.28)); // v5.1
789
+ const gooWob = 3.2 * Math.exp(-Math.max(0, tl) / 0.35) + 0.9 + 0.5 * smooth(inv(6.9, 7.5, t)); // r2 s2: livelier skin
790
+ const yc = massY + (l.sy0 - massY) * gs + dy;
791
+ const y = yc + (1 - syJ) * (l.bbox[3] - l.sy0) * 0.9 * gs;
792
+ pos.push({ x, y: yc, gs });
793
+ S.letters.push({
794
+ x, y, px: l.px, py: l.py, grow: 3 * (1 - smooth(inv(0, 0.15, Math.max(0, tl)))), u: letterU[i], sx: gs * sxJ, sy: gs * syJ,
795
+ a: tl > 0 ? 0.012 * wob(tl, 2.4, 0.4) * (i % 2 ? 1 : -1) : 0, on: true, m: Math.max(1e-3, m), wob: gooWob, wph: t * 7.0 + i * 1.7,
796
+ ell: [x, massY + dy, hxP[i] * (1 + 0.4 * m), vyP * (1 + 0.4 * m)],
797
+ });
798
+ });
799
+ // r2: the three necks thin and snap L->R one frame apart (between frames 192/193, 193/194, 194/195); stubs recoil
800
+ bridges.forEach((br) => {
801
+ const Pa = pos[br.i], Pb = pos[br.j], la = L[br.i], lb = L[br.j];
802
+ const A = [Pa.x + (br.xa - la.sx0) * Pa.gs, Pa.y + (br.y - la.sy0) * Pa.gs];
803
+ const B = [Pb.x + (br.xb - lb.sx0) * Pb.gs, Pb.y + (br.y - lb.sy0) * Pb.gs];
804
+ const tBr = tSnap + (br.i + 0.5) / 30;
805
+ if (t < tBr) {
806
+ const k = inv(tSnap, tBr, t);
807
+ const rm = lerp(br.r * 0.85, 1.0, Math.pow(k, 0.6));
808
+ const M = [(A[0] + B[0]) / 2, (A[1] + B[1]) / 2 + 6 * k];
809
+ const ra = br.r * 1.1 * Pa.gs;
810
+ seg(S, A[0], A[1], M[0], M[1], ra, rm, letterU[br.i]); seg(S, M[0], M[1], B[0], B[1], rm, ra, letterU[br.j]);
811
+ } else if (t < tBr + 0.3) {
812
+ const tt = t - tBr, k = spring(tt, 7, 0.35);
813
+ const half = [(B[0] - A[0]) / 2, (B[1] - A[1]) / 2];
814
+ const ra = br.r * (1 - 0.5 * smooth(inv(0, 0.25, tt)));
815
+ seg(S, A[0], A[1], A[0] + half[0] * (1 - k) * 0.8, A[1] + half[1] * (1 - k) + 6, ra, 2.5 + 3 * (1 - k), letterU[br.i]);
816
+ seg(S, B[0], B[1], B[0] - half[0] * (1 - k) * 0.8, B[1] - half[1] * (1 - k) + 6, ra, 2.5 + 3 * (1 - k), letterU[br.j]);
817
+ }
818
+ });
819
+ snapDrops.forEach((d) => emitDroplet(S, d, t));
820
+ }
821
+ // hanging drips (grow slowly, never fall) — living hold
822
+ dripSpots.forEach((d) => {
823
+ if (t < d.t0) return;
824
+ const k = outSoft(inv(d.t0, 10.2, t));
825
+ const len = d.len * k, r = d.r * (0.4 + 0.6 * smooth(inv(d.t0, d.t0 + 0.6, t)));
826
+ const sway = 1.5 * Math.sin(t * 3 + d.li);
827
+ if (len + r < 3) return;
828
+ seg(S, d.x, d.y - 8, d.x + sway * 0.5, d.y + len, r * 1.25, r * 0.55, letterU[d.li]);
829
+ ball(S, d.x + sway, d.y + len + r * 0.35, r * (0.6 + 0.4 * k), letterU[d.li], 1.12);
830
+ });
831
+ // v5.1: fat drip under 态 (the dot's OUT): swell 7.2-8.27, neck thins and snaps 8.27, free fall out of the frame
832
+ if (V5 && t > fat.tStart) {
833
+ const u = fat.u;
834
+ if (t < fat.tSnap) {
835
+ const k = inv(fat.tStart, fat.tSnap, t), sag = easeIn(k);
836
+ const br = fat.rMax * (0.25 + 0.75 * smooth(inv(0, 0.85, k)));
837
+ const sv = 1 + 0.12 * sag;
838
+ const by = lerp(fat.y + br * 0.95, fat.ySnap, sag); // beads up under the stroke, then sags on a lengthening neck
839
+ const top = by - br * sv * 0.8, A = fat.y - 12, M = (A + top) / 2;
840
+ const rn = lerp(26, 2.5, Math.pow(inv(0.55, 1, k), 1.4));
841
+ tag(S, ['dot']);
842
+ if (top > A + 4) { seg(S, fat.x, A, fat.x, M, 30, rn, u); seg(S, fat.x, M, fat.x, top, rn, br * 0.6, u); }
843
+ tag(S, ['dot'], 'dot', smooth(inv(0, 0.2, k)), 'fat drip swelling under 态 (OUT: snaps 8.27, falls out of frame)');
844
+ ball(S, fat.x, by, br, u, sv);
845
+ untag(S);
846
+ } else {
847
+ const tt = t - fat.tSnap, y = fat.at(tt), v = fat.vSnap + G * tt;
848
+ if (y - fat.rMax * 1.3 < 1250) {
849
+ const sv = 1 + 0.12 * Math.exp(-tt / 0.12) * Math.cos(2 * PI * 6 * tt) + 0.06 * clamp(v / 2500); // teardrop -> round, jiggles
850
+ tag(S, ['dot'], 'dot', 1, 'fat drip falling from 态 (out through the bottom edge)');
851
+ ball(S, fat.x, y, fat.rMax, u, sv);
852
+ tag(S, ['dot']);
853
+ const len = 46 * (1 - spring(tt, 7, 0.45)); // the snapped tail recoils into the drop
854
+ if (len > 1) seg(S, fat.x, y - fat.rMax * sv - len, fat.x, y - fat.rMax * 0.6, 3, 16, u);
855
+ untag(S);
856
+ }
857
+ if (tt < 0.6) { // stub on 态 recoils (rebound)
858
+ const k = spring(tt, 6, 0.33), len = 44 * (1 - k) + 3;
859
+ seg(S, fat.x, fat.y - 10, fat.x, fat.y + len, 24, 5 + 4 * (1 - k), u);
860
+ }
861
+ if (t > fat.tFloor) S.tagRev = { x: fat.x, y: FY, age: t - fat.tFloor, amp: 1 }; // tagline wipes in in its wake
862
+ }
863
+ }
864
+ // hero drip from the p descender
865
+ if (!V5 && t > hero.tStart) {
866
+ const u = letterU[3];
867
+ if (t < hero.tSnap) {
868
+ const k = inv(hero.tStart, hero.tSnap, t);
869
+ const kk = easeIn(k);
870
+ const by = lerp(hero.y + 8, hero.ySnap, kk);
871
+ const br = hero.rMax * (0.35 + 0.65 * smooth(inv(0, 0.55, k)));
872
+ const neckY = lerp(hero.y + 6, by - br * 0.9, 0.5);
873
+ const rn = lerp(16, 2.2, Math.pow(inv(0.35, 1, k), 1.2));
874
+ seg(S, hero.x, hero.y - 10, hero.x, neckY, 22, rn, u);
875
+ tag(S, ['dot']); seg(S, hero.x, neckY, hero.x, by - br * 0.5, rn, br * 0.7, u);
876
+ tag(S, ['dot'], 'dot', smooth(inv(0, 0.25, k)), 'drip bulb swelling under 动');
877
+ ball(S, hero.x, by, br, u, 1 + 0.18 * kk);
878
+ untag(S);
879
+ } else if (t < hero.tLand) {
880
+ const tt = t - hero.tSnap;
881
+ const y = hero.ySnap + hero.vSnap * tt + 0.5 * G * tt * tt;
882
+ const v = hero.vSnap + G * tt;
883
+ const sv = 1.12 + 0.2 * clamp(v / 1400);
884
+ tag(S, ['dot'], 'dot', 1, 'drip falling from 动');
885
+ ball(S, hero.x, y, hero.rMax, u, sv);
886
+ tag(S, ['dot']);
887
+ // tail recoils into the falling drop
888
+ const k = spring(tt, 8, 0.45);
889
+ const len = 30 * (1 - k);
890
+ if (len > 1) seg(S, hero.x, y - hero.rMax * sv - len, hero.x, y - hero.rMax * 0.6, 2, 8, u);
891
+ untag(S);
892
+ } else {
893
+ const tl = t - hero.tLand;
894
+ const sv = 1.3 + (0.42 - 1.3) * spring(tl, 5.5, 0.4);
895
+ const r = hero.rMax * (1 + 0.1 * smooth(inv(0, 0.3, tl)));
896
+ tag(S, ['dot'], 'dot', 1, 'drip landed: glossy puddle lens + ripple');
897
+ ball(S, hero.x, FY - r * sv + 0.5, r, u, sv);
898
+ untag(S);
899
+ S.rip = { x: hero.x, y: FY, age: tl, amp: 1 };
900
+ if (V5) S.ripDot = true;
901
+ }
902
+ // stub on the descender recoils after the snap (rebound 2-3 frames)
903
+ if (t >= hero.tSnap && t < hero.tSnap + 0.6) {
904
+ const k = spring(t - hero.tSnap, 6, 0.33);
905
+ const len = 40 * (1 - k) + 3;
906
+ seg(S, hero.x, hero.y - 10, hero.x, hero.y + len, 18, 4 + 3 * (1 - k), u);
907
+ }
908
+ }
909
+ if (!V5) splat.forEach((d) => emitDroplet(S, d, t));
910
+
911
+ // ---------------- light: glint sweep after the snap ----------------
912
+ const g2 = inv(9.0, 9.0 + 10 / 30, t);
913
+ const glint1 = t > cues.glint - 0.1 && t < cues.glint + 0.8;
914
+ const kd = smooth(inv(6.9, 10.0, t)); // r2 s2 P3: the key softbox travels during the end card -> speculars slide over the letters
915
+ S.key = { x: -0.78 + 0.06 * Math.sin(t * 0.6) + 0.30 * kd, y: -0.95 + 0.12 * kd,
916
+ glint: glint1 ? lerp(-900, 1700, inOut(inv(cues.glint - 0.1, cues.glint + 0.75, t))) : lerp(-300, 1900, inOut(g2)),
917
+ glintAmp: glint1 ? 1 : (g2 > 0 && g2 < 1 ? 2.2 * Math.sin(PI * g2) : 0) }; // r2 P3: bold 9.0 glint
918
+ { const f = (t - tSnap) * 30; if (f >= 0 && f < 2.5) { S.key.glint = lerp(-200, 1300, f / 2.5); S.key.glintAmp = 2.8 * (1 - 0.5 * f / 2.5); } } // r2: 2 f specular pop
919
+ const gA = inv(3.72, 3.72 + 6 / 30, t); // s3 P8: strip light rakes the falling hero sphere (6 f)
920
+ if (gA > 0 && gA < 1) { S.key.glint = 0.8 * MX - 0.6 * sphereY(t) + lerp(-300, 300, inOut(gA)); S.key.glintAmp = 2.4 * Math.sin(PI * gA); }
921
+ S.bloom = 0.55 + 0.45 * Math.exp(-Math.max(0, t - tSnap) / 0.25) * (t > tSnap ? 1 : 0);
922
+ const gap = smooth(inv(tSnap - 0.2, tSnap - 1 / 30, t)) * (t < tSnap ? 1 : 0);
923
+ S.spot = 1.0 - 0.55 * gap;
924
+ if (t >= tSnap) S.spot = 1.12 + 0.45 * Math.exp(-(t - tSnap) / 0.15); // r2: small lift; the ring carries the hit
925
+ { // r2 P1: radial light ring from behind the bar (0 -> 1400 px in 8 f, flung) + backlit crown sheet (gone in 10 f)
926
+ const tr = t - tSnap;
927
+ if (tr >= 0 && tr < 0.6) {
928
+ const ur = inv(0, 10 / 30, tr), kr = 1 - (1 - ur) * (1 - ur); // expo-ish out: energy spread over 4-5 f
929
+ S.ring = [960, massY + 10, 40 + 1360 * kr, 0.62 * (1 - 0.45 * kr) * (1 - smooth(inv(5 / 30, 12 / 30, tr)))];
930
+ const kc = flung(inv(0, 3 / 30, tr)), fc = smooth(inv(3 / 30, 9 / 30, tr));
931
+ if (tr < 9 / 30) S.crown = [960, massY - 30, 350 * kc * (1 - 0.6 * fc), (1 - fc) * smooth(inv(0, 1 / 30, tr))];
932
+ }
933
+ // r2 P3: living end card: cyc spot drifts +-80 px, backdrop parallax 8 px against the letters
934
+ const ek = 0.5 - 0.5 * Math.cos(PI * inv(6.6, 10.0, t));
935
+ S.caus = smooth(inv(6.55, 7.2, t)) * (1 + (!V5 && t > cues.drip_land ? 1.6 * Math.exp(-(t - cues.drip_land) / 0.45) : 0)); // r2 s2: the drip's splash flares the wall caustics
936
+ S.par = [8 * ek, -5 * ek, 80 * Math.sin(2 * PI * (t - 6.6) / 3.0) * smooth(inv(6.6, 7.4, t)), 18 * Math.sin(2 * PI * (t - 6.6) / 4.0) * smooth(inv(6.6, 7.4, t))];
937
+ }
938
+ for (const kt of KICKS) S.spot += 0.14 * Math.exp(-Math.max(0, t - kt) / 0.14) * (t >= kt ? 1 : 0);
939
+ // exposure (EV) applied in post
940
+ S.flash = -0.8 * gap; // s4: deeper held breath (-0.8 EV)
941
+ if (t >= tSnap) { const f = (t - tSnap) * 30; S.flash = f < 1 ? 0.9 : f < 2 ? 0.65 : 0.65 * (1 - smooth(inv(2, 8, f))); } // r2: +0.6 EV (ring does the rest)
942
+ S.bubbles = [];
943
+ bubbles.forEach((b) => {
944
+ if (t < b.t0) return;
945
+ const len = b.y0 - b.y1, per = len / b.sp + 0.25;
946
+ const ph = ((t - b.t0) / per + b.ph) % 1;
947
+ const k = ph * per * b.sp / len; // 0..1 along the rise (last 0.25 s = gap)
948
+ if (k > 1) return;
949
+ const l = S.letters[b.li]; if (!l) return;
950
+ const y = lerp(b.y0, b.y1, k) + (l.y - L[b.li].sy0), x = b.x + b.wob * Math.sin((t - b.t0) * b.wf + b.ph * 6);
951
+ const a = smooth(inv(0, 0.12, k)) * (1 - smooth(inv(0.88, 1, k))) * smooth(inv(b.t0, b.t0 + 0.3, t));
952
+ S.bubbles.push([x, y, b.r * (0.8 + 0.4 * k), a]);
953
+ });
954
+ // round 1: sharp micro-bubbles rising through the flood (drawn only where liquid covers the pixel)
955
+ floodBub.forEach((b) => {
956
+ const tt = t - b.t0; if (tt < 0 || tt > b.dur) return;
957
+ const y = b.y0 - b.sp * tt - 160 * tt * tt, x = b.x + b.wob * Math.sin(tt * b.wf + b.ph);
958
+ if (S.bubbles.length < 32) S.bubbles.push([x, y, b.r * (1 + 0.5 * tt), smooth(inv(0, 0.06, tt)) * (1 - smooth(inv(b.dur - 0.12, b.dur, tt)))]);
959
+ });
960
+ S.halo = t >= tSnap ? smooth(inv(tSnap, tSnap + 0.08, t)) * (0.55 + 0.45 * Math.exp(-(t - tSnap) / 0.3)) : 0;
961
+ return S;
962
+ };
963
+ }