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