dsh-drawai 0.4.2 → 0.4.4

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 (379) hide show
  1. package/README.md +7 -5
  2. package/lib/client.js +142 -16
  3. package/lib/index.js +7945 -7719
  4. package/lib/motion/asset-pack.js +394 -394
  5. package/lib/motion/assets.js +291 -291
  6. package/lib/motion/demo-assets-lock.js +10 -10
  7. package/lib/motion/mgstyles/briefs/23-ink-wash.md +147 -0
  8. package/lib/motion/mgstyles/catalog.d/23-ink-wash.json +28 -0
  9. package/lib/motion/mgstyles/catalog.json +127 -14
  10. package/lib/motion/mgstyles/upstream/demos/23-ink-wash/brush.js +149 -0
  11. package/lib/motion/mgstyles/upstream/demos/23-ink-wash/film.js +202 -0
  12. package/lib/motion/mgstyles/upstream/manifest.json +28 -3
  13. package/lib/motion/mgstyles/upstream/prompts/23-ink-wash.md +59 -0
  14. package/lib/motion/mgstyles/vendor/README.md +28 -28
  15. package/lib/motion/mgstyles/vendor/modules/d3/LICENSE +13 -13
  16. package/lib/motion/mgstyles/vendor/modules/d3/dist/d3.min.js +2 -2
  17. package/lib/motion/mgstyles/vendor/modules/opentype.js/LICENSE +20 -20
  18. package/lib/motion/mgstyles/vendor/modules/opentype.js/dist/opentype.mjs +14460 -14460
  19. package/lib/motion/mgstyles/vendor/modules/simplex-noise/LICENSE +21 -21
  20. package/lib/motion/mgstyles/vendor/modules/simplex-noise/dist/esm/simplex-noise.js +465 -465
  21. package/lib/motion/mgstyles/vendor/modules/topojson-client/LICENSE +13 -13
  22. package/lib/motion/mgstyles/vendor/modules/topojson-client/dist/topojson-client.min.js +2 -2
  23. package/lib/motion/project.js +5 -1
  24. package/lib/motion/qa.js +116 -6
  25. package/lib/motion/render.js +179 -179
  26. package/lib/motion/scene-kit.js +1 -1
  27. package/lib/motion/shotcraft/upstream/SKILL.md +286 -286
  28. package/lib/motion/shotcraft/upstream/assets/lib/Caption.tsx +54 -54
  29. package/lib/motion/shotcraft/upstream/assets/lib/ClipCard.tsx +200 -200
  30. package/lib/motion/shotcraft/upstream/assets/lib/DigitRoll.tsx +45 -45
  31. package/lib/motion/shotcraft/upstream/assets/lib/FlashCut.tsx +20 -20
  32. package/lib/motion/shotcraft/upstream/assets/lib/FlatPanel.tsx +65 -65
  33. package/lib/motion/shotcraft/upstream/assets/lib/PageCam.tsx +151 -151
  34. package/lib/motion/shotcraft/upstream/assets/lib/VerticalTicker.tsx +129 -129
  35. package/lib/motion/shotcraft/upstream/assets/lib/helpers/camera.tsx +75 -75
  36. package/lib/motion/shotcraft/upstream/assets/lib/helpers/motion.ts +40 -40
  37. package/lib/motion/shotcraft/upstream/assets/lib/helpers/rand.ts +11 -11
  38. package/lib/motion/shotcraft/upstream/assets/lib/helpers/shake.ts +10 -10
  39. package/lib/motion/shotcraft/upstream/assets/scripts/build-readme-cover.mjs +49 -49
  40. package/lib/motion/shotcraft/upstream/assets/scripts/capture-template.mjs +210 -210
  41. package/lib/motion/shotcraft/upstream/assets/scripts/sfx-source.py +423 -423
  42. package/lib/motion/shotcraft/upstream/assets/scripts/smoke-render-demos.py +170 -170
  43. package/lib/motion/shotcraft/upstream/assets/scripts/test_sfx_source.py +157 -157
  44. package/lib/motion/shotcraft/upstream/demos/README.md +120 -120
  45. package/lib/motion/shotcraft/upstream/demos/_fixtures/Brand.tsx +112 -112
  46. package/lib/motion/shotcraft/upstream/demos/_fixtures/Fixtures.tsx +647 -647
  47. package/lib/motion/shotcraft/upstream/demos/_fixtures/Look.tsx +316 -316
  48. package/lib/motion/shotcraft/upstream/demos/_fixtures/Motion.tsx +82 -82
  49. package/lib/motion/shotcraft/upstream/demos/_fixtures/PageCam2D.tsx +115 -115
  50. package/lib/motion/shotcraft/upstream/demos/_fixtures/Polish.tsx +243 -243
  51. package/lib/motion/shotcraft/upstream/demos/_textures/live-layout.json +388 -388
  52. package/lib/motion/shotcraft/upstream/demos/camera/basic-3d-scene/Basic3DScene.tsx +250 -250
  53. package/lib/motion/shotcraft/upstream/demos/camera/crash-zoom-punch/CrashImpactReal.tsx +305 -305
  54. package/lib/motion/shotcraft/upstream/demos/camera/crash-zoom-punch/CrashZoomReal.tsx +246 -246
  55. package/lib/motion/shotcraft/upstream/demos/camera/cursor-flyover/CursorFlyover.tsx +390 -390
  56. package/lib/motion/shotcraft/upstream/demos/camera/depth-layer-moves/MultiplaneReal.tsx +138 -138
  57. package/lib/motion/shotcraft/upstream/demos/camera/graze-face-tour/GrazeFaceTour.tsx +542 -542
  58. package/lib/motion/shotcraft/upstream/demos/camera/overhead-camera-moves/OverheadTabletopDrop.tsx +315 -315
  59. package/lib/motion/shotcraft/upstream/demos/camera/overhead-camera-moves/TiltReveal.tsx +299 -299
  60. package/lib/motion/shotcraft/upstream/demos/camera/space-camera-moves/DroneDiveLanding.tsx +173 -173
  61. package/lib/motion/shotcraft/upstream/demos/camera/space-camera-moves/ExplodedView.tsx +348 -348
  62. package/lib/motion/shotcraft/upstream/demos/camera/steep-tilt-glide/SteepTiltGlide.tsx +366 -366
  63. package/lib/motion/shotcraft/upstream/demos/camera/tension-camera-moves/BulletTimeFreezeOrbit.tsx +279 -279
  64. package/lib/motion/shotcraft/upstream/demos/camera/tension-camera-moves/PullBackIsolation.tsx +184 -184
  65. package/lib/motion/shotcraft/upstream/demos/camera/terminal-3d/Terminal3D.tsx +390 -390
  66. package/lib/motion/shotcraft/upstream/demos/data/before-after-slider-scrub/BeforeAfterSliderScrub.tsx +155 -155
  67. package/lib/motion/shotcraft/upstream/demos/data/chart-live-moves/AxisRescaleShockV2.tsx +303 -303
  68. package/lib/motion/shotcraft/upstream/demos/data/chart-live-moves/OscilloscopeStreamV2.tsx +290 -290
  69. package/lib/motion/shotcraft/upstream/demos/data/chart-live-moves/UnitDotSwarmRegroupV2.tsx +278 -278
  70. package/lib/motion/shotcraft/upstream/demos/data/gauge-readout-moves/NeedleSweepSelftest.tsx +307 -307
  71. package/lib/motion/shotcraft/upstream/demos/data/gauge-readout-moves/TapeScrollFixedPointer.tsx +215 -215
  72. package/lib/motion/shotcraft/upstream/demos/data/scroll-brake-moves/BrakeReticleLock.tsx +299 -299
  73. package/lib/motion/shotcraft/upstream/demos/data/scroll-brake-moves/ChangelogScrollBrake.tsx +232 -232
  74. package/lib/motion/shotcraft/upstream/demos/data/timeline-travel/TimelineTravel.tsx +290 -290
  75. package/lib/motion/shotcraft/upstream/demos/effects/assemble-then-type-flyin/AssembleThenTypeFlyin.tsx +273 -273
  76. package/lib/motion/shotcraft/upstream/demos/effects/aurora-bloom-bg-flip/AuroraBloomBgFlip.tsx +167 -167
  77. package/lib/motion/shotcraft/upstream/demos/effects/brand-frame-snap/BrandFrameSnap.tsx +536 -536
  78. package/lib/motion/shotcraft/upstream/demos/effects/fui-hud-moves/ReticleLockOn.tsx +164 -164
  79. package/lib/motion/shotcraft/upstream/demos/effects/glow-flyline-moves/FlylineArc.tsx +175 -175
  80. package/lib/motion/shotcraft/upstream/demos/effects/glow-flyline-moves/GlowOrbAmbient.tsx +197 -197
  81. package/lib/motion/shotcraft/upstream/demos/effects/glow-flyline-moves/OrbFlylineRelay.tsx +241 -241
  82. package/lib/motion/shotcraft/upstream/demos/effects/icon-performance-moves/AttentionBounce.tsx +279 -279
  83. package/lib/motion/shotcraft/upstream/demos/effects/impact-feedback/AnimeImpact.tsx +230 -230
  84. package/lib/motion/shotcraft/upstream/demos/effects/impact-feedback/HitCounter.tsx +345 -345
  85. package/lib/motion/shotcraft/upstream/demos/effects/light-play-moves/SheenSweepRetry.tsx +275 -275
  86. package/lib/motion/shotcraft/upstream/demos/effects/light-play-moves/SpotlightSweepReveal.tsx +229 -229
  87. package/lib/motion/shotcraft/upstream/demos/effects/radial-ripple-phone-chips/RadialRipplePhoneChips.tsx +312 -312
  88. package/lib/motion/shotcraft/upstream/demos/effects/riso-print-hits/RisoMisregistrationHit.tsx +222 -222
  89. package/lib/motion/shotcraft/upstream/demos/effects/scan-bracket-sweep/ScanBracketSweep.tsx +295 -295
  90. package/lib/motion/shotcraft/upstream/demos/effects/scanline-annotate-focus/ScanlineAnnotateFocus.tsx +329 -329
  91. package/lib/motion/shotcraft/upstream/demos/effects/scanline-assemble-flyin/ScanlineAssembleFlyin.tsx +315 -315
  92. package/lib/motion/shotcraft/upstream/demos/effects/slam-entrance-moves/ImpactBurstKit.tsx +264 -264
  93. package/lib/motion/shotcraft/upstream/demos/effects/slam-entrance-moves/KanadaPerspectiveSnap.tsx +260 -260
  94. package/lib/motion/shotcraft/upstream/demos/effects/slam-entrance-moves/ScoreSlam.tsx +263 -263
  95. package/lib/motion/shotcraft/upstream/demos/effects/spotlight-sweep-moves/CornerSpotlightReveal.tsx +123 -123
  96. package/lib/motion/shotcraft/upstream/demos/effects/spotlight-sweep-moves/GlowWakeSleepPanel.tsx +187 -187
  97. package/lib/motion/shotcraft/upstream/demos/effects/spotlight-sweep-moves/SlideSpotlightPan.tsx +175 -175
  98. package/lib/motion/shotcraft/upstream/demos/interaction/autolayout-gap-dial/AutolayoutGapDial.tsx +305 -305
  99. package/lib/motion/shotcraft/upstream/demos/interaction/canvas-materialize-moves/DiagramCascadeBuild.tsx +350 -350
  100. package/lib/motion/shotcraft/upstream/demos/interaction/canvas-materialize-moves/PanelToCanvasMaterialize.tsx +352 -352
  101. package/lib/motion/shotcraft/upstream/demos/interaction/chip-grid-single-select-blackout/ChipGridSingleSelectBlackout.tsx +218 -218
  102. package/lib/motion/shotcraft/upstream/demos/interaction/collab-cursor-moves/CursorCastEnsemble.tsx +297 -297
  103. package/lib/motion/shotcraft/upstream/demos/interaction/glass-pill-dictation-typing/GlassPillDictationTyping.tsx +157 -157
  104. package/lib/motion/shotcraft/upstream/demos/interaction/hashtag-to-pill-materialize/HashtagToPillMaterialize.tsx +218 -218
  105. package/lib/motion/shotcraft/upstream/demos/interaction/picker-carousel-feature-cycle/PickerCarouselFeatureCycle.tsx +202 -202
  106. package/lib/motion/shotcraft/upstream/demos/interaction/segmented-thumb-hero/SegmentedThumbHero.tsx +265 -265
  107. package/lib/motion/shotcraft/upstream/demos/interaction/theme-switch-moves/PaletteThemeRipple.tsx +457 -457
  108. package/lib/motion/shotcraft/upstream/demos/interaction/theme-switch-moves/ThemeSweepToggle.tsx +446 -446
  109. package/lib/motion/shotcraft/upstream/demos/interaction/type-and-filter/TypeAndFilter.tsx +249 -249
  110. package/lib/motion/shotcraft/upstream/demos/interaction/voice-waveform-live/VoiceWaveformLive.tsx +278 -278
  111. package/lib/motion/shotcraft/upstream/demos/opening/crane-rise-reveal/CraneRiseReveal.tsx +332 -332
  112. package/lib/motion/shotcraft/upstream/demos/opening/dataviz-landscape-open/DatavizLandscapeOpen.tsx +358 -358
  113. package/lib/motion/shotcraft/upstream/demos/opening/fracture/Fracture.tsx +201 -201
  114. package/lib/motion/shotcraft/upstream/demos/opening/icon-field-colorize/IconFieldColorize.tsx +200 -200
  115. package/lib/motion/shotcraft/upstream/demos/opening/letterspace-materialize/LetterspaceMaterialize.tsx +208 -208
  116. package/lib/motion/shotcraft/upstream/demos/opening/magician-card-flourish/MagicianCardFlourish.tsx +341 -341
  117. package/lib/motion/shotcraft/upstream/demos/opening/orbit-ring-title-open/OrbitRingTitleOpen.tsx +389 -389
  118. package/lib/motion/shotcraft/upstream/demos/opening/spotlight-hero-card/SpotlightHeroCard.tsx +293 -293
  119. package/lib/motion/shotcraft/upstream/demos/opening/stroke-segment-build/StrokeSegmentBuild.tsx +183 -183
  120. package/lib/motion/shotcraft/upstream/demos/opening/text-as-mask/TextAsMask.tsx +245 -245
  121. package/lib/motion/shotcraft/upstream/demos/outro/grain-dissolve/GrainDissolve.tsx +222 -222
  122. package/lib/motion/shotcraft/upstream/demos/outro/logo-shrink-wordmark-lockup/LogoShrinkWordmarkLockup.tsx +223 -223
  123. package/lib/motion/shotcraft/upstream/demos/outro/neon-triple-marquee/NeonTripleMarquee.tsx +217 -217
  124. package/lib/motion/shotcraft/upstream/demos/outro/outro-group-photo-launch/OutroGroupPhotoLaunch.tsx +343 -343
  125. package/lib/motion/shotcraft/upstream/demos/outro/ui-strip-away-outro/UiStripAwayOutro.tsx +357 -357
  126. package/lib/motion/shotcraft/upstream/demos/outro/ui-to-brand-morph/IconFlipBloomLogo.tsx +187 -187
  127. package/lib/motion/shotcraft/upstream/demos/outro/ui-to-brand-morph/InputMorphsIntoLogo.tsx +333 -333
  128. package/lib/motion/shotcraft/upstream/demos/rhythm/beat-cut-moves/PaparazziFlash.tsx +274 -274
  129. package/lib/motion/shotcraft/upstream/demos/rhythm/beat-step-list-theme-cycle/BeatStepListThemeCycle.tsx +270 -270
  130. package/lib/motion/shotcraft/upstream/demos/rhythm/panel-grid-moves/ComicPanelSplit.tsx +430 -430
  131. package/lib/motion/shotcraft/upstream/demos/rhythm/quad-split-parallel-scenes/QuadSplitParallelScenes.tsx +252 -252
  132. package/lib/motion/shotcraft/upstream/demos/rhythm/rhythm-interrupt-moves/JumpCutPunchIn.tsx +106 -106
  133. package/lib/motion/shotcraft/upstream/demos/rhythm/sakuga-timing-shift/SakugaTimingShift.tsx +288 -288
  134. package/lib/motion/shotcraft/upstream/demos/rhythm/speed-ramp-freeze/FreezeAnnotateReal.tsx +362 -362
  135. package/lib/motion/shotcraft/upstream/demos/rhythm/trailer-grammar-moves/TrailerBumper.tsx +168 -168
  136. package/lib/motion/shotcraft/upstream/demos/transition/bottom-push-stack-wipe/BottomPushStackWipe.tsx +310 -310
  137. package/lib/motion/shotcraft/upstream/demos/transition/card-flip-reveal/CardFlipReveal.tsx +261 -261
  138. package/lib/motion/shotcraft/upstream/demos/transition/card-flock-tumble/CardFlockTumble.tsx +459 -459
  139. package/lib/motion/shotcraft/upstream/demos/transition/circle-match-iris/CircleMatchIris.tsx +294 -294
  140. package/lib/motion/shotcraft/upstream/demos/transition/gradient-transition/GradientTransition.tsx +161 -161
  141. package/lib/motion/shotcraft/upstream/demos/transition/line-carry-transition/LineCarryTransition.tsx +248 -248
  142. package/lib/motion/shotcraft/upstream/demos/transition/mosaic-reframe/MosaicReframe.tsx +359 -359
  143. package/lib/motion/shotcraft/upstream/demos/transition/page-turn-transitions/BarnDoorSplit.tsx +227 -227
  144. package/lib/motion/shotcraft/upstream/demos/transition/print-texture-transitions/InkBleedReveal.tsx +162 -162
  145. package/lib/motion/shotcraft/upstream/demos/transition/shot-transitions/PortalWipeV2.tsx +248 -248
  146. package/lib/motion/shotcraft/upstream/demos/transition/shot-transitions/WhipBrakeReal.tsx +186 -186
  147. package/lib/motion/shotcraft/upstream/demos/transition/tear-streak-transitions/GlitchDisplace.tsx +355 -355
  148. package/lib/motion/shotcraft/upstream/demos/transition/transition-hidden-cut/InvisibleCut.tsx +272 -272
  149. package/lib/motion/shotcraft/upstream/demos/transition/transition-hidden-cut/LightLeakBurn.tsx +262 -262
  150. package/lib/motion/shotcraft/upstream/demos/transition/transition-hidden-cut/VersusSlam.tsx +217 -217
  151. package/lib/motion/shotcraft/upstream/demos/transition/transition-travel/LetterformZoom.tsx +285 -285
  152. package/lib/motion/shotcraft/upstream/demos/transition/white-flash-logo-simplify-cut/WhiteFlashLogoSimplifyCut.tsx +205 -205
  153. package/lib/motion/shotcraft/upstream/demos/transition/wipe-transitions/BlindsSlice.tsx +178 -178
  154. package/lib/motion/shotcraft/upstream/demos/typography/blur-slide/BlurSlide.tsx +147 -147
  155. package/lib/motion/shotcraft/upstream/demos/typography/brace-expand/BraceExpand.tsx +143 -143
  156. package/lib/motion/shotcraft/upstream/demos/typography/brand-ink-open/BrandInkOpen.tsx +134 -134
  157. package/lib/motion/shotcraft/upstream/demos/typography/countdown-arc-scatter/CountdownArcScatter.tsx +217 -217
  158. package/lib/motion/shotcraft/upstream/demos/typography/flying-words/FlyingWords.tsx +198 -198
  159. package/lib/motion/shotcraft/upstream/demos/typography/glitch-cycle/GlitchCycle.tsx +221 -221
  160. package/lib/motion/shotcraft/upstream/demos/typography/gradient-word-sweep/GradientWordSweep.tsx +312 -312
  161. package/lib/motion/shotcraft/upstream/demos/typography/lead-word-zoom-assemble/LeadWordZoomAssemble.tsx +261 -261
  162. package/lib/motion/shotcraft/upstream/demos/typography/marker-underline-title/MarkerUnderlineTitle.tsx +121 -121
  163. package/lib/motion/shotcraft/upstream/demos/typography/outline-word-fill/OutlineWordFill.tsx +153 -153
  164. package/lib/motion/shotcraft/upstream/demos/typography/paper-title-card/PaperTitleCard.tsx +179 -179
  165. package/lib/motion/shotcraft/upstream/demos/typography/pill-chip-slot-cycle-handled/PillChipSlotCycleHandled.tsx +140 -140
  166. package/lib/motion/shotcraft/upstream/demos/typography/pill-slot-cycle/PillSlotCycle.tsx +309 -309
  167. package/lib/motion/shotcraft/upstream/demos/typography/text-column-converge/TextColumnConverge.tsx +191 -191
  168. package/lib/motion/shotcraft/upstream/demos/typography/title-demote-to-label/TitleDemoteToLabel.tsx +292 -292
  169. package/lib/motion/shotcraft/upstream/demos/typography/type-assembly-moves/LetterformDriftAssembly.tsx +135 -135
  170. package/lib/motion/shotcraft/upstream/demos/typography/type-assembly-moves/SplitTextStagger.tsx +124 -124
  171. package/lib/motion/shotcraft/upstream/demos/typography/type-assembly-moves/TextOnPath.tsx +255 -255
  172. package/lib/motion/shotcraft/upstream/demos/typography/type-entrance-moves/LetterDropPhysics.tsx +173 -173
  173. package/lib/motion/shotcraft/upstream/demos/typography/type-rhythm-sync/FontWeightPump.tsx +225 -225
  174. package/lib/motion/shotcraft/upstream/demos/typography/type-rhythm-sync/KaraokeFillSync.tsx +141 -141
  175. package/lib/motion/shotcraft/upstream/demos/typography/typewriter-moves/TypewriterErrorRetype.tsx +137 -137
  176. package/lib/motion/shotcraft/upstream/demos/typography/vertical-word-roll-blur-cycle/VerticalWordRollBlurCycle.tsx +147 -147
  177. package/lib/motion/shotcraft/upstream/demos/typography/word-relay-filmstrip/WordRelayFilmstrip.tsx +409 -409
  178. package/lib/motion/shotcraft/upstream/demos/typography/word-relay-geometry/WordRelayGeometry.tsx +280 -280
  179. package/lib/motion/shotcraft/upstream/demos/ui-entrance/avatar-bracket-carousel/AvatarBracketCarousel.tsx +271 -271
  180. package/lib/motion/shotcraft/upstream/demos/ui-entrance/bezier-source-converge-merge/BezierSourceConvergeMerge.tsx +385 -385
  181. package/lib/motion/shotcraft/upstream/demos/ui-entrance/card-stack/CardStack.tsx +312 -312
  182. package/lib/motion/shotcraft/upstream/demos/ui-entrance/carousel-3d/Carousel3D.tsx +303 -303
  183. package/lib/motion/shotcraft/upstream/demos/ui-entrance/clipcard-looping/ClipCardLooping.tsx +77 -77
  184. package/lib/motion/shotcraft/upstream/demos/ui-entrance/cloner-depth-echo/ClonerDepthEcho.tsx +311 -311
  185. package/lib/motion/shotcraft/upstream/demos/ui-entrance/deck-deal-flyin/DeckDealFlyin.tsx +307 -307
  186. package/lib/motion/shotcraft/upstream/demos/ui-entrance/doc-park-left-pill-deal/DocParkLeftPillDeal.tsx +349 -349
  187. package/lib/motion/shotcraft/upstream/demos/ui-entrance/document-typewriter-reveal/DocumentTypewriterReveal.tsx +255 -255
  188. package/lib/motion/shotcraft/upstream/demos/ui-entrance/draw-svg-trace/DrawSvgTrace.tsx +244 -244
  189. package/lib/motion/shotcraft/upstream/demos/ui-entrance/element-body-moves/ContactShadowLift.tsx +217 -217
  190. package/lib/motion/shotcraft/upstream/demos/ui-entrance/integration-hub-map/IntegrationHubMap.tsx +652 -652
  191. package/lib/motion/shotcraft/upstream/demos/ui-entrance/list-reveal/ListReveal.tsx +153 -153
  192. package/lib/motion/shotcraft/upstream/demos/ui-entrance/list-stack-press/ListStackPress.tsx +238 -238
  193. package/lib/motion/shotcraft/upstream/demos/ui-entrance/neon-frame-forerun/NeonFrameForerun.tsx +324 -324
  194. package/lib/motion/shotcraft/upstream/demos/ui-entrance/neon-frame-orbit-drop/NeonFrameForerunOrbit.tsx +354 -354
  195. package/lib/motion/shotcraft/upstream/demos/ui-entrance/page-waterfall-wall/PageWaterfallWall.tsx +101 -101
  196. package/lib/motion/shotcraft/upstream/demos/ui-entrance/page-waterfall-wall/VerticalTicker.tsx +129 -129
  197. package/lib/motion/shotcraft/upstream/demos/ui-entrance/product-card-progressive-assemble/ProductCardProgressiveAssemble.tsx +297 -297
  198. package/lib/motion/shotcraft/upstream/demos/ui-entrance/radial-wave/RadialWave.tsx +57 -57
  199. package/lib/motion/shotcraft/upstream/demos/ui-entrance/research-card-stack-scroll/ResearchCardStackScroll.tsx +155 -155
  200. package/lib/motion/shotcraft/upstream/demos/ui-entrance/row-embed/RowEmbed.tsx +175 -175
  201. package/lib/motion/shotcraft/upstream/demos/ui-entrance/value-stagger-gradient/ValueStaggerGradient.tsx +153 -153
  202. package/lib/motion/shotcraft/upstream/demos/ui-entrance/wall-reveal-moves/BentoLightUp.tsx +146 -146
  203. package/lib/motion/shotcraft/upstream/demos/ui-entrance/wall-reveal-moves/WireframeDrawOn.tsx +226 -226
  204. package/lib/motion/shotcraft/upstream/gallery/api/library.json +1 -1
  205. package/lib/motion/shotcraft/upstream/references/aesthetic-rules.md +193 -193
  206. package/lib/motion/shotcraft/upstream/references/final-review.md +121 -121
  207. package/lib/motion/shotcraft/upstream/references/guided-free-creation.md +199 -199
  208. package/lib/motion/shotcraft/upstream/references/jianying-export.md +183 -183
  209. package/lib/motion/shotcraft/upstream/references/music-beat-sync.md +257 -257
  210. package/lib/motion/shotcraft/upstream/references/pipeline.md +382 -382
  211. package/lib/motion/shotcraft/upstream/references/publish-upload-post.md +111 -111
  212. package/lib/motion/shotcraft/upstream/references/sequences/promo-energy-arc.md +50 -50
  213. package/lib/motion/shotcraft/upstream/references/sfx-sourcing.md +95 -95
  214. package/lib/motion/shotcraft/upstream/references/shots/camera/basic-3d-scene.md +41 -41
  215. package/lib/motion/shotcraft/upstream/references/shots/camera/crash-zoom-punch.md +52 -52
  216. package/lib/motion/shotcraft/upstream/references/shots/camera/cursor-flyover.md +39 -39
  217. package/lib/motion/shotcraft/upstream/references/shots/camera/depth-layer-moves.md +39 -39
  218. package/lib/motion/shotcraft/upstream/references/shots/camera/graze-face-tour.md +57 -57
  219. package/lib/motion/shotcraft/upstream/references/shots/camera/overhead-camera-moves.md +51 -51
  220. package/lib/motion/shotcraft/upstream/references/shots/camera/space-camera-moves.md +46 -46
  221. package/lib/motion/shotcraft/upstream/references/shots/camera/steep-tilt-glide.md +52 -52
  222. package/lib/motion/shotcraft/upstream/references/shots/camera/tension-camera-moves.md +43 -43
  223. package/lib/motion/shotcraft/upstream/references/shots/camera/terminal-3d.md +42 -42
  224. package/lib/motion/shotcraft/upstream/references/shots/data/before-after-slider-scrub.md +48 -48
  225. package/lib/motion/shotcraft/upstream/references/shots/data/chart-live-moves.md +52 -52
  226. package/lib/motion/shotcraft/upstream/references/shots/data/gauge-readout-moves.md +45 -45
  227. package/lib/motion/shotcraft/upstream/references/shots/data/scroll-brake-moves.md +46 -46
  228. package/lib/motion/shotcraft/upstream/references/shots/data/timeline-travel.md +44 -44
  229. package/lib/motion/shotcraft/upstream/references/shots/effects/assemble-then-type-flyin.md +63 -63
  230. package/lib/motion/shotcraft/upstream/references/shots/effects/aurora-bloom-bg-flip.md +54 -54
  231. package/lib/motion/shotcraft/upstream/references/shots/effects/brand-frame-snap.md +58 -58
  232. package/lib/motion/shotcraft/upstream/references/shots/effects/fui-hud-moves.md +35 -35
  233. package/lib/motion/shotcraft/upstream/references/shots/effects/glow-flyline-moves.md +53 -53
  234. package/lib/motion/shotcraft/upstream/references/shots/effects/icon-performance-moves.md +42 -42
  235. package/lib/motion/shotcraft/upstream/references/shots/effects/impact-feedback.md +50 -50
  236. package/lib/motion/shotcraft/upstream/references/shots/effects/light-play-moves.md +46 -46
  237. package/lib/motion/shotcraft/upstream/references/shots/effects/radial-ripple-phone-chips.md +56 -56
  238. package/lib/motion/shotcraft/upstream/references/shots/effects/riso-print-hits.md +49 -49
  239. package/lib/motion/shotcraft/upstream/references/shots/effects/scan-bracket-sweep.md +50 -50
  240. package/lib/motion/shotcraft/upstream/references/shots/effects/scanline-annotate-focus.md +53 -53
  241. package/lib/motion/shotcraft/upstream/references/shots/effects/scanline-assemble-flyin.md +53 -53
  242. package/lib/motion/shotcraft/upstream/references/shots/effects/slam-entrance-moves.md +55 -55
  243. package/lib/motion/shotcraft/upstream/references/shots/effects/spotlight-sweep-moves.md +52 -52
  244. package/lib/motion/shotcraft/upstream/references/shots/interaction/autolayout-gap-dial.md +60 -60
  245. package/lib/motion/shotcraft/upstream/references/shots/interaction/canvas-materialize-moves.md +57 -57
  246. package/lib/motion/shotcraft/upstream/references/shots/interaction/chip-grid-single-select-blackout.md +51 -51
  247. package/lib/motion/shotcraft/upstream/references/shots/interaction/collab-cursor-moves.md +41 -41
  248. package/lib/motion/shotcraft/upstream/references/shots/interaction/glass-pill-dictation-typing.md +53 -53
  249. package/lib/motion/shotcraft/upstream/references/shots/interaction/hashtag-to-pill-materialize.md +60 -60
  250. package/lib/motion/shotcraft/upstream/references/shots/interaction/picker-carousel-feature-cycle.md +55 -55
  251. package/lib/motion/shotcraft/upstream/references/shots/interaction/segmented-thumb-hero.md +57 -57
  252. package/lib/motion/shotcraft/upstream/references/shots/interaction/theme-switch-moves.md +47 -47
  253. package/lib/motion/shotcraft/upstream/references/shots/interaction/type-and-filter.md +37 -37
  254. package/lib/motion/shotcraft/upstream/references/shots/interaction/voice-waveform-live.md +54 -54
  255. package/lib/motion/shotcraft/upstream/references/shots/opening/brand-ink-open.md +36 -36
  256. package/lib/motion/shotcraft/upstream/references/shots/opening/crane-rise-reveal.md +56 -56
  257. package/lib/motion/shotcraft/upstream/references/shots/opening/dataviz-landscape-open.md +56 -56
  258. package/lib/motion/shotcraft/upstream/references/shots/opening/fracture.md +40 -40
  259. package/lib/motion/shotcraft/upstream/references/shots/opening/icon-field-colorize.md +60 -60
  260. package/lib/motion/shotcraft/upstream/references/shots/opening/letterspace-materialize.md +57 -57
  261. package/lib/motion/shotcraft/upstream/references/shots/opening/magician-card-flourish.md +73 -73
  262. package/lib/motion/shotcraft/upstream/references/shots/opening/orbit-ring-title-open.md +87 -87
  263. package/lib/motion/shotcraft/upstream/references/shots/opening/spotlight-hero-card.md +55 -55
  264. package/lib/motion/shotcraft/upstream/references/shots/opening/stroke-segment-build.md +50 -50
  265. package/lib/motion/shotcraft/upstream/references/shots/opening/text-as-mask.md +59 -59
  266. package/lib/motion/shotcraft/upstream/references/shots/outro/grain-dissolve.md +38 -38
  267. package/lib/motion/shotcraft/upstream/references/shots/outro/logo-shrink-wordmark-lockup.md +38 -38
  268. package/lib/motion/shotcraft/upstream/references/shots/outro/neon-triple-marquee.md +59 -59
  269. package/lib/motion/shotcraft/upstream/references/shots/outro/outro-group-photo-launch.md +39 -39
  270. package/lib/motion/shotcraft/upstream/references/shots/outro/ui-strip-away-outro.md +61 -61
  271. package/lib/motion/shotcraft/upstream/references/shots/outro/ui-to-brand-morph.md +52 -52
  272. package/lib/motion/shotcraft/upstream/references/shots/rhythm/beat-cut-moves.md +50 -50
  273. package/lib/motion/shotcraft/upstream/references/shots/rhythm/beat-step-list-theme-cycle.md +66 -66
  274. package/lib/motion/shotcraft/upstream/references/shots/rhythm/panel-grid-moves.md +43 -43
  275. package/lib/motion/shotcraft/upstream/references/shots/rhythm/quad-split-parallel-scenes.md +38 -38
  276. package/lib/motion/shotcraft/upstream/references/shots/rhythm/rhythm-interrupt-moves.md +38 -38
  277. package/lib/motion/shotcraft/upstream/references/shots/rhythm/sakuga-timing-shift.md +56 -56
  278. package/lib/motion/shotcraft/upstream/references/shots/rhythm/speed-ramp-freeze.md +44 -44
  279. package/lib/motion/shotcraft/upstream/references/shots/rhythm/trailer-grammar-moves.md +38 -38
  280. package/lib/motion/shotcraft/upstream/references/shots/transition/bottom-push-stack-wipe.md +63 -63
  281. package/lib/motion/shotcraft/upstream/references/shots/transition/card-flip-reveal.md +57 -57
  282. package/lib/motion/shotcraft/upstream/references/shots/transition/card-flock-tumble.md +63 -63
  283. package/lib/motion/shotcraft/upstream/references/shots/transition/circle-match-iris.md +58 -58
  284. package/lib/motion/shotcraft/upstream/references/shots/transition/gradient-transition.md +45 -45
  285. package/lib/motion/shotcraft/upstream/references/shots/transition/line-carry-transition.md +55 -55
  286. package/lib/motion/shotcraft/upstream/references/shots/transition/mosaic-reframe.md +49 -49
  287. package/lib/motion/shotcraft/upstream/references/shots/transition/page-turn-transitions.md +42 -42
  288. package/lib/motion/shotcraft/upstream/references/shots/transition/print-texture-transitions.md +42 -42
  289. package/lib/motion/shotcraft/upstream/references/shots/transition/shot-transitions.md +45 -45
  290. package/lib/motion/shotcraft/upstream/references/shots/transition/tear-streak-transitions.md +48 -48
  291. package/lib/motion/shotcraft/upstream/references/shots/transition/transition-hidden-cut.md +55 -55
  292. package/lib/motion/shotcraft/upstream/references/shots/transition/transition-travel.md +43 -43
  293. package/lib/motion/shotcraft/upstream/references/shots/transition/white-flash-logo-simplify-cut.md +41 -41
  294. package/lib/motion/shotcraft/upstream/references/shots/transition/wipe-transitions.md +42 -42
  295. package/lib/motion/shotcraft/upstream/references/shots/typography/blur-slide.md +59 -59
  296. package/lib/motion/shotcraft/upstream/references/shots/typography/brace-expand.md +55 -55
  297. package/lib/motion/shotcraft/upstream/references/shots/typography/countdown-arc-scatter.md +65 -65
  298. package/lib/motion/shotcraft/upstream/references/shots/typography/document-typewriter-reveal.md +42 -42
  299. package/lib/motion/shotcraft/upstream/references/shots/typography/flying-words.md +64 -64
  300. package/lib/motion/shotcraft/upstream/references/shots/typography/glitch-cycle.md +64 -64
  301. package/lib/motion/shotcraft/upstream/references/shots/typography/gradient-word-sweep.md +54 -54
  302. package/lib/motion/shotcraft/upstream/references/shots/typography/lead-word-zoom-assemble.md +91 -91
  303. package/lib/motion/shotcraft/upstream/references/shots/typography/marker-underline-title.md +51 -51
  304. package/lib/motion/shotcraft/upstream/references/shots/typography/outline-word-fill.md +66 -66
  305. package/lib/motion/shotcraft/upstream/references/shots/typography/paper-title-card.md +38 -38
  306. package/lib/motion/shotcraft/upstream/references/shots/typography/pill-chip-slot-cycle-handled.md +66 -66
  307. package/lib/motion/shotcraft/upstream/references/shots/typography/pill-slot-cycle.md +58 -58
  308. package/lib/motion/shotcraft/upstream/references/shots/typography/text-column-converge.md +61 -61
  309. package/lib/motion/shotcraft/upstream/references/shots/typography/title-demote-to-label.md +64 -64
  310. package/lib/motion/shotcraft/upstream/references/shots/typography/type-assembly-moves.md +45 -45
  311. package/lib/motion/shotcraft/upstream/references/shots/typography/type-entrance-moves.md +41 -41
  312. package/lib/motion/shotcraft/upstream/references/shots/typography/type-rhythm-sync.md +51 -51
  313. package/lib/motion/shotcraft/upstream/references/shots/typography/typewriter-moves.md +42 -42
  314. package/lib/motion/shotcraft/upstream/references/shots/typography/vertical-word-roll-blur-cycle.md +63 -63
  315. package/lib/motion/shotcraft/upstream/references/shots/typography/word-relay-filmstrip.md +57 -57
  316. package/lib/motion/shotcraft/upstream/references/shots/typography/word-relay-geometry.md +75 -75
  317. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/avatar-bracket-carousel.md +59 -59
  318. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/bezier-source-converge-merge.md +67 -67
  319. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/card-stack.md +60 -60
  320. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/carousel-3d.md +66 -66
  321. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/cloner-depth-echo.md +40 -40
  322. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/deck-deal-flyin.md +45 -45
  323. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/doc-park-left-pill-deal.md +62 -62
  324. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/draw-svg-trace.md +55 -55
  325. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/element-body-moves.md +37 -37
  326. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/integration-hub-map.md +55 -55
  327. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/list-reveal.md +50 -50
  328. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/list-stack-press.md +43 -43
  329. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/neon-frame-forerun.md +55 -55
  330. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/neon-frame-orbit-drop.md +51 -51
  331. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/page-waterfall-wall.md +51 -51
  332. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/product-card-progressive-assemble.md +56 -56
  333. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/radial-wave.md +49 -49
  334. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/research-card-stack-scroll.md +57 -57
  335. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/row-embed.md +36 -36
  336. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/value-stagger-gradient.md +64 -64
  337. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/wall-reveal-moves.md +47 -47
  338. package/lib/motion/shotcraft/upstream/references/sound-design.md +170 -170
  339. package/lib/motion/shotcraft/upstream/references/workbench.md +157 -157
  340. package/lib/motion/shotcraft/upstream/template/TEMPLATE.md +103 -103
  341. package/lib/motion/shotcraft/upstream/template/THEMES.md +44 -44
  342. package/lib/motion/shotcraft/upstream/template/package.json +20 -20
  343. package/lib/motion/shotcraft/upstream/template/remotion.config.ts +6 -6
  344. package/lib/motion/shotcraft/upstream/template/src/Root.tsx +17 -17
  345. package/lib/motion/shotcraft/upstream/template/src/aifl/Caption.tsx +65 -65
  346. package/lib/motion/shotcraft/upstream/template/src/aifl/DigitRoll.tsx +48 -48
  347. package/lib/motion/shotcraft/upstream/template/src/aifl/FlashCut.tsx +24 -24
  348. package/lib/motion/shotcraft/upstream/template/src/aifl/Main.tsx +192 -192
  349. package/lib/motion/shotcraft/upstream/template/src/aifl/PaperTitleCard.tsx +111 -111
  350. package/lib/motion/shotcraft/upstream/template/src/aifl/live/PageCam.tsx +150 -150
  351. package/lib/motion/shotcraft/upstream/template/src/aifl/live/SceneDetail.tsx +158 -158
  352. package/lib/motion/shotcraft/upstream/template/src/aifl/live/SceneFlyIn.tsx +468 -468
  353. package/lib/motion/shotcraft/upstream/template/src/aifl/live/SceneOpen.tsx +552 -552
  354. package/lib/motion/shotcraft/upstream/template/src/aifl/live/SceneOutroLive.tsx +411 -411
  355. package/lib/motion/shotcraft/upstream/template/src/aifl/live/ScenePapers.tsx +200 -200
  356. package/lib/motion/shotcraft/upstream/template/src/aifl/live/SceneWbr.tsx +324 -324
  357. package/lib/motion/shotcraft/upstream/template/src/aifl/live-layout.json +388 -388
  358. package/lib/motion/shotcraft/upstream/template/src/index.ts +4 -4
  359. package/lib/motion/shotcraft/upstream/template/src/themes/ThemedFilm.tsx +7 -7
  360. package/lib/motion/shotcraft/upstream/template/src/themes/manifest.tsx +68 -68
  361. package/lib/motion/shotcraft/upstream/template/src/themes/palette-assets.json +1 -1
  362. package/lib/motion/shotcraft/upstream/template/src/themes/palette-assets.ts +24 -24
  363. package/lib/motion/shotcraft/upstream/template/src/themes/palettes.json +74 -74
  364. package/lib/motion/shotcraft/upstream/template/src/themes/visual-theme.tsx +91 -91
  365. package/lib/motion/shotcraft/upstream/template/src/workbench.ts +213 -213
  366. package/lib/motion/shotcraft/upstream/template/tsconfig.json +15 -15
  367. package/lib/motion/style-frames.js +306 -306
  368. package/lib/motion/sync.js +214 -214
  369. package/lib/motion-library.js +1 -1
  370. package/lib/motion-project.js +105 -105
  371. package/lib/motion-render-worker.js +174 -174
  372. package/lib/routing/router.js +222 -222
  373. package/lib/routing/vendor/README.md +96 -96
  374. package/lib/routing/vendor/libavoid-routing.js +753 -753
  375. package/lib/routing/vendor/libavoid.min.js +59 -59
  376. package/lib/svg-export.js +1344 -0
  377. package/lib/video-trace-pool.js +128 -128
  378. package/package.json +3 -2
  379. package/LICENSE +0 -4175
@@ -1,552 +1,552 @@
1
- import { useVisualTheme, themePaint, themeAsset, sceneDefaults } from '../../themes/visual-theme';
2
- import { AbsoluteFill, Img, interpolate, useCurrentFrame, Easing } from 'remotion';
3
- import { AIFL_SHOTS } from '../Main';
4
- import { PageCam, CamKey } from './PageCam';
5
- import layout from '../live-layout.json';
6
-
7
-
8
- /** Context-level defaults (copy / sizes / palette), editable per clip in the
9
- * workbench. Motion timing below stays fixed. Hex = sRGB of the oklch tokens. */
10
- export const SCENE_OPEN_DEFAULTS = {
11
- wordmark: 'AI Foundation Lab',
12
- wordmarkSize: 132,
13
- kicker: 'TEAM RESEARCH CONSOLE',
14
- kickerSize: 26,
15
- noteLine1: 'One card,',
16
- noteLine2: 'one project.',
17
- noteSize: 37,
18
- ink: '#13110f', // oklch(18% 0.006 82)
19
- amber: '#955905', // oklch(52% 0.115 65)
20
- muted: '#65635f', // oklch(50% 0.006 82)
21
- paper: '#faf7f2',
22
- };
23
- type SceneOpenProps = Partial<typeof SCENE_OPEN_DEFAULTS>;
24
-
25
- const PAGE_H = layout.projects.pageH;
26
-
27
- // --- the ONE hero card the whole opening focuses on -------------------------
28
- // cards[3] sits in the centre column, mid-grid; its centre x is exactly 960
29
- // (page centre), so the push-in stays perfectly framed.
30
- const MAIN = 3;
31
- const CARD = layout.projects.cards[MAIN];
32
- const MCX = CARD.x + CARD.w / 2; // ≈ 960
33
- const MCY = CARD.y + CARD.h / 2; // 772
34
- const RADIUS = 16;
35
-
36
- // Camera: straight-on full page (52→84, static while the spotlight roves and
37
- // locks), then a 16-frame push-in that swings to a LEFT-SIDE view — rotY
38
- // dominant so the card's left edge reads near and its right edge recedes, with
39
- // only a slight downward tilt (rotX) — until the single card nearly fills the
40
- // frame (84→100), then held to the end (190) with an almost-imperceptible pull.
41
- // The focal page-point sits a touch LEFT of the card centre so the card lands
42
- // slightly right of screen-centre, foregrounding its near (left) edge.
43
- const CAM_KEYS: CamKey[] = [
44
- { frame: 82, cx: 960, cy: 540, zoom: 0.78, rotX: 0, rotY: 0, rotZ: 0, persp: 1200 },
45
- { frame: 114, cx: 960, cy: 540, zoom: 0.78, rotX: 0, rotY: 0, rotZ: 0, persp: 1200 },
46
- { frame: 130, cx: MCX - 30, cy: MCY, zoom: 2.6, rotX: 8, rotY: 34, rotZ: 2, persp: 1200 },
47
- { frame: 220, cx: MCX - 30, cy: MCY, zoom: 2.58, rotX: 8, rotY: 34, rotZ: 2, persp: 1200 },
48
- ];
49
- const PUSH_EASE = Easing.bezier(0.35, 0, 0.2, 1);
50
-
51
- const POP_EASE = Easing.bezier(0.2, 1.25, 0.3, 1); // spring with overshoot
52
- const RESEAT_EASE = Easing.bezier(0.4, 0, 0.3, 1.05); // compress on touchdown
53
-
54
- /** Brand open (0–83): an invisible pen draws an amber crosshair, "AI
55
- * Foundation Lab" letterpresses in glyph by glyph (all glyphs down by ~46), a mono kicker
56
- * types itself out, then the finished lockup RESTS fully-on for a second
57
- * (46–76) before dissolving and handing to the console.
58
- *
59
- * Single-card macro (82–220): a warm spotlight roves the dashboard, then locks
60
- * onto ONE card (pool shrinks, surroundings dim); the camera pushes in and
61
- * SWINGS to a left-side view (rotY-dominant: left edge near, right edge far)
62
- * until the card nearly fills the frame, a crisp 4x capture crossfading in over
63
- * the soft page texture; the card springs up off the plane and hovers ~54f
64
- * while a beam runs two laps around its rounded outline; then it settles flush
65
- * back into its slot (lock→touchdown ≈ 3.3s). */
66
- export const SceneOpen: React.FC<SceneOpenProps> = (props) => {
67
- const theme = useVisualTheme();
68
- const paperStyle = theme.id === 'ink-press';
69
- const paint = (css: string) => themePaint(theme, css);
70
- const asset = (src: string) => themeAsset(theme, src);
71
- const SERIF = (paperStyle ? 'ui-serif, Georgia, "Times New Roman", serif' : theme.font);
72
- const MONO = (paperStyle ? 'ui-monospace, SFMono-Regular, Menlo, monospace' : theme.font);
73
- const PATCH = paint('oklch(97.5% 0.008 82)');
74
- const SLOT_AMBER = paint('oklch(58% 0.13 65)');
75
- const BEAM_CORE = paint('rgba(255,248,232,0.98)');
76
- const {
77
- wordmark: WORDMARK, wordmarkSize, kicker: KICKER, kickerSize, noteLine1, noteLine2, noteSize,
78
- ink: INK, amber: AMBER, muted: INK2, paper,
79
- } = { ...sceneDefaults(theme, 'morning', SCENE_OPEN_DEFAULTS), ...props };
80
- const frame = useCurrentFrame();
81
- const duration = AIFL_SHOTS.morning.duration; // 220
82
-
83
- // --- crosshair draw-on (SVG pathLength = 100) ---
84
- const vDraw = interpolate(frame, [0, 9], [100, 0], {
85
- extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: Easing.bezier(0.3, 0, 0.2, 1),
86
- });
87
- const hDraw = interpolate(frame, [8, 18], [100, 0], {
88
- extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: Easing.linear,
89
- });
90
- const crossFade = interpolate(frame, [24, 34], [1, 0], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp' });
91
-
92
- // --- kicker typewriter (28 -> ~44), 0.7 frames per char ---
93
- const perChar = 0.7;
94
- const kickStart = 28;
95
- const kickChars = Math.floor(Math.max(0, frame - kickStart) / perChar);
96
- const kickDone = kickStart + KICKER.length * perChar; // ~43.4
97
- const cursorOn = (() => {
98
- if (frame < kickStart) return false;
99
- if (frame < kickDone) return true;
100
- if (frame > 74) return false; // keep blinking through the 1s rest
101
- const b = frame - kickDone;
102
- return Math.floor(b / 2) % 2 === 0;
103
- })();
104
-
105
- // --- brand group rests fully-on for ~1s (46 -> 76), then dissolves out
106
- // (76 -> 83): lift + shrink + fade ---
107
- const brandOut = interpolate(frame, [76, 83], [0, 1], {
108
- extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: Easing.bezier(0.4, 0, 0.5, 1),
109
- });
110
- const brandOpacity = 1 - brandOut;
111
- const groupY = -brandOut * 40;
112
- const groupScale = 1 - brandOut * 0.12;
113
-
114
- // --- macro fades in only after the brand is gone (82 -> 90) ---
115
- const macroIn = interpolate(frame, [82, 90], [0, 1], {
116
- extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: Easing.bezier(0.3, 0, 0.2, 1),
117
- });
118
-
119
- // --- roving spotlight (screen space): three deterministic waypoints across
120
- // the flat page, then locks onto the hero card centre (which sits at screen
121
- // (50%, ~67%) in the straight-on view) and rides the push-in to the middle.
122
- const spotEase = Easing.bezier(0.4, 0, 0.3, 1);
123
- const spotX = interpolate(frame, [86, 90, 98, 104, 110, 130], [25, 25, 70, 42, 50, 50], {
124
- extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: spotEase,
125
- });
126
- const spotY = interpolate(frame, [86, 90, 98, 104, 110, 130], [30, 30, 45, 60, 67, 50], {
127
- extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: spotEase,
128
- });
129
- const spotOn = interpolate(frame, [84, 92], [0, 1], {
130
- extrapolateLeft: 'clamp', extrapolateRight: 'clamp',
131
- });
132
-
133
- // pool radius: 620 while roving, shrinks to 420 on lock (74→84), then tightens
134
- // to wrap just the card through the push-in; a small pulse at the lock moment.
135
- const poolBase = interpolate(frame, [104, 114, 130], [620, 420, 360], {
136
- extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: Easing.bezier(0.4, 0, 0.3, 1),
137
- });
138
- const poolPulse = interpolate(frame, [114, 118, 123], [0, 0.06, 0], {
139
- extrapolateLeft: 'clamp', extrapolateRight: 'clamp',
140
- });
141
- const poolRx = poolBase * (1 + poolPulse);
142
- const poolRy = poolBase * 0.8 * (1 + poolPulse);
143
- // outside-pool dim deepens as the light locks and the camera pushes in.
144
- const vignette = interpolate(frame, [104, 114, 130], paperStyle ? [0.16, 0.34, 0.42] : [0.04, 0.07, 0.09], {
145
- extrapolateLeft: 'clamp', extrapolateRight: 'clamp',
146
- });
147
-
148
- // --- DOF rises through the push-in, then relaxes to zero while the card
149
- // hovers (the hero is the only subject and the surround is already dim). ---
150
- const dofStrength = interpolate(frame, [114, 130, 140, 150], [0, 9, 9, 0], {
151
- extrapolateLeft: 'clamp', extrapolateRight: 'clamp',
152
- });
153
-
154
- // --- hero card pop-up ------------------------------------------------------
155
- // rise 130→140 (spring overshoot), hover 140→194 (54f sin bob), reseat
156
- // 194→212 (18f, gentle) — lock(114)→touchdown(212) ≈ 98f ≈ 3.3s.
157
- const rise = interpolate(frame, [130, 140], [0, 1], {
158
- extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: POP_EASE,
159
- });
160
- const reseat = interpolate(frame, [194, 212], [0, 1], {
161
- extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: RESEAT_EASE,
162
- });
163
- const lift = rise * (1 - reseat); // altitude fraction, 0 at rest / reseated
164
- // slow hover bob over the 54-frame float (period ~40f -> ~1.35 cycles)
165
- const bob = Math.sin(((frame - 140) / 40) * Math.PI * 2) * 4 * lift;
166
- const z = 110 * lift + bob;
167
- const landed = frame >= 212;
168
-
169
- // press-bounce on touchdown (0.997 → 1 over the last few reseat frames)
170
- const press = interpolate(frame, [208, 211, 212], [1, 0.997, 1], {
171
- extrapolateLeft: 'clamp', extrapolateRight: 'clamp',
172
- });
173
-
174
- // two-layer shadow that grows with altitude
175
- const shadow = paint(`0 ${8 * lift}px ${10 + 12 * lift}px rgba(40,30,20,${0.18 * lift}), 0 ${46 * lift}px ${90 * lift}px rgba(40,30,20,${0.22 * lift})`);
176
-
177
- // vacated-slot amber outline: alive while airborne, brightens as the card
178
- // lands, then vanishes.
179
- const slotVis = Math.min(1, rise * 2) * (1 - reseat);
180
- const landPulse = interpolate(frame, [208, 212, 216], [0, 1, 0], {
181
- extrapolateLeft: 'clamp', extrapolateRight: 'clamp',
182
- });
183
- const slotEdge = Math.min(1, 0.4 * (1 - reseat)) + landPulse * 0.6;
184
-
185
- // --- perimeter beam: TWO laps around the card outline during the hover.
186
- // Lap 1 (142→156) runs fast and bright; lap 2 (162→182) runs slower and
187
- // weaker — two laps read as a sustained scan rather than a single blink. A
188
- // faint amber ring lingers after lap 2 and fades before reseat (182→194). ---
189
- const beam1Prog = interpolate(frame, [142, 156], [0, 1], {
190
- extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: Easing.linear,
191
- });
192
- const beam1On = frame >= 141 && frame <= 157;
193
- const beam2Prog = interpolate(frame, [162, 182], [0, 1], {
194
- extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: Easing.bezier(0.4, 0, 0.4, 1),
195
- });
196
- const beam2On = frame >= 161 && frame <= 183;
197
- const beamTrail = interpolate(frame, [182, 194], [0.35, 0], {
198
- extrapolateLeft: 'clamp', extrapolateRight: 'clamp',
199
- });
200
- const bw = CARD.w + 6;
201
- const bh = CARD.h + 6;
202
-
203
- // --- hi-res hero: a 4x element capture crossfades in as the push-in starts
204
- // (84→90), covering the soft 2x page texture underneath. It stays on top for
205
- // the pop-up / hover / reseat so the enlarged card reads crisp. ---
206
- const hiresIn = interpolate(frame, [114, 120], [0, 1], {
207
- extrapolateLeft: 'clamp', extrapolateRight: 'clamp',
208
- });
209
-
210
- // --- whole scene dissolves to paper just before the cut -> title card 1 ---
211
- const outT = interpolate(frame, [duration - 5, duration], [0, 1], {
212
- extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: Easing.bezier(0.5, 0, 0.6, 1),
213
- });
214
- const rootOpacity = 1 - outT;
215
-
216
- return (
217
- <AbsoluteFill style={{ backgroundColor: paper, opacity: rootOpacity }}>
218
- {frame >= 84 ? (
219
- <AbsoluteFill style={{ opacity: macroIn }}>
220
- <PageCam
221
- src="textures/live/projects-full.png"
222
- pageH={PAGE_H}
223
- keys={CAM_KEYS}
224
- ease={PUSH_EASE}
225
- dof={{ focusY: 240, strength: dofStrength }}
226
- >
227
- {/* rim light along the near (bottom) edge of the tilted plane */}
228
- <div
229
- style={{
230
- position: 'absolute', left: 0, right: 0, bottom: 0, height: 8,
231
- background: paint('rgba(255,255,255,0.85)'), filter: 'blur(6px)',
232
- opacity: 0.6 * Math.min(1, lift + Math.max(0, (frame - 114) / 16)),
233
- pointerEvents: 'none',
234
- }}
235
- />
236
-
237
- {/* hero card: lifts off its slot, hovers, beam runs, settles back */}
238
- <div style={{ transformStyle: 'preserve-3d' }}>
239
- {/* vacated-slot patch + breathing amber outline (only when up) */}
240
- {slotVis > 0.02 ? (
241
- <div
242
- style={{
243
- position: 'absolute', left: CARD.x - 2, top: CARD.y - 2,
244
- width: CARD.w + 4, height: CARD.h + 4, background: PATCH,
245
- borderRadius: RADIUS,
246
- boxShadow: paint(`inset 0 0 26px rgba(180,120,50,${0.12 * slotEdge})`),
247
- opacity: slotVis,
248
- }}
249
- >
250
- <div
251
- style={{
252
- position: 'absolute', inset: 0, borderRadius: RADIUS,
253
- border: `1.5px solid ${SLOT_AMBER}`, opacity: slotEdge,
254
- pointerEvents: 'none',
255
- }}
256
- />
257
- </div>
258
- ) : null}
259
-
260
- {/* the levitating card */}
261
- <div
262
- style={{
263
- position: 'absolute', left: CARD.x, top: CARD.y,
264
- width: CARD.w, height: CARD.h,
265
- transform: `translateZ(${z}px) scale(${press})`,
266
- transformOrigin: 'center center',
267
- transformStyle: 'preserve-3d',
268
- }}
269
- >
270
- {/* card body (clipped) + top-light gradient */}
271
- <div
272
- style={{
273
- position: 'absolute', inset: 0, borderRadius: RADIUS,
274
- overflow: 'hidden',
275
- boxShadow: landed ? 'none' : shadow,
276
- }}
277
- >
278
- <Img
279
- src={asset(`textures/live/${CARD.file}`)}
280
- style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', display: 'block' }}
281
- />
282
- {/* 4x hi-res capture layered on top, laid out at plain CARD
283
- size. Because PageCam now applies the push-in magnification
284
- as a LAYOUT-scale CSS `zoom` (not a transform scale), this
285
- image rasterizes at its enlarged device size (~CARD.w*zoom
286
- px) and samples DOWN from the 1432px source — crisp — rather
287
- than being downsampled to layout size and GPU-upscaled. */}
288
- <Img
289
- src={asset('textures/live/card4-hires.png')}
290
- style={{
291
- position: 'absolute',
292
- inset: 0,
293
- width: '100%',
294
- height: '100%',
295
- display: 'block',
296
- opacity: hiresIn,
297
- }}
298
- />
299
- {/* top-light sheen, like a card lit from above by the spot */}
300
- <div
301
- style={{
302
- position: 'absolute', inset: 0,
303
- background: paint('linear-gradient(160deg, rgba(255,255,255,0.5), transparent 40%)'),
304
- opacity: lift, pointerEvents: 'none',
305
- }}
306
- />
307
- </div>
308
- {/* 1px white inner stroke (lit paper edge) */}
309
- <div
310
- style={{
311
- position: 'absolute', inset: 0, borderRadius: RADIUS,
312
- boxShadow: paint(`inset 0 0 0 1px rgba(255,255,255,${0.7 * lift})`),
313
- pointerEvents: 'none',
314
- }}
315
- />
316
-
317
- {/* perimeter beam: SVG rounded-rect, pathLength=1 travelling arc.
318
- Lap 1 (fast, bright) then lap 2 (slow, weaker). */}
319
- {(beam1On || beam2On) && lift > 0.4 ? (
320
- <svg
321
- width={bw}
322
- height={bh}
323
- viewBox={`0 0 ${bw} ${bh}`}
324
- style={{
325
- position: 'absolute', left: -3, top: -3, overflow: 'visible',
326
- pointerEvents: 'none',
327
- opacity: beam1On ? 1 : 0.62,
328
- filter: paint(`drop-shadow(0 0 6px ${AMBER}) drop-shadow(0 0 18px rgba(255,240,210,0.55))`),
329
- }}
330
- >
331
- {/* amber edge (wider) */}
332
- <rect
333
- x={2} y={2} width={bw - 4} height={bh - 4} rx={RADIUS} fill="none"
334
- stroke={AMBER} strokeWidth={beam1On ? 5 : 3.5} strokeLinecap="round"
335
- pathLength={1} strokeDasharray="0.14 1" strokeDashoffset={-(beam1On ? beam1Prog : beam2Prog)}
336
- />
337
- {/* warm-white core (narrower) */}
338
- <rect
339
- x={2} y={2} width={bw - 4} height={bh - 4} rx={RADIUS} fill="none"
340
- stroke={BEAM_CORE} strokeWidth={beam1On ? 2.5 : 1.75} strokeLinecap="round"
341
- pathLength={1} strokeDasharray="0.14 1" strokeDashoffset={-(beam1On ? beam1Prog : beam2Prog)}
342
- />
343
- </svg>
344
- ) : null}
345
-
346
- {/* faint amber ring left behind, fades out */}
347
- {beamTrail > 0.01 ? (
348
- <div
349
- style={{
350
- position: 'absolute', inset: -3, borderRadius: RADIUS + 3,
351
- border: `1.5px solid ${AMBER}`, opacity: beamTrail,
352
- pointerEvents: 'none',
353
- }}
354
- />
355
- ) : null}
356
- </div>
357
- </div>
358
-
359
- {/* 3D floating annotation LEFT of the hovering card: big serif note
360
- lifted off the page plane (translateZ), seen through the same
361
- oblique camera; a marker highlight sweeps under the key words. */}
362
- {frame >= 142 && frame <= 212 ? (() => {
363
- const noteIn = interpolate(frame, [142, 152], [0, 1], {
364
- extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: Easing.bezier(0.2, 0.75, 0.3, 1),
365
- });
366
- const noteOut = interpolate(frame, [198, 208], [1, 0], {
367
- extrapolateLeft: 'clamp', extrapolateRight: 'clamp',
368
- });
369
- const noteVis = noteIn * noteOut;
370
- // gentle bob in sympathy with the card's hover
371
- const noteZ = 92 + Math.sin(((frame - 142) / 44) * Math.PI * 2) * 3;
372
- const hl = interpolate(frame, [156, 168], [0, 1], {
373
- extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: Easing.bezier(0.3, 0, 0.2, 1),
374
- });
375
- return (
376
- <div style={{ transformStyle: 'preserve-3d', pointerEvents: 'none' }}>
377
- {/* soft shadow the note casts onto the page */}
378
- <div
379
- style={{
380
- position: 'absolute', left: 566, top: 736, width: 210, height: 74,
381
- transform: 'translateZ(2px)',
382
- background: paint('radial-gradient(ellipse at 50% 50%, rgba(40,30,20,0.3), transparent 70%)'),
383
- filter: 'blur(12px)',
384
- opacity: 0.55 * noteVis,
385
- }}
386
- />
387
- <div
388
- style={{
389
- position: 'absolute', left: 556, top: 668, width: 230,
390
- ...(!paperStyle ? {background: theme.page, boxShadow: `0 0 0 14px ${theme.page}`, borderRadius: 7} : {}),
391
- transform: `translateZ(${noteZ}px) translateY(${(1 - noteIn) * 26}px)`,
392
- opacity: noteVis,
393
- filter: `blur(${(1 - noteIn) * 4}px)`,
394
- }}
395
- >
396
- <div
397
- style={{
398
- fontFamily: SERIF, fontSize: noteSize, fontWeight: 600, color: INK,
399
- lineHeight: 1.16, letterSpacing: '-0.012em',
400
- }}
401
- >
402
- {noteLine1}
403
- </div>
404
- <div style={{ position: 'relative', display: 'inline-block' }}>
405
- {/* marker highlight sweeping in behind the words */}
406
- <div
407
- style={{
408
- position: 'absolute', left: -5, top: '12%', bottom: '4%',
409
- width: `calc(${hl} * (100% + 10px))`,
410
- background: paint('oklch(88% 0.095 85)'),
411
- borderRadius: 4,
412
- }}
413
- />
414
- <div
415
- style={{
416
- position: 'relative',
417
- fontFamily: SERIF, fontStyle: paperStyle ? 'italic' : 'normal', fontSize: noteSize, fontWeight: 600,
418
- color: INK, lineHeight: 1.16, letterSpacing: '-0.012em',
419
- }}
420
- >
421
- {noteLine2}
422
- </div>
423
- </div>
424
- </div>
425
- </div>
426
- );
427
- })() : null}
428
- </PageCam>
429
-
430
- {/* roving / locking spotlight: warm pool + dim outside so it reads */}
431
- <AbsoluteFill
432
- style={{
433
- background: paint(`radial-gradient(${poolRx}px ${poolRy}px at ${spotX}% ${spotY}%, rgba(255,241,214,0.42), rgba(255,241,214,0.10) 45%, rgba(70,56,38,${vignette * spotOn}) 100%)`),
434
- pointerEvents: 'none',
435
- opacity: paperStyle ? spotOn : spotOn * 0.18,
436
- }}
437
- />
438
- {/* faint trailing bounce of the same light */}
439
- <AbsoluteFill
440
- style={{
441
- background: paint(`radial-gradient(300px 220px at ${spotX - 6}% ${spotY + 10}%, rgba(255,246,228,0.18), transparent 70%)`),
442
- pointerEvents: 'none',
443
- opacity: paperStyle ? spotOn * 0.7 : 0,
444
- }}
445
- />
446
- </AbsoluteFill>
447
- ) : null}
448
-
449
- {/* brand group: crosshair + wordmark + kicker, dissolves out by frame 53 */}
450
- {brandOpacity > 0 ? (
451
- <AbsoluteFill style={{ justifyContent: 'center', alignItems: 'center', pointerEvents: 'none', opacity: brandOpacity }}>
452
- <div
453
- style={{
454
- textAlign: 'center',
455
- transform: `translateY(${groupY}px) scale(${groupScale})`,
456
- transformOrigin: 'center center',
457
- }}
458
- >
459
- {/* crosshair drawn by an invisible amber pen */}
460
- <svg
461
- width={64}
462
- height={64}
463
- viewBox="0 0 64 64"
464
- style={{ display: 'block', margin: '0 auto 34px', opacity: crossFade }}
465
- >
466
- <line
467
- x1={32} y1={2} x2={32} y2={62}
468
- stroke={AMBER} strokeWidth={5} strokeLinecap="round"
469
- pathLength={100} strokeDasharray={100} strokeDashoffset={vDraw}
470
- />
471
- <line
472
- x1={2} y1={32} x2={62} y2={32}
473
- stroke={AMBER} strokeWidth={5} strokeLinecap="round"
474
- pathLength={100} strokeDasharray={100} strokeDashoffset={hDraw}
475
- />
476
- </svg>
477
-
478
- {/* wordmark: glyph-by-glyph letterpress with amber under-glint */}
479
- <div
480
- style={{
481
- fontFamily: SERIF, fontSize: wordmarkSize, fontWeight: 600, color: INK,
482
- letterSpacing: '-0.01em', lineHeight: 1, whiteSpace: 'pre',
483
- display: 'inline-flex', alignItems: 'flex-end',
484
- }}
485
- >
486
- {WORDMARK.split('').map((ch, i) => {
487
- const delay = 10 + i * (paperStyle ? 3 : 1.5);
488
- const t = interpolate(frame, [delay, delay + 12], [0, 1], {
489
- extrapolateLeft: 'clamp', extrapolateRight: 'clamp',
490
- easing: Easing.bezier(0.2, 0.7, 0.25, 1),
491
- });
492
- const glintCenter = delay + 12;
493
- const glint = interpolate(frame, [glintCenter - 4, glintCenter, glintCenter + 4], [0, 1, 0], {
494
- extrapolateLeft: 'clamp', extrapolateRight: 'clamp',
495
- });
496
- return (
497
- <span
498
- key={i}
499
- style={{
500
- position: 'relative',
501
- display: 'inline-block',
502
- opacity: t,
503
- transform: `scale(${1.6 - 0.6 * t})`,
504
- transformOrigin: 'center bottom',
505
- filter: `blur(${(1 - t) * 6}px)`,
506
- }}
507
- >
508
- {ch === ' ' ? ' ' : ch}
509
- <span
510
- style={{
511
- position: 'absolute',
512
- left: '50%',
513
- bottom: -6,
514
- transform: 'translateX(-50%)',
515
- width: `${glint * 100}%`,
516
- height: 2,
517
- background: AMBER,
518
- opacity: glint,
519
- borderRadius: 2,
520
- }}
521
- />
522
- </span>
523
- );
524
- })}
525
- </div>
526
-
527
- {/* mono kicker typewriter + amber block cursor */}
528
- <div
529
- style={{
530
- fontFamily: MONO, fontSize: kickerSize, letterSpacing: '0.14em', color: INK2,
531
- marginTop: 30, textTransform: 'uppercase', height: 30,
532
- display: 'flex', justifyContent: 'center', alignItems: 'center',
533
- }}
534
- >
535
- <span style={{ whiteSpace: 'pre' }}>{KICKER.slice(0, kickChars)}</span>
536
- <span
537
- style={{
538
- display: 'inline-block',
539
- width: 14,
540
- height: 24,
541
- marginLeft: 4,
542
- background: AMBER,
543
- opacity: cursorOn ? 0.85 : 0,
544
- }}
545
- />
546
- </div>
547
- </div>
548
- </AbsoluteFill>
549
- ) : null}
550
- </AbsoluteFill>
551
- );
552
- };
1
+ import { useVisualTheme, themePaint, themeAsset, sceneDefaults } from '../../themes/visual-theme';
2
+ import { AbsoluteFill, Img, interpolate, useCurrentFrame, Easing } from 'remotion';
3
+ import { AIFL_SHOTS } from '../Main';
4
+ import { PageCam, CamKey } from './PageCam';
5
+ import layout from '../live-layout.json';
6
+
7
+
8
+ /** Context-level defaults (copy / sizes / palette), editable per clip in the
9
+ * workbench. Motion timing below stays fixed. Hex = sRGB of the oklch tokens. */
10
+ export const SCENE_OPEN_DEFAULTS = {
11
+ wordmark: 'AI Foundation Lab',
12
+ wordmarkSize: 132,
13
+ kicker: 'TEAM RESEARCH CONSOLE',
14
+ kickerSize: 26,
15
+ noteLine1: 'One card,',
16
+ noteLine2: 'one project.',
17
+ noteSize: 37,
18
+ ink: '#13110f', // oklch(18% 0.006 82)
19
+ amber: '#955905', // oklch(52% 0.115 65)
20
+ muted: '#65635f', // oklch(50% 0.006 82)
21
+ paper: '#faf7f2',
22
+ };
23
+ type SceneOpenProps = Partial<typeof SCENE_OPEN_DEFAULTS>;
24
+
25
+ const PAGE_H = layout.projects.pageH;
26
+
27
+ // --- the ONE hero card the whole opening focuses on -------------------------
28
+ // cards[3] sits in the centre column, mid-grid; its centre x is exactly 960
29
+ // (page centre), so the push-in stays perfectly framed.
30
+ const MAIN = 3;
31
+ const CARD = layout.projects.cards[MAIN];
32
+ const MCX = CARD.x + CARD.w / 2; // ≈ 960
33
+ const MCY = CARD.y + CARD.h / 2; // 772
34
+ const RADIUS = 16;
35
+
36
+ // Camera: straight-on full page (52→84, static while the spotlight roves and
37
+ // locks), then a 16-frame push-in that swings to a LEFT-SIDE view — rotY
38
+ // dominant so the card's left edge reads near and its right edge recedes, with
39
+ // only a slight downward tilt (rotX) — until the single card nearly fills the
40
+ // frame (84→100), then held to the end (190) with an almost-imperceptible pull.
41
+ // The focal page-point sits a touch LEFT of the card centre so the card lands
42
+ // slightly right of screen-centre, foregrounding its near (left) edge.
43
+ const CAM_KEYS: CamKey[] = [
44
+ { frame: 82, cx: 960, cy: 540, zoom: 0.78, rotX: 0, rotY: 0, rotZ: 0, persp: 1200 },
45
+ { frame: 114, cx: 960, cy: 540, zoom: 0.78, rotX: 0, rotY: 0, rotZ: 0, persp: 1200 },
46
+ { frame: 130, cx: MCX - 30, cy: MCY, zoom: 2.6, rotX: 8, rotY: 34, rotZ: 2, persp: 1200 },
47
+ { frame: 220, cx: MCX - 30, cy: MCY, zoom: 2.58, rotX: 8, rotY: 34, rotZ: 2, persp: 1200 },
48
+ ];
49
+ const PUSH_EASE = Easing.bezier(0.35, 0, 0.2, 1);
50
+
51
+ const POP_EASE = Easing.bezier(0.2, 1.25, 0.3, 1); // spring with overshoot
52
+ const RESEAT_EASE = Easing.bezier(0.4, 0, 0.3, 1.05); // compress on touchdown
53
+
54
+ /** Brand open (0–83): an invisible pen draws an amber crosshair, "AI
55
+ * Foundation Lab" letterpresses in glyph by glyph (all glyphs down by ~46), a mono kicker
56
+ * types itself out, then the finished lockup RESTS fully-on for a second
57
+ * (46–76) before dissolving and handing to the console.
58
+ *
59
+ * Single-card macro (82–220): a warm spotlight roves the dashboard, then locks
60
+ * onto ONE card (pool shrinks, surroundings dim); the camera pushes in and
61
+ * SWINGS to a left-side view (rotY-dominant: left edge near, right edge far)
62
+ * until the card nearly fills the frame, a crisp 4x capture crossfading in over
63
+ * the soft page texture; the card springs up off the plane and hovers ~54f
64
+ * while a beam runs two laps around its rounded outline; then it settles flush
65
+ * back into its slot (lock→touchdown ≈ 3.3s). */
66
+ export const SceneOpen: React.FC<SceneOpenProps> = (props) => {
67
+ const theme = useVisualTheme();
68
+ const paperStyle = theme.id === 'ink-press';
69
+ const paint = (css: string) => themePaint(theme, css);
70
+ const asset = (src: string) => themeAsset(theme, src);
71
+ const SERIF = (paperStyle ? 'ui-serif, Georgia, "Times New Roman", serif' : theme.font);
72
+ const MONO = (paperStyle ? 'ui-monospace, SFMono-Regular, Menlo, monospace' : theme.font);
73
+ const PATCH = paint('oklch(97.5% 0.008 82)');
74
+ const SLOT_AMBER = paint('oklch(58% 0.13 65)');
75
+ const BEAM_CORE = paint('rgba(255,248,232,0.98)');
76
+ const {
77
+ wordmark: WORDMARK, wordmarkSize, kicker: KICKER, kickerSize, noteLine1, noteLine2, noteSize,
78
+ ink: INK, amber: AMBER, muted: INK2, paper,
79
+ } = { ...sceneDefaults(theme, 'morning', SCENE_OPEN_DEFAULTS), ...props };
80
+ const frame = useCurrentFrame();
81
+ const duration = AIFL_SHOTS.morning.duration; // 220
82
+
83
+ // --- crosshair draw-on (SVG pathLength = 100) ---
84
+ const vDraw = interpolate(frame, [0, 9], [100, 0], {
85
+ extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: Easing.bezier(0.3, 0, 0.2, 1),
86
+ });
87
+ const hDraw = interpolate(frame, [8, 18], [100, 0], {
88
+ extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: Easing.linear,
89
+ });
90
+ const crossFade = interpolate(frame, [24, 34], [1, 0], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp' });
91
+
92
+ // --- kicker typewriter (28 -> ~44), 0.7 frames per char ---
93
+ const perChar = 0.7;
94
+ const kickStart = 28;
95
+ const kickChars = Math.floor(Math.max(0, frame - kickStart) / perChar);
96
+ const kickDone = kickStart + KICKER.length * perChar; // ~43.4
97
+ const cursorOn = (() => {
98
+ if (frame < kickStart) return false;
99
+ if (frame < kickDone) return true;
100
+ if (frame > 74) return false; // keep blinking through the 1s rest
101
+ const b = frame - kickDone;
102
+ return Math.floor(b / 2) % 2 === 0;
103
+ })();
104
+
105
+ // --- brand group rests fully-on for ~1s (46 -> 76), then dissolves out
106
+ // (76 -> 83): lift + shrink + fade ---
107
+ const brandOut = interpolate(frame, [76, 83], [0, 1], {
108
+ extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: Easing.bezier(0.4, 0, 0.5, 1),
109
+ });
110
+ const brandOpacity = 1 - brandOut;
111
+ const groupY = -brandOut * 40;
112
+ const groupScale = 1 - brandOut * 0.12;
113
+
114
+ // --- macro fades in only after the brand is gone (82 -> 90) ---
115
+ const macroIn = interpolate(frame, [82, 90], [0, 1], {
116
+ extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: Easing.bezier(0.3, 0, 0.2, 1),
117
+ });
118
+
119
+ // --- roving spotlight (screen space): three deterministic waypoints across
120
+ // the flat page, then locks onto the hero card centre (which sits at screen
121
+ // (50%, ~67%) in the straight-on view) and rides the push-in to the middle.
122
+ const spotEase = Easing.bezier(0.4, 0, 0.3, 1);
123
+ const spotX = interpolate(frame, [86, 90, 98, 104, 110, 130], [25, 25, 70, 42, 50, 50], {
124
+ extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: spotEase,
125
+ });
126
+ const spotY = interpolate(frame, [86, 90, 98, 104, 110, 130], [30, 30, 45, 60, 67, 50], {
127
+ extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: spotEase,
128
+ });
129
+ const spotOn = interpolate(frame, [84, 92], [0, 1], {
130
+ extrapolateLeft: 'clamp', extrapolateRight: 'clamp',
131
+ });
132
+
133
+ // pool radius: 620 while roving, shrinks to 420 on lock (74→84), then tightens
134
+ // to wrap just the card through the push-in; a small pulse at the lock moment.
135
+ const poolBase = interpolate(frame, [104, 114, 130], [620, 420, 360], {
136
+ extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: Easing.bezier(0.4, 0, 0.3, 1),
137
+ });
138
+ const poolPulse = interpolate(frame, [114, 118, 123], [0, 0.06, 0], {
139
+ extrapolateLeft: 'clamp', extrapolateRight: 'clamp',
140
+ });
141
+ const poolRx = poolBase * (1 + poolPulse);
142
+ const poolRy = poolBase * 0.8 * (1 + poolPulse);
143
+ // outside-pool dim deepens as the light locks and the camera pushes in.
144
+ const vignette = interpolate(frame, [104, 114, 130], paperStyle ? [0.16, 0.34, 0.42] : [0.04, 0.07, 0.09], {
145
+ extrapolateLeft: 'clamp', extrapolateRight: 'clamp',
146
+ });
147
+
148
+ // --- DOF rises through the push-in, then relaxes to zero while the card
149
+ // hovers (the hero is the only subject and the surround is already dim). ---
150
+ const dofStrength = interpolate(frame, [114, 130, 140, 150], [0, 9, 9, 0], {
151
+ extrapolateLeft: 'clamp', extrapolateRight: 'clamp',
152
+ });
153
+
154
+ // --- hero card pop-up ------------------------------------------------------
155
+ // rise 130→140 (spring overshoot), hover 140→194 (54f sin bob), reseat
156
+ // 194→212 (18f, gentle) — lock(114)→touchdown(212) ≈ 98f ≈ 3.3s.
157
+ const rise = interpolate(frame, [130, 140], [0, 1], {
158
+ extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: POP_EASE,
159
+ });
160
+ const reseat = interpolate(frame, [194, 212], [0, 1], {
161
+ extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: RESEAT_EASE,
162
+ });
163
+ const lift = rise * (1 - reseat); // altitude fraction, 0 at rest / reseated
164
+ // slow hover bob over the 54-frame float (period ~40f -> ~1.35 cycles)
165
+ const bob = Math.sin(((frame - 140) / 40) * Math.PI * 2) * 4 * lift;
166
+ const z = 110 * lift + bob;
167
+ const landed = frame >= 212;
168
+
169
+ // press-bounce on touchdown (0.997 → 1 over the last few reseat frames)
170
+ const press = interpolate(frame, [208, 211, 212], [1, 0.997, 1], {
171
+ extrapolateLeft: 'clamp', extrapolateRight: 'clamp',
172
+ });
173
+
174
+ // two-layer shadow that grows with altitude
175
+ const shadow = paint(`0 ${8 * lift}px ${10 + 12 * lift}px rgba(40,30,20,${0.18 * lift}), 0 ${46 * lift}px ${90 * lift}px rgba(40,30,20,${0.22 * lift})`);
176
+
177
+ // vacated-slot amber outline: alive while airborne, brightens as the card
178
+ // lands, then vanishes.
179
+ const slotVis = Math.min(1, rise * 2) * (1 - reseat);
180
+ const landPulse = interpolate(frame, [208, 212, 216], [0, 1, 0], {
181
+ extrapolateLeft: 'clamp', extrapolateRight: 'clamp',
182
+ });
183
+ const slotEdge = Math.min(1, 0.4 * (1 - reseat)) + landPulse * 0.6;
184
+
185
+ // --- perimeter beam: TWO laps around the card outline during the hover.
186
+ // Lap 1 (142→156) runs fast and bright; lap 2 (162→182) runs slower and
187
+ // weaker — two laps read as a sustained scan rather than a single blink. A
188
+ // faint amber ring lingers after lap 2 and fades before reseat (182→194). ---
189
+ const beam1Prog = interpolate(frame, [142, 156], [0, 1], {
190
+ extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: Easing.linear,
191
+ });
192
+ const beam1On = frame >= 141 && frame <= 157;
193
+ const beam2Prog = interpolate(frame, [162, 182], [0, 1], {
194
+ extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: Easing.bezier(0.4, 0, 0.4, 1),
195
+ });
196
+ const beam2On = frame >= 161 && frame <= 183;
197
+ const beamTrail = interpolate(frame, [182, 194], [0.35, 0], {
198
+ extrapolateLeft: 'clamp', extrapolateRight: 'clamp',
199
+ });
200
+ const bw = CARD.w + 6;
201
+ const bh = CARD.h + 6;
202
+
203
+ // --- hi-res hero: a 4x element capture crossfades in as the push-in starts
204
+ // (84→90), covering the soft 2x page texture underneath. It stays on top for
205
+ // the pop-up / hover / reseat so the enlarged card reads crisp. ---
206
+ const hiresIn = interpolate(frame, [114, 120], [0, 1], {
207
+ extrapolateLeft: 'clamp', extrapolateRight: 'clamp',
208
+ });
209
+
210
+ // --- whole scene dissolves to paper just before the cut -> title card 1 ---
211
+ const outT = interpolate(frame, [duration - 5, duration], [0, 1], {
212
+ extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: Easing.bezier(0.5, 0, 0.6, 1),
213
+ });
214
+ const rootOpacity = 1 - outT;
215
+
216
+ return (
217
+ <AbsoluteFill style={{ backgroundColor: paper, opacity: rootOpacity }}>
218
+ {frame >= 84 ? (
219
+ <AbsoluteFill style={{ opacity: macroIn }}>
220
+ <PageCam
221
+ src="textures/live/projects-full.png"
222
+ pageH={PAGE_H}
223
+ keys={CAM_KEYS}
224
+ ease={PUSH_EASE}
225
+ dof={{ focusY: 240, strength: dofStrength }}
226
+ >
227
+ {/* rim light along the near (bottom) edge of the tilted plane */}
228
+ <div
229
+ style={{
230
+ position: 'absolute', left: 0, right: 0, bottom: 0, height: 8,
231
+ background: paint('rgba(255,255,255,0.85)'), filter: 'blur(6px)',
232
+ opacity: 0.6 * Math.min(1, lift + Math.max(0, (frame - 114) / 16)),
233
+ pointerEvents: 'none',
234
+ }}
235
+ />
236
+
237
+ {/* hero card: lifts off its slot, hovers, beam runs, settles back */}
238
+ <div style={{ transformStyle: 'preserve-3d' }}>
239
+ {/* vacated-slot patch + breathing amber outline (only when up) */}
240
+ {slotVis > 0.02 ? (
241
+ <div
242
+ style={{
243
+ position: 'absolute', left: CARD.x - 2, top: CARD.y - 2,
244
+ width: CARD.w + 4, height: CARD.h + 4, background: PATCH,
245
+ borderRadius: RADIUS,
246
+ boxShadow: paint(`inset 0 0 26px rgba(180,120,50,${0.12 * slotEdge})`),
247
+ opacity: slotVis,
248
+ }}
249
+ >
250
+ <div
251
+ style={{
252
+ position: 'absolute', inset: 0, borderRadius: RADIUS,
253
+ border: `1.5px solid ${SLOT_AMBER}`, opacity: slotEdge,
254
+ pointerEvents: 'none',
255
+ }}
256
+ />
257
+ </div>
258
+ ) : null}
259
+
260
+ {/* the levitating card */}
261
+ <div
262
+ style={{
263
+ position: 'absolute', left: CARD.x, top: CARD.y,
264
+ width: CARD.w, height: CARD.h,
265
+ transform: `translateZ(${z}px) scale(${press})`,
266
+ transformOrigin: 'center center',
267
+ transformStyle: 'preserve-3d',
268
+ }}
269
+ >
270
+ {/* card body (clipped) + top-light gradient */}
271
+ <div
272
+ style={{
273
+ position: 'absolute', inset: 0, borderRadius: RADIUS,
274
+ overflow: 'hidden',
275
+ boxShadow: landed ? 'none' : shadow,
276
+ }}
277
+ >
278
+ <Img
279
+ src={asset(`textures/live/${CARD.file}`)}
280
+ style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', display: 'block' }}
281
+ />
282
+ {/* 4x hi-res capture layered on top, laid out at plain CARD
283
+ size. Because PageCam now applies the push-in magnification
284
+ as a LAYOUT-scale CSS `zoom` (not a transform scale), this
285
+ image rasterizes at its enlarged device size (~CARD.w*zoom
286
+ px) and samples DOWN from the 1432px source — crisp — rather
287
+ than being downsampled to layout size and GPU-upscaled. */}
288
+ <Img
289
+ src={asset('textures/live/card4-hires.png')}
290
+ style={{
291
+ position: 'absolute',
292
+ inset: 0,
293
+ width: '100%',
294
+ height: '100%',
295
+ display: 'block',
296
+ opacity: hiresIn,
297
+ }}
298
+ />
299
+ {/* top-light sheen, like a card lit from above by the spot */}
300
+ <div
301
+ style={{
302
+ position: 'absolute', inset: 0,
303
+ background: paint('linear-gradient(160deg, rgba(255,255,255,0.5), transparent 40%)'),
304
+ opacity: lift, pointerEvents: 'none',
305
+ }}
306
+ />
307
+ </div>
308
+ {/* 1px white inner stroke (lit paper edge) */}
309
+ <div
310
+ style={{
311
+ position: 'absolute', inset: 0, borderRadius: RADIUS,
312
+ boxShadow: paint(`inset 0 0 0 1px rgba(255,255,255,${0.7 * lift})`),
313
+ pointerEvents: 'none',
314
+ }}
315
+ />
316
+
317
+ {/* perimeter beam: SVG rounded-rect, pathLength=1 travelling arc.
318
+ Lap 1 (fast, bright) then lap 2 (slow, weaker). */}
319
+ {(beam1On || beam2On) && lift > 0.4 ? (
320
+ <svg
321
+ width={bw}
322
+ height={bh}
323
+ viewBox={`0 0 ${bw} ${bh}`}
324
+ style={{
325
+ position: 'absolute', left: -3, top: -3, overflow: 'visible',
326
+ pointerEvents: 'none',
327
+ opacity: beam1On ? 1 : 0.62,
328
+ filter: paint(`drop-shadow(0 0 6px ${AMBER}) drop-shadow(0 0 18px rgba(255,240,210,0.55))`),
329
+ }}
330
+ >
331
+ {/* amber edge (wider) */}
332
+ <rect
333
+ x={2} y={2} width={bw - 4} height={bh - 4} rx={RADIUS} fill="none"
334
+ stroke={AMBER} strokeWidth={beam1On ? 5 : 3.5} strokeLinecap="round"
335
+ pathLength={1} strokeDasharray="0.14 1" strokeDashoffset={-(beam1On ? beam1Prog : beam2Prog)}
336
+ />
337
+ {/* warm-white core (narrower) */}
338
+ <rect
339
+ x={2} y={2} width={bw - 4} height={bh - 4} rx={RADIUS} fill="none"
340
+ stroke={BEAM_CORE} strokeWidth={beam1On ? 2.5 : 1.75} strokeLinecap="round"
341
+ pathLength={1} strokeDasharray="0.14 1" strokeDashoffset={-(beam1On ? beam1Prog : beam2Prog)}
342
+ />
343
+ </svg>
344
+ ) : null}
345
+
346
+ {/* faint amber ring left behind, fades out */}
347
+ {beamTrail > 0.01 ? (
348
+ <div
349
+ style={{
350
+ position: 'absolute', inset: -3, borderRadius: RADIUS + 3,
351
+ border: `1.5px solid ${AMBER}`, opacity: beamTrail,
352
+ pointerEvents: 'none',
353
+ }}
354
+ />
355
+ ) : null}
356
+ </div>
357
+ </div>
358
+
359
+ {/* 3D floating annotation LEFT of the hovering card: big serif note
360
+ lifted off the page plane (translateZ), seen through the same
361
+ oblique camera; a marker highlight sweeps under the key words. */}
362
+ {frame >= 142 && frame <= 212 ? (() => {
363
+ const noteIn = interpolate(frame, [142, 152], [0, 1], {
364
+ extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: Easing.bezier(0.2, 0.75, 0.3, 1),
365
+ });
366
+ const noteOut = interpolate(frame, [198, 208], [1, 0], {
367
+ extrapolateLeft: 'clamp', extrapolateRight: 'clamp',
368
+ });
369
+ const noteVis = noteIn * noteOut;
370
+ // gentle bob in sympathy with the card's hover
371
+ const noteZ = 92 + Math.sin(((frame - 142) / 44) * Math.PI * 2) * 3;
372
+ const hl = interpolate(frame, [156, 168], [0, 1], {
373
+ extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: Easing.bezier(0.3, 0, 0.2, 1),
374
+ });
375
+ return (
376
+ <div style={{ transformStyle: 'preserve-3d', pointerEvents: 'none' }}>
377
+ {/* soft shadow the note casts onto the page */}
378
+ <div
379
+ style={{
380
+ position: 'absolute', left: 566, top: 736, width: 210, height: 74,
381
+ transform: 'translateZ(2px)',
382
+ background: paint('radial-gradient(ellipse at 50% 50%, rgba(40,30,20,0.3), transparent 70%)'),
383
+ filter: 'blur(12px)',
384
+ opacity: 0.55 * noteVis,
385
+ }}
386
+ />
387
+ <div
388
+ style={{
389
+ position: 'absolute', left: 556, top: 668, width: 230,
390
+ ...(!paperStyle ? {background: theme.page, boxShadow: `0 0 0 14px ${theme.page}`, borderRadius: 7} : {}),
391
+ transform: `translateZ(${noteZ}px) translateY(${(1 - noteIn) * 26}px)`,
392
+ opacity: noteVis,
393
+ filter: `blur(${(1 - noteIn) * 4}px)`,
394
+ }}
395
+ >
396
+ <div
397
+ style={{
398
+ fontFamily: SERIF, fontSize: noteSize, fontWeight: 600, color: INK,
399
+ lineHeight: 1.16, letterSpacing: '-0.012em',
400
+ }}
401
+ >
402
+ {noteLine1}
403
+ </div>
404
+ <div style={{ position: 'relative', display: 'inline-block' }}>
405
+ {/* marker highlight sweeping in behind the words */}
406
+ <div
407
+ style={{
408
+ position: 'absolute', left: -5, top: '12%', bottom: '4%',
409
+ width: `calc(${hl} * (100% + 10px))`,
410
+ background: paint('oklch(88% 0.095 85)'),
411
+ borderRadius: 4,
412
+ }}
413
+ />
414
+ <div
415
+ style={{
416
+ position: 'relative',
417
+ fontFamily: SERIF, fontStyle: paperStyle ? 'italic' : 'normal', fontSize: noteSize, fontWeight: 600,
418
+ color: INK, lineHeight: 1.16, letterSpacing: '-0.012em',
419
+ }}
420
+ >
421
+ {noteLine2}
422
+ </div>
423
+ </div>
424
+ </div>
425
+ </div>
426
+ );
427
+ })() : null}
428
+ </PageCam>
429
+
430
+ {/* roving / locking spotlight: warm pool + dim outside so it reads */}
431
+ <AbsoluteFill
432
+ style={{
433
+ background: paint(`radial-gradient(${poolRx}px ${poolRy}px at ${spotX}% ${spotY}%, rgba(255,241,214,0.42), rgba(255,241,214,0.10) 45%, rgba(70,56,38,${vignette * spotOn}) 100%)`),
434
+ pointerEvents: 'none',
435
+ opacity: paperStyle ? spotOn : spotOn * 0.18,
436
+ }}
437
+ />
438
+ {/* faint trailing bounce of the same light */}
439
+ <AbsoluteFill
440
+ style={{
441
+ background: paint(`radial-gradient(300px 220px at ${spotX - 6}% ${spotY + 10}%, rgba(255,246,228,0.18), transparent 70%)`),
442
+ pointerEvents: 'none',
443
+ opacity: paperStyle ? spotOn * 0.7 : 0,
444
+ }}
445
+ />
446
+ </AbsoluteFill>
447
+ ) : null}
448
+
449
+ {/* brand group: crosshair + wordmark + kicker, dissolves out by frame 53 */}
450
+ {brandOpacity > 0 ? (
451
+ <AbsoluteFill style={{ justifyContent: 'center', alignItems: 'center', pointerEvents: 'none', opacity: brandOpacity }}>
452
+ <div
453
+ style={{
454
+ textAlign: 'center',
455
+ transform: `translateY(${groupY}px) scale(${groupScale})`,
456
+ transformOrigin: 'center center',
457
+ }}
458
+ >
459
+ {/* crosshair drawn by an invisible amber pen */}
460
+ <svg
461
+ width={64}
462
+ height={64}
463
+ viewBox="0 0 64 64"
464
+ style={{ display: 'block', margin: '0 auto 34px', opacity: crossFade }}
465
+ >
466
+ <line
467
+ x1={32} y1={2} x2={32} y2={62}
468
+ stroke={AMBER} strokeWidth={5} strokeLinecap="round"
469
+ pathLength={100} strokeDasharray={100} strokeDashoffset={vDraw}
470
+ />
471
+ <line
472
+ x1={2} y1={32} x2={62} y2={32}
473
+ stroke={AMBER} strokeWidth={5} strokeLinecap="round"
474
+ pathLength={100} strokeDasharray={100} strokeDashoffset={hDraw}
475
+ />
476
+ </svg>
477
+
478
+ {/* wordmark: glyph-by-glyph letterpress with amber under-glint */}
479
+ <div
480
+ style={{
481
+ fontFamily: SERIF, fontSize: wordmarkSize, fontWeight: 600, color: INK,
482
+ letterSpacing: '-0.01em', lineHeight: 1, whiteSpace: 'pre',
483
+ display: 'inline-flex', alignItems: 'flex-end',
484
+ }}
485
+ >
486
+ {WORDMARK.split('').map((ch, i) => {
487
+ const delay = 10 + i * (paperStyle ? 3 : 1.5);
488
+ const t = interpolate(frame, [delay, delay + 12], [0, 1], {
489
+ extrapolateLeft: 'clamp', extrapolateRight: 'clamp',
490
+ easing: Easing.bezier(0.2, 0.7, 0.25, 1),
491
+ });
492
+ const glintCenter = delay + 12;
493
+ const glint = interpolate(frame, [glintCenter - 4, glintCenter, glintCenter + 4], [0, 1, 0], {
494
+ extrapolateLeft: 'clamp', extrapolateRight: 'clamp',
495
+ });
496
+ return (
497
+ <span
498
+ key={i}
499
+ style={{
500
+ position: 'relative',
501
+ display: 'inline-block',
502
+ opacity: t,
503
+ transform: `scale(${1.6 - 0.6 * t})`,
504
+ transformOrigin: 'center bottom',
505
+ filter: `blur(${(1 - t) * 6}px)`,
506
+ }}
507
+ >
508
+ {ch === ' ' ? ' ' : ch}
509
+ <span
510
+ style={{
511
+ position: 'absolute',
512
+ left: '50%',
513
+ bottom: -6,
514
+ transform: 'translateX(-50%)',
515
+ width: `${glint * 100}%`,
516
+ height: 2,
517
+ background: AMBER,
518
+ opacity: glint,
519
+ borderRadius: 2,
520
+ }}
521
+ />
522
+ </span>
523
+ );
524
+ })}
525
+ </div>
526
+
527
+ {/* mono kicker typewriter + amber block cursor */}
528
+ <div
529
+ style={{
530
+ fontFamily: MONO, fontSize: kickerSize, letterSpacing: '0.14em', color: INK2,
531
+ marginTop: 30, textTransform: 'uppercase', height: 30,
532
+ display: 'flex', justifyContent: 'center', alignItems: 'center',
533
+ }}
534
+ >
535
+ <span style={{ whiteSpace: 'pre' }}>{KICKER.slice(0, kickChars)}</span>
536
+ <span
537
+ style={{
538
+ display: 'inline-block',
539
+ width: 14,
540
+ height: 24,
541
+ marginLeft: 4,
542
+ background: AMBER,
543
+ opacity: cursorOn ? 0.85 : 0,
544
+ }}
545
+ />
546
+ </div>
547
+ </div>
548
+ </AbsoluteFill>
549
+ ) : null}
550
+ </AbsoluteFill>
551
+ );
552
+ };