dsh-drawai 0.4.2 → 0.4.3

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 (366) hide show
  1. package/README.md +4 -3
  2. package/lib/index.js +7777 -7719
  3. package/lib/motion/asset-pack.js +394 -394
  4. package/lib/motion/assets.js +291 -291
  5. package/lib/motion/demo-assets-lock.js +10 -10
  6. package/lib/motion/mgstyles/vendor/README.md +28 -28
  7. package/lib/motion/mgstyles/vendor/modules/d3/LICENSE +13 -13
  8. package/lib/motion/mgstyles/vendor/modules/d3/dist/d3.min.js +2 -2
  9. package/lib/motion/mgstyles/vendor/modules/opentype.js/LICENSE +20 -20
  10. package/lib/motion/mgstyles/vendor/modules/opentype.js/dist/opentype.mjs +14460 -14460
  11. package/lib/motion/mgstyles/vendor/modules/simplex-noise/LICENSE +21 -21
  12. package/lib/motion/mgstyles/vendor/modules/simplex-noise/dist/esm/simplex-noise.js +465 -465
  13. package/lib/motion/mgstyles/vendor/modules/topojson-client/LICENSE +13 -13
  14. package/lib/motion/mgstyles/vendor/modules/topojson-client/dist/topojson-client.min.js +2 -2
  15. package/lib/motion/render.js +179 -179
  16. package/lib/motion/shotcraft/upstream/SKILL.md +286 -286
  17. package/lib/motion/shotcraft/upstream/assets/lib/Caption.tsx +54 -54
  18. package/lib/motion/shotcraft/upstream/assets/lib/ClipCard.tsx +200 -200
  19. package/lib/motion/shotcraft/upstream/assets/lib/DigitRoll.tsx +45 -45
  20. package/lib/motion/shotcraft/upstream/assets/lib/FlashCut.tsx +20 -20
  21. package/lib/motion/shotcraft/upstream/assets/lib/FlatPanel.tsx +65 -65
  22. package/lib/motion/shotcraft/upstream/assets/lib/PageCam.tsx +151 -151
  23. package/lib/motion/shotcraft/upstream/assets/lib/VerticalTicker.tsx +129 -129
  24. package/lib/motion/shotcraft/upstream/assets/lib/helpers/camera.tsx +75 -75
  25. package/lib/motion/shotcraft/upstream/assets/lib/helpers/motion.ts +40 -40
  26. package/lib/motion/shotcraft/upstream/assets/lib/helpers/rand.ts +11 -11
  27. package/lib/motion/shotcraft/upstream/assets/lib/helpers/shake.ts +10 -10
  28. package/lib/motion/shotcraft/upstream/assets/scripts/build-readme-cover.mjs +49 -49
  29. package/lib/motion/shotcraft/upstream/assets/scripts/capture-template.mjs +210 -210
  30. package/lib/motion/shotcraft/upstream/assets/scripts/sfx-source.py +423 -423
  31. package/lib/motion/shotcraft/upstream/assets/scripts/smoke-render-demos.py +170 -170
  32. package/lib/motion/shotcraft/upstream/assets/scripts/test_sfx_source.py +157 -157
  33. package/lib/motion/shotcraft/upstream/demos/README.md +120 -120
  34. package/lib/motion/shotcraft/upstream/demos/_fixtures/Brand.tsx +112 -112
  35. package/lib/motion/shotcraft/upstream/demos/_fixtures/Fixtures.tsx +647 -647
  36. package/lib/motion/shotcraft/upstream/demos/_fixtures/Look.tsx +316 -316
  37. package/lib/motion/shotcraft/upstream/demos/_fixtures/Motion.tsx +82 -82
  38. package/lib/motion/shotcraft/upstream/demos/_fixtures/PageCam2D.tsx +115 -115
  39. package/lib/motion/shotcraft/upstream/demos/_fixtures/Polish.tsx +243 -243
  40. package/lib/motion/shotcraft/upstream/demos/_textures/live-layout.json +388 -388
  41. package/lib/motion/shotcraft/upstream/demos/camera/basic-3d-scene/Basic3DScene.tsx +250 -250
  42. package/lib/motion/shotcraft/upstream/demos/camera/crash-zoom-punch/CrashImpactReal.tsx +305 -305
  43. package/lib/motion/shotcraft/upstream/demos/camera/crash-zoom-punch/CrashZoomReal.tsx +246 -246
  44. package/lib/motion/shotcraft/upstream/demos/camera/cursor-flyover/CursorFlyover.tsx +390 -390
  45. package/lib/motion/shotcraft/upstream/demos/camera/depth-layer-moves/MultiplaneReal.tsx +138 -138
  46. package/lib/motion/shotcraft/upstream/demos/camera/graze-face-tour/GrazeFaceTour.tsx +542 -542
  47. package/lib/motion/shotcraft/upstream/demos/camera/overhead-camera-moves/OverheadTabletopDrop.tsx +315 -315
  48. package/lib/motion/shotcraft/upstream/demos/camera/overhead-camera-moves/TiltReveal.tsx +299 -299
  49. package/lib/motion/shotcraft/upstream/demos/camera/space-camera-moves/DroneDiveLanding.tsx +173 -173
  50. package/lib/motion/shotcraft/upstream/demos/camera/space-camera-moves/ExplodedView.tsx +348 -348
  51. package/lib/motion/shotcraft/upstream/demos/camera/steep-tilt-glide/SteepTiltGlide.tsx +366 -366
  52. package/lib/motion/shotcraft/upstream/demos/camera/tension-camera-moves/BulletTimeFreezeOrbit.tsx +279 -279
  53. package/lib/motion/shotcraft/upstream/demos/camera/tension-camera-moves/PullBackIsolation.tsx +184 -184
  54. package/lib/motion/shotcraft/upstream/demos/camera/terminal-3d/Terminal3D.tsx +390 -390
  55. package/lib/motion/shotcraft/upstream/demos/data/before-after-slider-scrub/BeforeAfterSliderScrub.tsx +155 -155
  56. package/lib/motion/shotcraft/upstream/demos/data/chart-live-moves/AxisRescaleShockV2.tsx +303 -303
  57. package/lib/motion/shotcraft/upstream/demos/data/chart-live-moves/OscilloscopeStreamV2.tsx +290 -290
  58. package/lib/motion/shotcraft/upstream/demos/data/chart-live-moves/UnitDotSwarmRegroupV2.tsx +278 -278
  59. package/lib/motion/shotcraft/upstream/demos/data/gauge-readout-moves/NeedleSweepSelftest.tsx +307 -307
  60. package/lib/motion/shotcraft/upstream/demos/data/gauge-readout-moves/TapeScrollFixedPointer.tsx +215 -215
  61. package/lib/motion/shotcraft/upstream/demos/data/scroll-brake-moves/BrakeReticleLock.tsx +299 -299
  62. package/lib/motion/shotcraft/upstream/demos/data/scroll-brake-moves/ChangelogScrollBrake.tsx +232 -232
  63. package/lib/motion/shotcraft/upstream/demos/data/timeline-travel/TimelineTravel.tsx +290 -290
  64. package/lib/motion/shotcraft/upstream/demos/effects/assemble-then-type-flyin/AssembleThenTypeFlyin.tsx +273 -273
  65. package/lib/motion/shotcraft/upstream/demos/effects/aurora-bloom-bg-flip/AuroraBloomBgFlip.tsx +167 -167
  66. package/lib/motion/shotcraft/upstream/demos/effects/brand-frame-snap/BrandFrameSnap.tsx +536 -536
  67. package/lib/motion/shotcraft/upstream/demos/effects/fui-hud-moves/ReticleLockOn.tsx +164 -164
  68. package/lib/motion/shotcraft/upstream/demos/effects/glow-flyline-moves/FlylineArc.tsx +175 -175
  69. package/lib/motion/shotcraft/upstream/demos/effects/glow-flyline-moves/GlowOrbAmbient.tsx +197 -197
  70. package/lib/motion/shotcraft/upstream/demos/effects/glow-flyline-moves/OrbFlylineRelay.tsx +241 -241
  71. package/lib/motion/shotcraft/upstream/demos/effects/icon-performance-moves/AttentionBounce.tsx +279 -279
  72. package/lib/motion/shotcraft/upstream/demos/effects/impact-feedback/AnimeImpact.tsx +230 -230
  73. package/lib/motion/shotcraft/upstream/demos/effects/impact-feedback/HitCounter.tsx +345 -345
  74. package/lib/motion/shotcraft/upstream/demos/effects/light-play-moves/SheenSweepRetry.tsx +275 -275
  75. package/lib/motion/shotcraft/upstream/demos/effects/light-play-moves/SpotlightSweepReveal.tsx +229 -229
  76. package/lib/motion/shotcraft/upstream/demos/effects/radial-ripple-phone-chips/RadialRipplePhoneChips.tsx +312 -312
  77. package/lib/motion/shotcraft/upstream/demos/effects/riso-print-hits/RisoMisregistrationHit.tsx +222 -222
  78. package/lib/motion/shotcraft/upstream/demos/effects/scan-bracket-sweep/ScanBracketSweep.tsx +295 -295
  79. package/lib/motion/shotcraft/upstream/demos/effects/scanline-annotate-focus/ScanlineAnnotateFocus.tsx +329 -329
  80. package/lib/motion/shotcraft/upstream/demos/effects/scanline-assemble-flyin/ScanlineAssembleFlyin.tsx +315 -315
  81. package/lib/motion/shotcraft/upstream/demos/effects/slam-entrance-moves/ImpactBurstKit.tsx +264 -264
  82. package/lib/motion/shotcraft/upstream/demos/effects/slam-entrance-moves/KanadaPerspectiveSnap.tsx +260 -260
  83. package/lib/motion/shotcraft/upstream/demos/effects/slam-entrance-moves/ScoreSlam.tsx +263 -263
  84. package/lib/motion/shotcraft/upstream/demos/effects/spotlight-sweep-moves/CornerSpotlightReveal.tsx +123 -123
  85. package/lib/motion/shotcraft/upstream/demos/effects/spotlight-sweep-moves/GlowWakeSleepPanel.tsx +187 -187
  86. package/lib/motion/shotcraft/upstream/demos/effects/spotlight-sweep-moves/SlideSpotlightPan.tsx +175 -175
  87. package/lib/motion/shotcraft/upstream/demos/interaction/autolayout-gap-dial/AutolayoutGapDial.tsx +305 -305
  88. package/lib/motion/shotcraft/upstream/demos/interaction/canvas-materialize-moves/DiagramCascadeBuild.tsx +350 -350
  89. package/lib/motion/shotcraft/upstream/demos/interaction/canvas-materialize-moves/PanelToCanvasMaterialize.tsx +352 -352
  90. package/lib/motion/shotcraft/upstream/demos/interaction/chip-grid-single-select-blackout/ChipGridSingleSelectBlackout.tsx +218 -218
  91. package/lib/motion/shotcraft/upstream/demos/interaction/collab-cursor-moves/CursorCastEnsemble.tsx +297 -297
  92. package/lib/motion/shotcraft/upstream/demos/interaction/glass-pill-dictation-typing/GlassPillDictationTyping.tsx +157 -157
  93. package/lib/motion/shotcraft/upstream/demos/interaction/hashtag-to-pill-materialize/HashtagToPillMaterialize.tsx +218 -218
  94. package/lib/motion/shotcraft/upstream/demos/interaction/picker-carousel-feature-cycle/PickerCarouselFeatureCycle.tsx +202 -202
  95. package/lib/motion/shotcraft/upstream/demos/interaction/segmented-thumb-hero/SegmentedThumbHero.tsx +265 -265
  96. package/lib/motion/shotcraft/upstream/demos/interaction/theme-switch-moves/PaletteThemeRipple.tsx +457 -457
  97. package/lib/motion/shotcraft/upstream/demos/interaction/theme-switch-moves/ThemeSweepToggle.tsx +446 -446
  98. package/lib/motion/shotcraft/upstream/demos/interaction/type-and-filter/TypeAndFilter.tsx +249 -249
  99. package/lib/motion/shotcraft/upstream/demos/interaction/voice-waveform-live/VoiceWaveformLive.tsx +278 -278
  100. package/lib/motion/shotcraft/upstream/demos/opening/crane-rise-reveal/CraneRiseReveal.tsx +332 -332
  101. package/lib/motion/shotcraft/upstream/demos/opening/dataviz-landscape-open/DatavizLandscapeOpen.tsx +358 -358
  102. package/lib/motion/shotcraft/upstream/demos/opening/fracture/Fracture.tsx +201 -201
  103. package/lib/motion/shotcraft/upstream/demos/opening/icon-field-colorize/IconFieldColorize.tsx +200 -200
  104. package/lib/motion/shotcraft/upstream/demos/opening/letterspace-materialize/LetterspaceMaterialize.tsx +208 -208
  105. package/lib/motion/shotcraft/upstream/demos/opening/magician-card-flourish/MagicianCardFlourish.tsx +341 -341
  106. package/lib/motion/shotcraft/upstream/demos/opening/orbit-ring-title-open/OrbitRingTitleOpen.tsx +389 -389
  107. package/lib/motion/shotcraft/upstream/demos/opening/spotlight-hero-card/SpotlightHeroCard.tsx +293 -293
  108. package/lib/motion/shotcraft/upstream/demos/opening/stroke-segment-build/StrokeSegmentBuild.tsx +183 -183
  109. package/lib/motion/shotcraft/upstream/demos/opening/text-as-mask/TextAsMask.tsx +245 -245
  110. package/lib/motion/shotcraft/upstream/demos/outro/grain-dissolve/GrainDissolve.tsx +222 -222
  111. package/lib/motion/shotcraft/upstream/demos/outro/logo-shrink-wordmark-lockup/LogoShrinkWordmarkLockup.tsx +223 -223
  112. package/lib/motion/shotcraft/upstream/demos/outro/neon-triple-marquee/NeonTripleMarquee.tsx +217 -217
  113. package/lib/motion/shotcraft/upstream/demos/outro/outro-group-photo-launch/OutroGroupPhotoLaunch.tsx +343 -343
  114. package/lib/motion/shotcraft/upstream/demos/outro/ui-strip-away-outro/UiStripAwayOutro.tsx +357 -357
  115. package/lib/motion/shotcraft/upstream/demos/outro/ui-to-brand-morph/IconFlipBloomLogo.tsx +187 -187
  116. package/lib/motion/shotcraft/upstream/demos/outro/ui-to-brand-morph/InputMorphsIntoLogo.tsx +333 -333
  117. package/lib/motion/shotcraft/upstream/demos/rhythm/beat-cut-moves/PaparazziFlash.tsx +274 -274
  118. package/lib/motion/shotcraft/upstream/demos/rhythm/beat-step-list-theme-cycle/BeatStepListThemeCycle.tsx +270 -270
  119. package/lib/motion/shotcraft/upstream/demos/rhythm/panel-grid-moves/ComicPanelSplit.tsx +430 -430
  120. package/lib/motion/shotcraft/upstream/demos/rhythm/quad-split-parallel-scenes/QuadSplitParallelScenes.tsx +252 -252
  121. package/lib/motion/shotcraft/upstream/demos/rhythm/rhythm-interrupt-moves/JumpCutPunchIn.tsx +106 -106
  122. package/lib/motion/shotcraft/upstream/demos/rhythm/sakuga-timing-shift/SakugaTimingShift.tsx +288 -288
  123. package/lib/motion/shotcraft/upstream/demos/rhythm/speed-ramp-freeze/FreezeAnnotateReal.tsx +362 -362
  124. package/lib/motion/shotcraft/upstream/demos/rhythm/trailer-grammar-moves/TrailerBumper.tsx +168 -168
  125. package/lib/motion/shotcraft/upstream/demos/transition/bottom-push-stack-wipe/BottomPushStackWipe.tsx +310 -310
  126. package/lib/motion/shotcraft/upstream/demos/transition/card-flip-reveal/CardFlipReveal.tsx +261 -261
  127. package/lib/motion/shotcraft/upstream/demos/transition/card-flock-tumble/CardFlockTumble.tsx +459 -459
  128. package/lib/motion/shotcraft/upstream/demos/transition/circle-match-iris/CircleMatchIris.tsx +294 -294
  129. package/lib/motion/shotcraft/upstream/demos/transition/gradient-transition/GradientTransition.tsx +161 -161
  130. package/lib/motion/shotcraft/upstream/demos/transition/line-carry-transition/LineCarryTransition.tsx +248 -248
  131. package/lib/motion/shotcraft/upstream/demos/transition/mosaic-reframe/MosaicReframe.tsx +359 -359
  132. package/lib/motion/shotcraft/upstream/demos/transition/page-turn-transitions/BarnDoorSplit.tsx +227 -227
  133. package/lib/motion/shotcraft/upstream/demos/transition/print-texture-transitions/InkBleedReveal.tsx +162 -162
  134. package/lib/motion/shotcraft/upstream/demos/transition/shot-transitions/PortalWipeV2.tsx +248 -248
  135. package/lib/motion/shotcraft/upstream/demos/transition/shot-transitions/WhipBrakeReal.tsx +186 -186
  136. package/lib/motion/shotcraft/upstream/demos/transition/tear-streak-transitions/GlitchDisplace.tsx +355 -355
  137. package/lib/motion/shotcraft/upstream/demos/transition/transition-hidden-cut/InvisibleCut.tsx +272 -272
  138. package/lib/motion/shotcraft/upstream/demos/transition/transition-hidden-cut/LightLeakBurn.tsx +262 -262
  139. package/lib/motion/shotcraft/upstream/demos/transition/transition-hidden-cut/VersusSlam.tsx +217 -217
  140. package/lib/motion/shotcraft/upstream/demos/transition/transition-travel/LetterformZoom.tsx +285 -285
  141. package/lib/motion/shotcraft/upstream/demos/transition/white-flash-logo-simplify-cut/WhiteFlashLogoSimplifyCut.tsx +205 -205
  142. package/lib/motion/shotcraft/upstream/demos/transition/wipe-transitions/BlindsSlice.tsx +178 -178
  143. package/lib/motion/shotcraft/upstream/demos/typography/blur-slide/BlurSlide.tsx +147 -147
  144. package/lib/motion/shotcraft/upstream/demos/typography/brace-expand/BraceExpand.tsx +143 -143
  145. package/lib/motion/shotcraft/upstream/demos/typography/brand-ink-open/BrandInkOpen.tsx +134 -134
  146. package/lib/motion/shotcraft/upstream/demos/typography/countdown-arc-scatter/CountdownArcScatter.tsx +217 -217
  147. package/lib/motion/shotcraft/upstream/demos/typography/flying-words/FlyingWords.tsx +198 -198
  148. package/lib/motion/shotcraft/upstream/demos/typography/glitch-cycle/GlitchCycle.tsx +221 -221
  149. package/lib/motion/shotcraft/upstream/demos/typography/gradient-word-sweep/GradientWordSweep.tsx +312 -312
  150. package/lib/motion/shotcraft/upstream/demos/typography/lead-word-zoom-assemble/LeadWordZoomAssemble.tsx +261 -261
  151. package/lib/motion/shotcraft/upstream/demos/typography/marker-underline-title/MarkerUnderlineTitle.tsx +121 -121
  152. package/lib/motion/shotcraft/upstream/demos/typography/outline-word-fill/OutlineWordFill.tsx +153 -153
  153. package/lib/motion/shotcraft/upstream/demos/typography/paper-title-card/PaperTitleCard.tsx +179 -179
  154. package/lib/motion/shotcraft/upstream/demos/typography/pill-chip-slot-cycle-handled/PillChipSlotCycleHandled.tsx +140 -140
  155. package/lib/motion/shotcraft/upstream/demos/typography/pill-slot-cycle/PillSlotCycle.tsx +309 -309
  156. package/lib/motion/shotcraft/upstream/demos/typography/text-column-converge/TextColumnConverge.tsx +191 -191
  157. package/lib/motion/shotcraft/upstream/demos/typography/title-demote-to-label/TitleDemoteToLabel.tsx +292 -292
  158. package/lib/motion/shotcraft/upstream/demos/typography/type-assembly-moves/LetterformDriftAssembly.tsx +135 -135
  159. package/lib/motion/shotcraft/upstream/demos/typography/type-assembly-moves/SplitTextStagger.tsx +124 -124
  160. package/lib/motion/shotcraft/upstream/demos/typography/type-assembly-moves/TextOnPath.tsx +255 -255
  161. package/lib/motion/shotcraft/upstream/demos/typography/type-entrance-moves/LetterDropPhysics.tsx +173 -173
  162. package/lib/motion/shotcraft/upstream/demos/typography/type-rhythm-sync/FontWeightPump.tsx +225 -225
  163. package/lib/motion/shotcraft/upstream/demos/typography/type-rhythm-sync/KaraokeFillSync.tsx +141 -141
  164. package/lib/motion/shotcraft/upstream/demos/typography/typewriter-moves/TypewriterErrorRetype.tsx +137 -137
  165. package/lib/motion/shotcraft/upstream/demos/typography/vertical-word-roll-blur-cycle/VerticalWordRollBlurCycle.tsx +147 -147
  166. package/lib/motion/shotcraft/upstream/demos/typography/word-relay-filmstrip/WordRelayFilmstrip.tsx +409 -409
  167. package/lib/motion/shotcraft/upstream/demos/typography/word-relay-geometry/WordRelayGeometry.tsx +280 -280
  168. package/lib/motion/shotcraft/upstream/demos/ui-entrance/avatar-bracket-carousel/AvatarBracketCarousel.tsx +271 -271
  169. package/lib/motion/shotcraft/upstream/demos/ui-entrance/bezier-source-converge-merge/BezierSourceConvergeMerge.tsx +385 -385
  170. package/lib/motion/shotcraft/upstream/demos/ui-entrance/card-stack/CardStack.tsx +312 -312
  171. package/lib/motion/shotcraft/upstream/demos/ui-entrance/carousel-3d/Carousel3D.tsx +303 -303
  172. package/lib/motion/shotcraft/upstream/demos/ui-entrance/clipcard-looping/ClipCardLooping.tsx +77 -77
  173. package/lib/motion/shotcraft/upstream/demos/ui-entrance/cloner-depth-echo/ClonerDepthEcho.tsx +311 -311
  174. package/lib/motion/shotcraft/upstream/demos/ui-entrance/deck-deal-flyin/DeckDealFlyin.tsx +307 -307
  175. package/lib/motion/shotcraft/upstream/demos/ui-entrance/doc-park-left-pill-deal/DocParkLeftPillDeal.tsx +349 -349
  176. package/lib/motion/shotcraft/upstream/demos/ui-entrance/document-typewriter-reveal/DocumentTypewriterReveal.tsx +255 -255
  177. package/lib/motion/shotcraft/upstream/demos/ui-entrance/draw-svg-trace/DrawSvgTrace.tsx +244 -244
  178. package/lib/motion/shotcraft/upstream/demos/ui-entrance/element-body-moves/ContactShadowLift.tsx +217 -217
  179. package/lib/motion/shotcraft/upstream/demos/ui-entrance/integration-hub-map/IntegrationHubMap.tsx +652 -652
  180. package/lib/motion/shotcraft/upstream/demos/ui-entrance/list-reveal/ListReveal.tsx +153 -153
  181. package/lib/motion/shotcraft/upstream/demos/ui-entrance/list-stack-press/ListStackPress.tsx +238 -238
  182. package/lib/motion/shotcraft/upstream/demos/ui-entrance/neon-frame-forerun/NeonFrameForerun.tsx +324 -324
  183. package/lib/motion/shotcraft/upstream/demos/ui-entrance/neon-frame-orbit-drop/NeonFrameForerunOrbit.tsx +354 -354
  184. package/lib/motion/shotcraft/upstream/demos/ui-entrance/page-waterfall-wall/PageWaterfallWall.tsx +101 -101
  185. package/lib/motion/shotcraft/upstream/demos/ui-entrance/page-waterfall-wall/VerticalTicker.tsx +129 -129
  186. package/lib/motion/shotcraft/upstream/demos/ui-entrance/product-card-progressive-assemble/ProductCardProgressiveAssemble.tsx +297 -297
  187. package/lib/motion/shotcraft/upstream/demos/ui-entrance/radial-wave/RadialWave.tsx +57 -57
  188. package/lib/motion/shotcraft/upstream/demos/ui-entrance/research-card-stack-scroll/ResearchCardStackScroll.tsx +155 -155
  189. package/lib/motion/shotcraft/upstream/demos/ui-entrance/row-embed/RowEmbed.tsx +175 -175
  190. package/lib/motion/shotcraft/upstream/demos/ui-entrance/value-stagger-gradient/ValueStaggerGradient.tsx +153 -153
  191. package/lib/motion/shotcraft/upstream/demos/ui-entrance/wall-reveal-moves/BentoLightUp.tsx +146 -146
  192. package/lib/motion/shotcraft/upstream/demos/ui-entrance/wall-reveal-moves/WireframeDrawOn.tsx +226 -226
  193. package/lib/motion/shotcraft/upstream/gallery/api/library.json +1 -1
  194. package/lib/motion/shotcraft/upstream/references/aesthetic-rules.md +193 -193
  195. package/lib/motion/shotcraft/upstream/references/final-review.md +121 -121
  196. package/lib/motion/shotcraft/upstream/references/guided-free-creation.md +199 -199
  197. package/lib/motion/shotcraft/upstream/references/jianying-export.md +183 -183
  198. package/lib/motion/shotcraft/upstream/references/music-beat-sync.md +257 -257
  199. package/lib/motion/shotcraft/upstream/references/pipeline.md +382 -382
  200. package/lib/motion/shotcraft/upstream/references/publish-upload-post.md +111 -111
  201. package/lib/motion/shotcraft/upstream/references/sequences/promo-energy-arc.md +50 -50
  202. package/lib/motion/shotcraft/upstream/references/sfx-sourcing.md +95 -95
  203. package/lib/motion/shotcraft/upstream/references/shots/camera/basic-3d-scene.md +41 -41
  204. package/lib/motion/shotcraft/upstream/references/shots/camera/crash-zoom-punch.md +52 -52
  205. package/lib/motion/shotcraft/upstream/references/shots/camera/cursor-flyover.md +39 -39
  206. package/lib/motion/shotcraft/upstream/references/shots/camera/depth-layer-moves.md +39 -39
  207. package/lib/motion/shotcraft/upstream/references/shots/camera/graze-face-tour.md +57 -57
  208. package/lib/motion/shotcraft/upstream/references/shots/camera/overhead-camera-moves.md +51 -51
  209. package/lib/motion/shotcraft/upstream/references/shots/camera/space-camera-moves.md +46 -46
  210. package/lib/motion/shotcraft/upstream/references/shots/camera/steep-tilt-glide.md +52 -52
  211. package/lib/motion/shotcraft/upstream/references/shots/camera/tension-camera-moves.md +43 -43
  212. package/lib/motion/shotcraft/upstream/references/shots/camera/terminal-3d.md +42 -42
  213. package/lib/motion/shotcraft/upstream/references/shots/data/before-after-slider-scrub.md +48 -48
  214. package/lib/motion/shotcraft/upstream/references/shots/data/chart-live-moves.md +52 -52
  215. package/lib/motion/shotcraft/upstream/references/shots/data/gauge-readout-moves.md +45 -45
  216. package/lib/motion/shotcraft/upstream/references/shots/data/scroll-brake-moves.md +46 -46
  217. package/lib/motion/shotcraft/upstream/references/shots/data/timeline-travel.md +44 -44
  218. package/lib/motion/shotcraft/upstream/references/shots/effects/assemble-then-type-flyin.md +63 -63
  219. package/lib/motion/shotcraft/upstream/references/shots/effects/aurora-bloom-bg-flip.md +54 -54
  220. package/lib/motion/shotcraft/upstream/references/shots/effects/brand-frame-snap.md +58 -58
  221. package/lib/motion/shotcraft/upstream/references/shots/effects/fui-hud-moves.md +35 -35
  222. package/lib/motion/shotcraft/upstream/references/shots/effects/glow-flyline-moves.md +53 -53
  223. package/lib/motion/shotcraft/upstream/references/shots/effects/icon-performance-moves.md +42 -42
  224. package/lib/motion/shotcraft/upstream/references/shots/effects/impact-feedback.md +50 -50
  225. package/lib/motion/shotcraft/upstream/references/shots/effects/light-play-moves.md +46 -46
  226. package/lib/motion/shotcraft/upstream/references/shots/effects/radial-ripple-phone-chips.md +56 -56
  227. package/lib/motion/shotcraft/upstream/references/shots/effects/riso-print-hits.md +49 -49
  228. package/lib/motion/shotcraft/upstream/references/shots/effects/scan-bracket-sweep.md +50 -50
  229. package/lib/motion/shotcraft/upstream/references/shots/effects/scanline-annotate-focus.md +53 -53
  230. package/lib/motion/shotcraft/upstream/references/shots/effects/scanline-assemble-flyin.md +53 -53
  231. package/lib/motion/shotcraft/upstream/references/shots/effects/slam-entrance-moves.md +55 -55
  232. package/lib/motion/shotcraft/upstream/references/shots/effects/spotlight-sweep-moves.md +52 -52
  233. package/lib/motion/shotcraft/upstream/references/shots/interaction/autolayout-gap-dial.md +60 -60
  234. package/lib/motion/shotcraft/upstream/references/shots/interaction/canvas-materialize-moves.md +57 -57
  235. package/lib/motion/shotcraft/upstream/references/shots/interaction/chip-grid-single-select-blackout.md +51 -51
  236. package/lib/motion/shotcraft/upstream/references/shots/interaction/collab-cursor-moves.md +41 -41
  237. package/lib/motion/shotcraft/upstream/references/shots/interaction/glass-pill-dictation-typing.md +53 -53
  238. package/lib/motion/shotcraft/upstream/references/shots/interaction/hashtag-to-pill-materialize.md +60 -60
  239. package/lib/motion/shotcraft/upstream/references/shots/interaction/picker-carousel-feature-cycle.md +55 -55
  240. package/lib/motion/shotcraft/upstream/references/shots/interaction/segmented-thumb-hero.md +57 -57
  241. package/lib/motion/shotcraft/upstream/references/shots/interaction/theme-switch-moves.md +47 -47
  242. package/lib/motion/shotcraft/upstream/references/shots/interaction/type-and-filter.md +37 -37
  243. package/lib/motion/shotcraft/upstream/references/shots/interaction/voice-waveform-live.md +54 -54
  244. package/lib/motion/shotcraft/upstream/references/shots/opening/brand-ink-open.md +36 -36
  245. package/lib/motion/shotcraft/upstream/references/shots/opening/crane-rise-reveal.md +56 -56
  246. package/lib/motion/shotcraft/upstream/references/shots/opening/dataviz-landscape-open.md +56 -56
  247. package/lib/motion/shotcraft/upstream/references/shots/opening/fracture.md +40 -40
  248. package/lib/motion/shotcraft/upstream/references/shots/opening/icon-field-colorize.md +60 -60
  249. package/lib/motion/shotcraft/upstream/references/shots/opening/letterspace-materialize.md +57 -57
  250. package/lib/motion/shotcraft/upstream/references/shots/opening/magician-card-flourish.md +73 -73
  251. package/lib/motion/shotcraft/upstream/references/shots/opening/orbit-ring-title-open.md +87 -87
  252. package/lib/motion/shotcraft/upstream/references/shots/opening/spotlight-hero-card.md +55 -55
  253. package/lib/motion/shotcraft/upstream/references/shots/opening/stroke-segment-build.md +50 -50
  254. package/lib/motion/shotcraft/upstream/references/shots/opening/text-as-mask.md +59 -59
  255. package/lib/motion/shotcraft/upstream/references/shots/outro/grain-dissolve.md +38 -38
  256. package/lib/motion/shotcraft/upstream/references/shots/outro/logo-shrink-wordmark-lockup.md +38 -38
  257. package/lib/motion/shotcraft/upstream/references/shots/outro/neon-triple-marquee.md +59 -59
  258. package/lib/motion/shotcraft/upstream/references/shots/outro/outro-group-photo-launch.md +39 -39
  259. package/lib/motion/shotcraft/upstream/references/shots/outro/ui-strip-away-outro.md +61 -61
  260. package/lib/motion/shotcraft/upstream/references/shots/outro/ui-to-brand-morph.md +52 -52
  261. package/lib/motion/shotcraft/upstream/references/shots/rhythm/beat-cut-moves.md +50 -50
  262. package/lib/motion/shotcraft/upstream/references/shots/rhythm/beat-step-list-theme-cycle.md +66 -66
  263. package/lib/motion/shotcraft/upstream/references/shots/rhythm/panel-grid-moves.md +43 -43
  264. package/lib/motion/shotcraft/upstream/references/shots/rhythm/quad-split-parallel-scenes.md +38 -38
  265. package/lib/motion/shotcraft/upstream/references/shots/rhythm/rhythm-interrupt-moves.md +38 -38
  266. package/lib/motion/shotcraft/upstream/references/shots/rhythm/sakuga-timing-shift.md +56 -56
  267. package/lib/motion/shotcraft/upstream/references/shots/rhythm/speed-ramp-freeze.md +44 -44
  268. package/lib/motion/shotcraft/upstream/references/shots/rhythm/trailer-grammar-moves.md +38 -38
  269. package/lib/motion/shotcraft/upstream/references/shots/transition/bottom-push-stack-wipe.md +63 -63
  270. package/lib/motion/shotcraft/upstream/references/shots/transition/card-flip-reveal.md +57 -57
  271. package/lib/motion/shotcraft/upstream/references/shots/transition/card-flock-tumble.md +63 -63
  272. package/lib/motion/shotcraft/upstream/references/shots/transition/circle-match-iris.md +58 -58
  273. package/lib/motion/shotcraft/upstream/references/shots/transition/gradient-transition.md +45 -45
  274. package/lib/motion/shotcraft/upstream/references/shots/transition/line-carry-transition.md +55 -55
  275. package/lib/motion/shotcraft/upstream/references/shots/transition/mosaic-reframe.md +49 -49
  276. package/lib/motion/shotcraft/upstream/references/shots/transition/page-turn-transitions.md +42 -42
  277. package/lib/motion/shotcraft/upstream/references/shots/transition/print-texture-transitions.md +42 -42
  278. package/lib/motion/shotcraft/upstream/references/shots/transition/shot-transitions.md +45 -45
  279. package/lib/motion/shotcraft/upstream/references/shots/transition/tear-streak-transitions.md +48 -48
  280. package/lib/motion/shotcraft/upstream/references/shots/transition/transition-hidden-cut.md +55 -55
  281. package/lib/motion/shotcraft/upstream/references/shots/transition/transition-travel.md +43 -43
  282. package/lib/motion/shotcraft/upstream/references/shots/transition/white-flash-logo-simplify-cut.md +41 -41
  283. package/lib/motion/shotcraft/upstream/references/shots/transition/wipe-transitions.md +42 -42
  284. package/lib/motion/shotcraft/upstream/references/shots/typography/blur-slide.md +59 -59
  285. package/lib/motion/shotcraft/upstream/references/shots/typography/brace-expand.md +55 -55
  286. package/lib/motion/shotcraft/upstream/references/shots/typography/countdown-arc-scatter.md +65 -65
  287. package/lib/motion/shotcraft/upstream/references/shots/typography/document-typewriter-reveal.md +42 -42
  288. package/lib/motion/shotcraft/upstream/references/shots/typography/flying-words.md +64 -64
  289. package/lib/motion/shotcraft/upstream/references/shots/typography/glitch-cycle.md +64 -64
  290. package/lib/motion/shotcraft/upstream/references/shots/typography/gradient-word-sweep.md +54 -54
  291. package/lib/motion/shotcraft/upstream/references/shots/typography/lead-word-zoom-assemble.md +91 -91
  292. package/lib/motion/shotcraft/upstream/references/shots/typography/marker-underline-title.md +51 -51
  293. package/lib/motion/shotcraft/upstream/references/shots/typography/outline-word-fill.md +66 -66
  294. package/lib/motion/shotcraft/upstream/references/shots/typography/paper-title-card.md +38 -38
  295. package/lib/motion/shotcraft/upstream/references/shots/typography/pill-chip-slot-cycle-handled.md +66 -66
  296. package/lib/motion/shotcraft/upstream/references/shots/typography/pill-slot-cycle.md +58 -58
  297. package/lib/motion/shotcraft/upstream/references/shots/typography/text-column-converge.md +61 -61
  298. package/lib/motion/shotcraft/upstream/references/shots/typography/title-demote-to-label.md +64 -64
  299. package/lib/motion/shotcraft/upstream/references/shots/typography/type-assembly-moves.md +45 -45
  300. package/lib/motion/shotcraft/upstream/references/shots/typography/type-entrance-moves.md +41 -41
  301. package/lib/motion/shotcraft/upstream/references/shots/typography/type-rhythm-sync.md +51 -51
  302. package/lib/motion/shotcraft/upstream/references/shots/typography/typewriter-moves.md +42 -42
  303. package/lib/motion/shotcraft/upstream/references/shots/typography/vertical-word-roll-blur-cycle.md +63 -63
  304. package/lib/motion/shotcraft/upstream/references/shots/typography/word-relay-filmstrip.md +57 -57
  305. package/lib/motion/shotcraft/upstream/references/shots/typography/word-relay-geometry.md +75 -75
  306. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/avatar-bracket-carousel.md +59 -59
  307. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/bezier-source-converge-merge.md +67 -67
  308. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/card-stack.md +60 -60
  309. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/carousel-3d.md +66 -66
  310. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/cloner-depth-echo.md +40 -40
  311. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/deck-deal-flyin.md +45 -45
  312. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/doc-park-left-pill-deal.md +62 -62
  313. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/draw-svg-trace.md +55 -55
  314. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/element-body-moves.md +37 -37
  315. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/integration-hub-map.md +55 -55
  316. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/list-reveal.md +50 -50
  317. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/list-stack-press.md +43 -43
  318. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/neon-frame-forerun.md +55 -55
  319. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/neon-frame-orbit-drop.md +51 -51
  320. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/page-waterfall-wall.md +51 -51
  321. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/product-card-progressive-assemble.md +56 -56
  322. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/radial-wave.md +49 -49
  323. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/research-card-stack-scroll.md +57 -57
  324. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/row-embed.md +36 -36
  325. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/value-stagger-gradient.md +64 -64
  326. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/wall-reveal-moves.md +47 -47
  327. package/lib/motion/shotcraft/upstream/references/sound-design.md +170 -170
  328. package/lib/motion/shotcraft/upstream/references/workbench.md +157 -157
  329. package/lib/motion/shotcraft/upstream/template/TEMPLATE.md +103 -103
  330. package/lib/motion/shotcraft/upstream/template/THEMES.md +44 -44
  331. package/lib/motion/shotcraft/upstream/template/package.json +20 -20
  332. package/lib/motion/shotcraft/upstream/template/remotion.config.ts +6 -6
  333. package/lib/motion/shotcraft/upstream/template/src/Root.tsx +17 -17
  334. package/lib/motion/shotcraft/upstream/template/src/aifl/Caption.tsx +65 -65
  335. package/lib/motion/shotcraft/upstream/template/src/aifl/DigitRoll.tsx +48 -48
  336. package/lib/motion/shotcraft/upstream/template/src/aifl/FlashCut.tsx +24 -24
  337. package/lib/motion/shotcraft/upstream/template/src/aifl/Main.tsx +192 -192
  338. package/lib/motion/shotcraft/upstream/template/src/aifl/PaperTitleCard.tsx +111 -111
  339. package/lib/motion/shotcraft/upstream/template/src/aifl/live/PageCam.tsx +150 -150
  340. package/lib/motion/shotcraft/upstream/template/src/aifl/live/SceneDetail.tsx +158 -158
  341. package/lib/motion/shotcraft/upstream/template/src/aifl/live/SceneFlyIn.tsx +468 -468
  342. package/lib/motion/shotcraft/upstream/template/src/aifl/live/SceneOpen.tsx +552 -552
  343. package/lib/motion/shotcraft/upstream/template/src/aifl/live/SceneOutroLive.tsx +411 -411
  344. package/lib/motion/shotcraft/upstream/template/src/aifl/live/ScenePapers.tsx +200 -200
  345. package/lib/motion/shotcraft/upstream/template/src/aifl/live/SceneWbr.tsx +324 -324
  346. package/lib/motion/shotcraft/upstream/template/src/aifl/live-layout.json +388 -388
  347. package/lib/motion/shotcraft/upstream/template/src/index.ts +4 -4
  348. package/lib/motion/shotcraft/upstream/template/src/themes/ThemedFilm.tsx +7 -7
  349. package/lib/motion/shotcraft/upstream/template/src/themes/manifest.tsx +68 -68
  350. package/lib/motion/shotcraft/upstream/template/src/themes/palette-assets.json +1 -1
  351. package/lib/motion/shotcraft/upstream/template/src/themes/palette-assets.ts +24 -24
  352. package/lib/motion/shotcraft/upstream/template/src/themes/palettes.json +74 -74
  353. package/lib/motion/shotcraft/upstream/template/src/themes/visual-theme.tsx +91 -91
  354. package/lib/motion/shotcraft/upstream/template/src/workbench.ts +213 -213
  355. package/lib/motion/shotcraft/upstream/template/tsconfig.json +15 -15
  356. package/lib/motion/style-frames.js +306 -306
  357. package/lib/motion/sync.js +214 -214
  358. package/lib/motion-project.js +105 -105
  359. package/lib/motion-render-worker.js +174 -174
  360. package/lib/routing/router.js +222 -222
  361. package/lib/routing/vendor/README.md +96 -96
  362. package/lib/routing/vendor/libavoid-routing.js +753 -753
  363. package/lib/routing/vendor/libavoid.min.js +59 -59
  364. package/lib/video-trace-pool.js +128 -128
  365. package/package.json +1 -1
  366. 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
+ };