@devinilabs/reelstack 1.3.2 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cli/scaffold.js +124 -16
- package/package.json +1 -1
- package/reference/dark/codedrop.tsx +908 -0
- package/reference/dark/gpt55.tsx +2608 -0
- package/reference/dark/resourcescta.tsx +609 -0
- package/reference/dark/skills.tsx +1460 -0
- package/reference/dark/stitch2.tsx +162 -0
- package/reference/glass/claudewatchcta.tsx +599 -0
- package/reference/glass/gstack.tsx +3020 -0
- package/reference/glass/jcode.tsx +2267 -0
- package/reference/glass/lilagents.tsx +2649 -0
- package/reference/paper/devini3d.tsx +1799 -0
- package/skill/SKILL.md +25 -16
|
@@ -0,0 +1,908 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* REFERENCE — CodeDropReel (Family: dark)
|
|
3
|
+
*
|
|
4
|
+
* Canonical example of the dark family's motion vocabulary, frame-locked
|
|
5
|
+
* BEAT structure, and scene choreography. Bundled with ReelStack v1.4+
|
|
6
|
+
* for STUDY and as the source for the scaffold-from-reference flow.
|
|
7
|
+
*
|
|
8
|
+
* Asset imports stripped (look for REFERENCE-STRIP markers). Bring your own
|
|
9
|
+
* voiceover, brand SVGs, captures.
|
|
10
|
+
*
|
|
11
|
+
* License: study + adapt patterns OK. Verbatim re-publication as your own
|
|
12
|
+
* template NOT OK. See ReelStack LICENSE.
|
|
13
|
+
*
|
|
14
|
+
* Source: my-video/src/CodeDropReel.tsx
|
|
15
|
+
* Bundled at: 2026-05-12T19:40:04.829Z
|
|
16
|
+
*/
|
|
17
|
+
import {
|
|
18
|
+
useCurrentFrame,
|
|
19
|
+
useVideoConfig,
|
|
20
|
+
interpolate,
|
|
21
|
+
spring,
|
|
22
|
+
AbsoluteFill,
|
|
23
|
+
Img,
|
|
24
|
+
staticFile,
|
|
25
|
+
} from "remotion";
|
|
26
|
+
import { ds } from "./designSystem";
|
|
27
|
+
|
|
28
|
+
// ═══════════════════════════════════════════════════════════════
|
|
29
|
+
// TOKENS — matched to ClaudeDispatchReel for family consistency
|
|
30
|
+
// ═══════════════════════════════════════════════════════════════
|
|
31
|
+
|
|
32
|
+
const FONT = ds.font.sans;
|
|
33
|
+
const MONO = ds.font.mono;
|
|
34
|
+
|
|
35
|
+
const C = {
|
|
36
|
+
bg: "#0a0a0b",
|
|
37
|
+
surface: "#1a1a1d",
|
|
38
|
+
border: "rgba(255,255,255,0.08)",
|
|
39
|
+
borderLoud: "rgba(255,255,255,0.14)",
|
|
40
|
+
fg: "#f5f5f7",
|
|
41
|
+
fgSoft: "#d1d1d6",
|
|
42
|
+
fgMuted: "#8e8e93",
|
|
43
|
+
fgDim: "#5a5a60",
|
|
44
|
+
claude: "#D4663A",
|
|
45
|
+
claudeSoft: "#e07a54",
|
|
46
|
+
claudeDim: "rgba(212,102,58,0.15)",
|
|
47
|
+
danger: "#e25822",
|
|
48
|
+
dangerSoft: "#ff6a45",
|
|
49
|
+
safe: "#4fc46a",
|
|
50
|
+
safeDim: "rgba(79,196,106,0.12)",
|
|
51
|
+
} as const;
|
|
52
|
+
|
|
53
|
+
// Blank 0-20s (user overlays website clips), Claude pitch 20-26s, CTA 26-37s.
|
|
54
|
+
const BLANK_END_SEC = 20;
|
|
55
|
+
const BUILD_START_SEC = 20;
|
|
56
|
+
const BUILD_END_SEC = 26;
|
|
57
|
+
const CTA_START_SEC = 26;
|
|
58
|
+
const CTA_END_SEC = 37;
|
|
59
|
+
|
|
60
|
+
// ═══════════════════════════════════════════════════════════════
|
|
61
|
+
// BACKGROUND — same drifting spotlights + grid as previous reels
|
|
62
|
+
// ═══════════════════════════════════════════════════════════════
|
|
63
|
+
|
|
64
|
+
const Background: React.FC<{ frame: number }> = ({ frame }) => {
|
|
65
|
+
const drift = (Math.sin(frame * 0.004) + 1) * 0.5;
|
|
66
|
+
const drift2 = (Math.cos(frame * 0.003) + 1) * 0.5;
|
|
67
|
+
const vignettePulse = 0.35 + Math.sin(frame * 0.008) * 0.04;
|
|
68
|
+
|
|
69
|
+
return (
|
|
70
|
+
<AbsoluteFill>
|
|
71
|
+
<div style={{ width: "100%", height: "100%", background: C.bg }} />
|
|
72
|
+
<div
|
|
73
|
+
style={{
|
|
74
|
+
position: "absolute",
|
|
75
|
+
inset: 0,
|
|
76
|
+
background: `radial-gradient(ellipse 800px 900px at ${20 + drift * 60}% ${15 + drift2 * 40}%, rgba(212,102,58,0.14) 0%, transparent 60%)`,
|
|
77
|
+
}}
|
|
78
|
+
/>
|
|
79
|
+
<div
|
|
80
|
+
style={{
|
|
81
|
+
position: "absolute",
|
|
82
|
+
inset: 0,
|
|
83
|
+
background: `radial-gradient(ellipse 600px 800px at ${80 - drift * 50}% ${70 + drift2 * 25}%, rgba(79,196,106,0.06) 0%, transparent 55%)`,
|
|
84
|
+
}}
|
|
85
|
+
/>
|
|
86
|
+
<div
|
|
87
|
+
style={{
|
|
88
|
+
position: "absolute",
|
|
89
|
+
inset: 0,
|
|
90
|
+
backgroundImage: `
|
|
91
|
+
linear-gradient(rgba(255,255,255,0.035) 1px, transparent 1px),
|
|
92
|
+
linear-gradient(90deg, rgba(255,255,255,0.035) 1px, transparent 1px)
|
|
93
|
+
`,
|
|
94
|
+
backgroundSize: "72px 72px",
|
|
95
|
+
maskImage:
|
|
96
|
+
"radial-gradient(ellipse at 50% 50%, black 30%, transparent 85%)",
|
|
97
|
+
WebkitMaskImage:
|
|
98
|
+
"radial-gradient(ellipse at 50% 50%, black 30%, transparent 85%)",
|
|
99
|
+
}}
|
|
100
|
+
/>
|
|
101
|
+
<div
|
|
102
|
+
style={{
|
|
103
|
+
position: "absolute",
|
|
104
|
+
inset: 0,
|
|
105
|
+
opacity: 0.06,
|
|
106
|
+
mixBlendMode: "overlay",
|
|
107
|
+
backgroundImage: `url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='200' height='200'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.9'/></filter><rect width='200' height='200' filter='url(%23n)' opacity='0.7'/></svg>")`,
|
|
108
|
+
}}
|
|
109
|
+
/>
|
|
110
|
+
<div
|
|
111
|
+
style={{
|
|
112
|
+
position: "absolute",
|
|
113
|
+
inset: 0,
|
|
114
|
+
background: `radial-gradient(ellipse at 50% 50%, transparent 40%, rgba(0,0,0,${vignettePulse}) 100%)`,
|
|
115
|
+
}}
|
|
116
|
+
/>
|
|
117
|
+
</AbsoluteFill>
|
|
118
|
+
);
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
// ═══════════════════════════════════════════════════════════════
|
|
122
|
+
// SAFE ZONE — IG reel: reserve top 290px + bottom 430px
|
|
123
|
+
// ═══════════════════════════════════════════════════════════════
|
|
124
|
+
|
|
125
|
+
const SafeZone: React.FC<{ children: React.ReactNode }> = ({ children }) => (
|
|
126
|
+
<div
|
|
127
|
+
style={{
|
|
128
|
+
position: "absolute",
|
|
129
|
+
top: 290,
|
|
130
|
+
left: 0,
|
|
131
|
+
right: 0,
|
|
132
|
+
bottom: 430,
|
|
133
|
+
}}
|
|
134
|
+
>
|
|
135
|
+
{children}
|
|
136
|
+
</div>
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
// ═══════════════════════════════════════════════════════════════
|
|
140
|
+
// SPRING HELPER (shared by all scenes)
|
|
141
|
+
// ═══════════════════════════════════════════════════════════════
|
|
142
|
+
|
|
143
|
+
const gs = (
|
|
144
|
+
frame: number,
|
|
145
|
+
fps: number,
|
|
146
|
+
delaySec: number,
|
|
147
|
+
kind: "bouncy" | "snappy" | "gentle" | "glass" = "snappy",
|
|
148
|
+
) =>
|
|
149
|
+
spring({
|
|
150
|
+
frame,
|
|
151
|
+
fps,
|
|
152
|
+
delay: Math.round(delaySec * fps),
|
|
153
|
+
config: ds.spring[kind],
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
// ═══════════════════════════════════════════════════════════════
|
|
157
|
+
// BUILD SCENE (20s → 26s) — "Build Within an Hour using Claude Skill"
|
|
158
|
+
// ═══════════════════════════════════════════════════════════════
|
|
159
|
+
|
|
160
|
+
const BuildScene: React.FC<{ frame: number; fps: number }> = ({ frame, fps }) => {
|
|
161
|
+
const startF = BUILD_START_SEC * fps;
|
|
162
|
+
const endF = BUILD_END_SEC * fps;
|
|
163
|
+
if (frame < startF || frame >= endF) return null;
|
|
164
|
+
const localF = frame - startF;
|
|
165
|
+
const totalLocal = endF - startF;
|
|
166
|
+
|
|
167
|
+
// Scene fade in/out
|
|
168
|
+
const sceneOp = interpolate(
|
|
169
|
+
localF,
|
|
170
|
+
[0, 0.4 * fps, totalLocal - 0.5 * fps, totalLocal],
|
|
171
|
+
[0, 1, 1, 0],
|
|
172
|
+
{ extrapolateLeft: "clamp", extrapolateRight: "clamp" },
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
// Logo entry — gentle scale + glow pulse
|
|
176
|
+
const logoSpring = gs(localF, fps, 0, "glass");
|
|
177
|
+
const logoScale = interpolate(logoSpring, [0, 1], [0.6, 1]);
|
|
178
|
+
const logoOp = interpolate(logoSpring, [0, 0.4], [0, 1], {
|
|
179
|
+
extrapolateLeft: "clamp",
|
|
180
|
+
extrapolateRight: "clamp",
|
|
181
|
+
});
|
|
182
|
+
const logoGlow = 0.3 + Math.sin(localF * 0.08) * 0.15;
|
|
183
|
+
|
|
184
|
+
// "POWERED BY CLAUDE" badge
|
|
185
|
+
const badgeSpring = gs(localF, fps, 0.3, "snappy");
|
|
186
|
+
const badgeOp = interpolate(badgeSpring, [0, 0.4], [0, 1], {
|
|
187
|
+
extrapolateLeft: "clamp",
|
|
188
|
+
extrapolateRight: "clamp",
|
|
189
|
+
});
|
|
190
|
+
const badgeY = interpolate(badgeSpring, [0, 1], [12, 0]);
|
|
191
|
+
|
|
192
|
+
// Headline word reveal: "Build Within An Hour"
|
|
193
|
+
const headWords = ["Build", "Within", "An", "Hour"];
|
|
194
|
+
|
|
195
|
+
// Subhead
|
|
196
|
+
const subSpring = gs(localF, fps, 1.8, "gentle");
|
|
197
|
+
const subOp = interpolate(subSpring, [0, 0.4], [0, 1], {
|
|
198
|
+
extrapolateLeft: "clamp",
|
|
199
|
+
extrapolateRight: "clamp",
|
|
200
|
+
});
|
|
201
|
+
const subY = interpolate(subSpring, [0, 1], [22, 0]);
|
|
202
|
+
|
|
203
|
+
// Stat chips entry
|
|
204
|
+
const chipDelays = [2.4, 2.6, 2.8];
|
|
205
|
+
|
|
206
|
+
// Highlight chip pulse on "Hour"
|
|
207
|
+
const hourPulse = 1 + Math.sin(localF * 0.12) * 0.04;
|
|
208
|
+
|
|
209
|
+
return (
|
|
210
|
+
<AbsoluteFill style={{ opacity: sceneOp }}>
|
|
211
|
+
<SafeZone>
|
|
212
|
+
{/* ── CLAUDE LOGO + BADGE ─────────────────── */}
|
|
213
|
+
<div
|
|
214
|
+
style={{
|
|
215
|
+
position: "absolute",
|
|
216
|
+
top: 0,
|
|
217
|
+
width: "100%",
|
|
218
|
+
display: "flex",
|
|
219
|
+
flexDirection: "column",
|
|
220
|
+
alignItems: "center",
|
|
221
|
+
gap: 22,
|
|
222
|
+
}}
|
|
223
|
+
>
|
|
224
|
+
{/* Logo with halo */}
|
|
225
|
+
<div
|
|
226
|
+
style={{
|
|
227
|
+
position: "relative",
|
|
228
|
+
opacity: logoOp,
|
|
229
|
+
transform: `scale(${logoScale})`,
|
|
230
|
+
width: 160,
|
|
231
|
+
height: 160,
|
|
232
|
+
display: "flex",
|
|
233
|
+
alignItems: "center",
|
|
234
|
+
justifyContent: "center",
|
|
235
|
+
}}
|
|
236
|
+
>
|
|
237
|
+
<div
|
|
238
|
+
style={{
|
|
239
|
+
position: "absolute",
|
|
240
|
+
inset: -30,
|
|
241
|
+
background: `radial-gradient(circle, rgba(212,102,58,${logoGlow}) 0%, transparent 60%)`,
|
|
242
|
+
filter: "blur(12px)",
|
|
243
|
+
}}
|
|
244
|
+
/>
|
|
245
|
+
<div
|
|
246
|
+
style={{
|
|
247
|
+
position: "relative",
|
|
248
|
+
width: 160,
|
|
249
|
+
height: 160,
|
|
250
|
+
borderRadius: 36,
|
|
251
|
+
background: `linear-gradient(135deg, #1a1a1d, #0f0f12)`,
|
|
252
|
+
border: `1.5px solid rgba(212,102,58,0.4)`,
|
|
253
|
+
display: "flex",
|
|
254
|
+
alignItems: "center",
|
|
255
|
+
justifyContent: "center",
|
|
256
|
+
boxShadow: `0 20px 50px -10px rgba(0,0,0,0.7), inset 0 1px 0 rgba(255,255,255,0.08)`,
|
|
257
|
+
overflow: "hidden",
|
|
258
|
+
}}
|
|
259
|
+
>
|
|
260
|
+
<Img
|
|
261
|
+
src={staticFile("claude-ai-icon.webp")}
|
|
262
|
+
style={{
|
|
263
|
+
width: 130,
|
|
264
|
+
height: 130,
|
|
265
|
+
objectFit: "contain",
|
|
266
|
+
filter: "drop-shadow(0 0 14px rgba(212,102,58,0.55))",
|
|
267
|
+
}}
|
|
268
|
+
/>
|
|
269
|
+
</div>
|
|
270
|
+
</div>
|
|
271
|
+
|
|
272
|
+
{/* Claude Code badge */}
|
|
273
|
+
<div
|
|
274
|
+
style={{
|
|
275
|
+
opacity: badgeOp,
|
|
276
|
+
transform: `translateY(${badgeY}px)`,
|
|
277
|
+
padding: "12px 26px",
|
|
278
|
+
borderRadius: 9999,
|
|
279
|
+
background: "rgba(212,102,58,0.14)",
|
|
280
|
+
border: "1.5px solid rgba(212,102,58,0.5)",
|
|
281
|
+
fontFamily: MONO,
|
|
282
|
+
fontSize: 26,
|
|
283
|
+
fontWeight: 700,
|
|
284
|
+
color: C.claudeSoft,
|
|
285
|
+
letterSpacing: "0.08em",
|
|
286
|
+
textTransform: "uppercase",
|
|
287
|
+
}}
|
|
288
|
+
>
|
|
289
|
+
Claude Code
|
|
290
|
+
</div>
|
|
291
|
+
</div>
|
|
292
|
+
|
|
293
|
+
{/* ── HEADLINE — "Build Within An Hour" ───────── */}
|
|
294
|
+
<div
|
|
295
|
+
style={{
|
|
296
|
+
position: "absolute",
|
|
297
|
+
top: 410,
|
|
298
|
+
width: "100%",
|
|
299
|
+
textAlign: "center",
|
|
300
|
+
padding: "0 50px",
|
|
301
|
+
display: "flex",
|
|
302
|
+
flexWrap: "wrap",
|
|
303
|
+
gap: "0 0.32em",
|
|
304
|
+
justifyContent: "center",
|
|
305
|
+
fontFamily: FONT,
|
|
306
|
+
fontSize: 96,
|
|
307
|
+
fontWeight: 700,
|
|
308
|
+
color: C.fg,
|
|
309
|
+
letterSpacing: "-0.04em",
|
|
310
|
+
lineHeight: 1.0,
|
|
311
|
+
}}
|
|
312
|
+
>
|
|
313
|
+
{headWords.map((w, i) => {
|
|
314
|
+
const s = gs(localF, fps, 0.7 + i * 0.1, "bouncy");
|
|
315
|
+
const y = interpolate(s, [0, 1], [50, 0]);
|
|
316
|
+
const op = interpolate(s, [0, 0.4], [0, 1], {
|
|
317
|
+
extrapolateLeft: "clamp",
|
|
318
|
+
extrapolateRight: "clamp",
|
|
319
|
+
});
|
|
320
|
+
const isHour = w === "Hour";
|
|
321
|
+
return (
|
|
322
|
+
<span
|
|
323
|
+
key={`${w}-${i}`}
|
|
324
|
+
style={{
|
|
325
|
+
display: "inline-block",
|
|
326
|
+
transform: `translateY(${y}px)${isHour ? ` scale(${hourPulse})` : ""}`,
|
|
327
|
+
opacity: op,
|
|
328
|
+
color: isHour ? C.claude : C.fg,
|
|
329
|
+
textShadow: isHour
|
|
330
|
+
? `0 0 24px rgba(212,102,58,0.5)`
|
|
331
|
+
: "none",
|
|
332
|
+
}}
|
|
333
|
+
>
|
|
334
|
+
{w}
|
|
335
|
+
</span>
|
|
336
|
+
);
|
|
337
|
+
})}
|
|
338
|
+
</div>
|
|
339
|
+
|
|
340
|
+
{/* ── SUBHEAD ─────────────────────────────────── */}
|
|
341
|
+
<div
|
|
342
|
+
style={{
|
|
343
|
+
position: "absolute",
|
|
344
|
+
top: 590,
|
|
345
|
+
width: "100%",
|
|
346
|
+
textAlign: "center",
|
|
347
|
+
padding: "0 70px",
|
|
348
|
+
opacity: subOp,
|
|
349
|
+
transform: `translateY(${subY}px)`,
|
|
350
|
+
fontFamily: FONT,
|
|
351
|
+
fontSize: 36,
|
|
352
|
+
fontWeight: 500,
|
|
353
|
+
color: C.fgSoft,
|
|
354
|
+
letterSpacing: "-0.025em",
|
|
355
|
+
lineHeight: 1.2,
|
|
356
|
+
}}
|
|
357
|
+
>
|
|
358
|
+
using a custom-created{" "}
|
|
359
|
+
<span
|
|
360
|
+
style={{
|
|
361
|
+
color: C.claudeSoft,
|
|
362
|
+
fontWeight: 700,
|
|
363
|
+
background: `linear-gradient(135deg, ${C.claude}, ${C.claudeSoft})`,
|
|
364
|
+
WebkitBackgroundClip: "text",
|
|
365
|
+
backgroundClip: "text",
|
|
366
|
+
WebkitTextFillColor: "transparent",
|
|
367
|
+
}}
|
|
368
|
+
>
|
|
369
|
+
Claude Skill
|
|
370
|
+
</span>
|
|
371
|
+
</div>
|
|
372
|
+
|
|
373
|
+
{/* ── STAT CHIPS ──────────────────────────────── */}
|
|
374
|
+
<div
|
|
375
|
+
style={{
|
|
376
|
+
position: "absolute",
|
|
377
|
+
top: 740,
|
|
378
|
+
width: "100%",
|
|
379
|
+
display: "flex",
|
|
380
|
+
justifyContent: "center",
|
|
381
|
+
gap: 14,
|
|
382
|
+
padding: "0 30px",
|
|
383
|
+
flexWrap: "wrap",
|
|
384
|
+
}}
|
|
385
|
+
>
|
|
386
|
+
{[
|
|
387
|
+
{ icon: "⏱", label: "1 HOUR", color: C.claudeSoft },
|
|
388
|
+
{ icon: "⚡", label: "ZERO BOILERPLATE", color: C.fg },
|
|
389
|
+
{ icon: "🚀", label: "SHIPPABLE", color: C.safe },
|
|
390
|
+
].map((chip, i) => {
|
|
391
|
+
const s = gs(localF, fps, chipDelays[i], "snappy");
|
|
392
|
+
const op = interpolate(s, [0, 0.4], [0, 1], {
|
|
393
|
+
extrapolateLeft: "clamp",
|
|
394
|
+
extrapolateRight: "clamp",
|
|
395
|
+
});
|
|
396
|
+
const y = interpolate(s, [0, 1], [16, 0]);
|
|
397
|
+
return (
|
|
398
|
+
<div
|
|
399
|
+
key={chip.label}
|
|
400
|
+
style={{
|
|
401
|
+
opacity: op,
|
|
402
|
+
transform: `translateY(${y}px)`,
|
|
403
|
+
padding: "10px 18px",
|
|
404
|
+
borderRadius: 12,
|
|
405
|
+
background: "rgba(255,255,255,0.04)",
|
|
406
|
+
border: `1px solid ${C.border}`,
|
|
407
|
+
display: "flex",
|
|
408
|
+
alignItems: "center",
|
|
409
|
+
gap: 8,
|
|
410
|
+
fontFamily: MONO,
|
|
411
|
+
fontSize: 18,
|
|
412
|
+
fontWeight: 600,
|
|
413
|
+
color: chip.color,
|
|
414
|
+
letterSpacing: "0.06em",
|
|
415
|
+
}}
|
|
416
|
+
>
|
|
417
|
+
<span style={{ fontSize: 20 }}>{chip.icon}</span>
|
|
418
|
+
{chip.label}
|
|
419
|
+
</div>
|
|
420
|
+
);
|
|
421
|
+
})}
|
|
422
|
+
</div>
|
|
423
|
+
</SafeZone>
|
|
424
|
+
</AbsoluteFill>
|
|
425
|
+
);
|
|
426
|
+
};
|
|
427
|
+
|
|
428
|
+
// ═══════════════════════════════════════════════════════════════
|
|
429
|
+
// CTA SCENE (26s → 37s) — Like · Follow · Comment "AI"
|
|
430
|
+
// ═══════════════════════════════════════════════════════════════
|
|
431
|
+
|
|
432
|
+
const CTAScene: React.FC<{ frame: number; fps: number }> = ({ frame, fps }) => {
|
|
433
|
+
const startF = CTA_START_SEC * fps;
|
|
434
|
+
const endF = CTA_END_SEC * fps;
|
|
435
|
+
const localF = Math.max(0, frame - startF);
|
|
436
|
+
|
|
437
|
+
const sceneOp = interpolate(
|
|
438
|
+
frame,
|
|
439
|
+
[startF, startF + 0.4 * fps, endF - 0.3 * fps, endF],
|
|
440
|
+
[0, 1, 1, 1],
|
|
441
|
+
{ extrapolateLeft: "clamp", extrapolateRight: "clamp" },
|
|
442
|
+
);
|
|
443
|
+
if (frame < startF) return null;
|
|
444
|
+
|
|
445
|
+
// Heading word reveal
|
|
446
|
+
const headingWords = ["Want", "this", "website's", "full", "code?"];
|
|
447
|
+
const subSpring = gs(localF, fps, 1.1, "gentle");
|
|
448
|
+
const subOp = interpolate(subSpring, [0, 0.4], [0, 1], {
|
|
449
|
+
extrapolateLeft: "clamp",
|
|
450
|
+
extrapolateRight: "clamp",
|
|
451
|
+
});
|
|
452
|
+
const subY = interpolate(subSpring, [0, 1], [20, 0]);
|
|
453
|
+
|
|
454
|
+
// Action rows — stagger in
|
|
455
|
+
const rowDelays = [1.6, 1.9, 2.2];
|
|
456
|
+
const rows = rowDelays.map((d) => {
|
|
457
|
+
const s = gs(localF, fps, d, "bouncy");
|
|
458
|
+
return {
|
|
459
|
+
op: interpolate(s, [0, 0.4], [0, 1], {
|
|
460
|
+
extrapolateLeft: "clamp",
|
|
461
|
+
extrapolateRight: "clamp",
|
|
462
|
+
}),
|
|
463
|
+
x: interpolate(s, [0, 1], [-60, 0]),
|
|
464
|
+
scale: interpolate(s, [0, 0.6, 1], [0.92, 1.02, 1]),
|
|
465
|
+
};
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
// "AI" typed into the comment row (3.2 → 3.9s)
|
|
469
|
+
const typed = Math.floor(
|
|
470
|
+
interpolate(localF, [3.2 * fps, 3.9 * fps], [0, 2], {
|
|
471
|
+
extrapolateLeft: "clamp",
|
|
472
|
+
extrapolateRight: "clamp",
|
|
473
|
+
}),
|
|
474
|
+
);
|
|
475
|
+
const aiText = "AI".slice(0, typed);
|
|
476
|
+
const cursorVis = Math.floor(localF / 10) % 2 === 0;
|
|
477
|
+
|
|
478
|
+
// Heart pulse when comment lands (4.0s)
|
|
479
|
+
const heartPulse = gs(localF, fps, 4.0, "bouncy");
|
|
480
|
+
const heartScale = interpolate(heartPulse, [0, 0.5, 1], [1, 1.35, 1.08]);
|
|
481
|
+
const heartFilled = localF > 4.0 * fps;
|
|
482
|
+
|
|
483
|
+
// Follow pill activates (4.3s)
|
|
484
|
+
const followActive = localF > 4.3 * fps;
|
|
485
|
+
|
|
486
|
+
// Bottom reward pill (4.8s)
|
|
487
|
+
const pillSpring = gs(localF, fps, 4.8, "glass");
|
|
488
|
+
const pillY = interpolate(pillSpring, [0, 1], [80, 0]);
|
|
489
|
+
const pillOp = interpolate(pillSpring, [0, 0.35], [0, 1], {
|
|
490
|
+
extrapolateLeft: "clamp",
|
|
491
|
+
extrapolateRight: "clamp",
|
|
492
|
+
});
|
|
493
|
+
const pillGlow = 0.5 + Math.sin(localF * 0.12) * 0.2;
|
|
494
|
+
|
|
495
|
+
// YouTube tutorial tag (5.3s)
|
|
496
|
+
const ytSpring = gs(localF, fps, 5.3, "gentle");
|
|
497
|
+
const ytOp = interpolate(ytSpring, [0, 0.4], [0, 1], {
|
|
498
|
+
extrapolateLeft: "clamp",
|
|
499
|
+
extrapolateRight: "clamp",
|
|
500
|
+
});
|
|
501
|
+
const ytY = interpolate(ytSpring, [0, 1], [16, 0]);
|
|
502
|
+
|
|
503
|
+
return (
|
|
504
|
+
<AbsoluteFill style={{ opacity: sceneOp }}>
|
|
505
|
+
<SafeZone>
|
|
506
|
+
{/* ── HEADING ─────────────────────────────────── */}
|
|
507
|
+
<div
|
|
508
|
+
style={{
|
|
509
|
+
position: "absolute",
|
|
510
|
+
top: 0,
|
|
511
|
+
width: "100%",
|
|
512
|
+
textAlign: "center",
|
|
513
|
+
padding: "0 60px",
|
|
514
|
+
}}
|
|
515
|
+
>
|
|
516
|
+
<div
|
|
517
|
+
style={{
|
|
518
|
+
fontFamily: FONT,
|
|
519
|
+
fontSize: 72,
|
|
520
|
+
fontWeight: 700,
|
|
521
|
+
color: C.fg,
|
|
522
|
+
letterSpacing: "-0.035em",
|
|
523
|
+
lineHeight: 1.05,
|
|
524
|
+
display: "flex",
|
|
525
|
+
flexWrap: "wrap",
|
|
526
|
+
gap: "0 0.32em",
|
|
527
|
+
justifyContent: "center",
|
|
528
|
+
}}
|
|
529
|
+
>
|
|
530
|
+
{headingWords.map((w, i) => {
|
|
531
|
+
const s = gs(localF, fps, 0.1 + i * 0.08, "snappy");
|
|
532
|
+
const y = interpolate(s, [0, 1], [40, 0]);
|
|
533
|
+
const op = interpolate(s, [0, 0.4], [0, 1], {
|
|
534
|
+
extrapolateLeft: "clamp",
|
|
535
|
+
extrapolateRight: "clamp",
|
|
536
|
+
});
|
|
537
|
+
const isFinal = w === "code?";
|
|
538
|
+
return (
|
|
539
|
+
<span
|
|
540
|
+
key={`${w}-${i}`}
|
|
541
|
+
style={{
|
|
542
|
+
display: "inline-block",
|
|
543
|
+
transform: `translateY(${y}px)`,
|
|
544
|
+
opacity: op,
|
|
545
|
+
color: isFinal ? C.claude : C.fg,
|
|
546
|
+
}}
|
|
547
|
+
>
|
|
548
|
+
{w}
|
|
549
|
+
</span>
|
|
550
|
+
);
|
|
551
|
+
})}
|
|
552
|
+
</div>
|
|
553
|
+
{(() => {
|
|
554
|
+
const sweep = (localF * 1.1) % 260 - 30; // -30 → 230 loop
|
|
555
|
+
const glow = 0.35 + Math.sin(localF * 0.08) * 0.12;
|
|
556
|
+
const glowRadius = 16 + Math.sin(localF * 0.08) * 6;
|
|
557
|
+
return (
|
|
558
|
+
<div
|
|
559
|
+
style={{
|
|
560
|
+
position: "relative",
|
|
561
|
+
marginTop: 24,
|
|
562
|
+
display: "inline-block",
|
|
563
|
+
opacity: subOp,
|
|
564
|
+
transform: `translateY(${subY}px)`,
|
|
565
|
+
}}
|
|
566
|
+
>
|
|
567
|
+
{/* Soft radial glow behind the text */}
|
|
568
|
+
<div
|
|
569
|
+
style={{
|
|
570
|
+
position: "absolute",
|
|
571
|
+
inset: "-40px -80px",
|
|
572
|
+
background: `radial-gradient(ellipse at center, rgba(212,102,58,${0.18 + Math.sin(localF * 0.06) * 0.05}) 0%, transparent 65%)`,
|
|
573
|
+
pointerEvents: "none",
|
|
574
|
+
filter: "blur(8px)",
|
|
575
|
+
}}
|
|
576
|
+
/>
|
|
577
|
+
{/* Text with animated sheen — gradient stays white, sheen band brightens to warm highlight */}
|
|
578
|
+
<div
|
|
579
|
+
style={{
|
|
580
|
+
position: "relative",
|
|
581
|
+
fontFamily: MONO,
|
|
582
|
+
fontSize: 32,
|
|
583
|
+
fontWeight: 700,
|
|
584
|
+
letterSpacing: "0.03em",
|
|
585
|
+
textTransform: "uppercase",
|
|
586
|
+
textShadow: `0 0 ${glowRadius}px rgba(212,102,58,${glow}), 0 0 40px rgba(212,102,58,0.15)`,
|
|
587
|
+
backgroundImage: `linear-gradient(105deg, #f5f5f7 0%, #f5f5f7 ${sweep - 12}%, #ffe9d6 ${sweep}%, #f5f5f7 ${sweep + 12}%, #f5f5f7 100%)`,
|
|
588
|
+
WebkitBackgroundClip: "text",
|
|
589
|
+
backgroundClip: "text",
|
|
590
|
+
WebkitTextFillColor: "transparent",
|
|
591
|
+
color: "transparent",
|
|
592
|
+
}}
|
|
593
|
+
>
|
|
594
|
+
+ THE ENTIRE SKILL PACK
|
|
595
|
+
</div>
|
|
596
|
+
</div>
|
|
597
|
+
);
|
|
598
|
+
})()}
|
|
599
|
+
</div>
|
|
600
|
+
|
|
601
|
+
{/* ── ACTION ROWS ─────────────────────────────── */}
|
|
602
|
+
<div
|
|
603
|
+
style={{
|
|
604
|
+
position: "absolute",
|
|
605
|
+
top: 320,
|
|
606
|
+
left: 0,
|
|
607
|
+
right: 0,
|
|
608
|
+
display: "flex",
|
|
609
|
+
flexDirection: "column",
|
|
610
|
+
gap: 18,
|
|
611
|
+
padding: "0 60px",
|
|
612
|
+
}}
|
|
613
|
+
>
|
|
614
|
+
{/* LIKE row */}
|
|
615
|
+
<ActionRow
|
|
616
|
+
anim={rows[0]}
|
|
617
|
+
active={heartFilled}
|
|
618
|
+
labelIdle="Like"
|
|
619
|
+
labelActive="Liked"
|
|
620
|
+
accent={C.danger}
|
|
621
|
+
icon={
|
|
622
|
+
<svg
|
|
623
|
+
width={58}
|
|
624
|
+
height={58}
|
|
625
|
+
viewBox="0 0 24 24"
|
|
626
|
+
fill={heartFilled ? C.danger : "none"}
|
|
627
|
+
stroke={heartFilled ? C.danger : C.fg}
|
|
628
|
+
strokeWidth="2"
|
|
629
|
+
style={{ transform: `scale(${heartScale})` }}
|
|
630
|
+
>
|
|
631
|
+
<path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z" />
|
|
632
|
+
</svg>
|
|
633
|
+
}
|
|
634
|
+
/>
|
|
635
|
+
|
|
636
|
+
{/* FOLLOW row */}
|
|
637
|
+
<ActionRow
|
|
638
|
+
anim={rows[1]}
|
|
639
|
+
active={followActive}
|
|
640
|
+
labelIdle="Follow"
|
|
641
|
+
labelActive="Following"
|
|
642
|
+
accent={C.claude}
|
|
643
|
+
icon={
|
|
644
|
+
<svg
|
|
645
|
+
width={58}
|
|
646
|
+
height={58}
|
|
647
|
+
viewBox="0 0 24 24"
|
|
648
|
+
fill="none"
|
|
649
|
+
stroke={followActive ? C.claude : C.fg}
|
|
650
|
+
strokeWidth="2"
|
|
651
|
+
strokeLinejoin="round"
|
|
652
|
+
strokeLinecap="round"
|
|
653
|
+
>
|
|
654
|
+
<path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2" />
|
|
655
|
+
<circle cx="9" cy="7" r="4" />
|
|
656
|
+
<line x1="19" y1="8" x2="19" y2="14" />
|
|
657
|
+
<line x1="22" y1="11" x2="16" y2="11" />
|
|
658
|
+
</svg>
|
|
659
|
+
}
|
|
660
|
+
/>
|
|
661
|
+
|
|
662
|
+
{/* COMMENT row — types "AI" */}
|
|
663
|
+
<ActionRow
|
|
664
|
+
anim={rows[2]}
|
|
665
|
+
active={typed > 0}
|
|
666
|
+
labelIdle="Comment"
|
|
667
|
+
accent={C.safe}
|
|
668
|
+
icon={
|
|
669
|
+
<svg
|
|
670
|
+
width={58}
|
|
671
|
+
height={58}
|
|
672
|
+
viewBox="0 0 24 24"
|
|
673
|
+
fill="none"
|
|
674
|
+
stroke={typed > 0 ? C.safe : C.fg}
|
|
675
|
+
strokeWidth="2"
|
|
676
|
+
strokeLinejoin="round"
|
|
677
|
+
strokeLinecap="round"
|
|
678
|
+
>
|
|
679
|
+
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" />
|
|
680
|
+
</svg>
|
|
681
|
+
}
|
|
682
|
+
trailing={
|
|
683
|
+
<div
|
|
684
|
+
style={{
|
|
685
|
+
fontFamily: MONO,
|
|
686
|
+
fontSize: 38,
|
|
687
|
+
fontWeight: 600,
|
|
688
|
+
color: typed > 0 ? C.safe : C.fgDim,
|
|
689
|
+
letterSpacing: "0.04em",
|
|
690
|
+
minWidth: 90,
|
|
691
|
+
textAlign: "right",
|
|
692
|
+
}}
|
|
693
|
+
>
|
|
694
|
+
{aiText || "_"}
|
|
695
|
+
{typed < 2 && cursorVis && (
|
|
696
|
+
<span style={{ color: C.fgMuted }}>|</span>
|
|
697
|
+
)}
|
|
698
|
+
</div>
|
|
699
|
+
}
|
|
700
|
+
/>
|
|
701
|
+
</div>
|
|
702
|
+
|
|
703
|
+
{/* ── YOUTUBE TUTORIAL TAG — animated glassy border ───── */}
|
|
704
|
+
<div
|
|
705
|
+
style={{
|
|
706
|
+
position: "absolute",
|
|
707
|
+
bottom: 150,
|
|
708
|
+
left: "50%",
|
|
709
|
+
transform: `translate(-50%, ${ytY}px)`,
|
|
710
|
+
opacity: ytOp,
|
|
711
|
+
padding: 3,
|
|
712
|
+
borderRadius: 9999,
|
|
713
|
+
background: `conic-gradient(from ${(localF * 3) % 360}deg, rgba(255,255,255,0.06), rgba(255,255,255,0.55), rgba(212,102,58,0.8), rgba(255,255,255,0.5), rgba(79,196,106,0.45), rgba(255,255,255,0.06))`,
|
|
714
|
+
boxShadow: `0 18px 50px -12px rgba(0,0,0,0.7), 0 0 60px -10px rgba(212,102,58,${0.15 + Math.sin(localF * 0.1) * 0.08})`,
|
|
715
|
+
}}
|
|
716
|
+
>
|
|
717
|
+
<div
|
|
718
|
+
style={{
|
|
719
|
+
display: "flex",
|
|
720
|
+
alignItems: "center",
|
|
721
|
+
gap: 22,
|
|
722
|
+
padding: "26px 46px",
|
|
723
|
+
background: `linear-gradient(135deg, rgba(20,20,22,0.92), rgba(26,26,29,0.92))`,
|
|
724
|
+
borderRadius: 9999,
|
|
725
|
+
backdropFilter: "blur(14px)",
|
|
726
|
+
position: "relative",
|
|
727
|
+
overflow: "hidden",
|
|
728
|
+
}}
|
|
729
|
+
>
|
|
730
|
+
{/* subtle inner sheen */}
|
|
731
|
+
<div
|
|
732
|
+
style={{
|
|
733
|
+
position: "absolute",
|
|
734
|
+
inset: 0,
|
|
735
|
+
background: `linear-gradient(180deg, rgba(255,255,255,0.06) 0%, transparent 40%, transparent 60%, rgba(255,255,255,0.02) 100%)`,
|
|
736
|
+
pointerEvents: "none",
|
|
737
|
+
}}
|
|
738
|
+
/>
|
|
739
|
+
{/* YouTube icon */}
|
|
740
|
+
<svg
|
|
741
|
+
width="58"
|
|
742
|
+
height="58"
|
|
743
|
+
viewBox="0 0 24 24"
|
|
744
|
+
fill="#FF0033"
|
|
745
|
+
style={{ position: "relative", flexShrink: 0 }}
|
|
746
|
+
>
|
|
747
|
+
<path d="M23.498 6.186a3.016 3.016 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.546 15.568V8.432L15.818 12l-6.272 3.568z" />
|
|
748
|
+
</svg>
|
|
749
|
+
<span
|
|
750
|
+
style={{
|
|
751
|
+
position: "relative",
|
|
752
|
+
fontFamily: FONT,
|
|
753
|
+
fontSize: 34,
|
|
754
|
+
fontWeight: 600,
|
|
755
|
+
color: C.fgSoft,
|
|
756
|
+
letterSpacing: "-0.02em",
|
|
757
|
+
whiteSpace: "nowrap",
|
|
758
|
+
}}
|
|
759
|
+
>
|
|
760
|
+
Check Bio, Full tutorial{" "}
|
|
761
|
+
<span style={{ fontWeight: 700, color: C.fg }}>@DeviniLabs</span>
|
|
762
|
+
</span>
|
|
763
|
+
</div>
|
|
764
|
+
</div>
|
|
765
|
+
|
|
766
|
+
{/* ── REWARD PILL ─────────────────────────────── */}
|
|
767
|
+
<div
|
|
768
|
+
style={{
|
|
769
|
+
position: "absolute",
|
|
770
|
+
bottom: 6,
|
|
771
|
+
left: "50%",
|
|
772
|
+
transform: `translate(-50%, ${pillY}px)`,
|
|
773
|
+
opacity: pillOp,
|
|
774
|
+
padding: "20px 36px",
|
|
775
|
+
background: `linear-gradient(135deg, ${C.claude}, ${C.claudeSoft})`,
|
|
776
|
+
borderRadius: 9999,
|
|
777
|
+
display: "flex",
|
|
778
|
+
alignItems: "center",
|
|
779
|
+
gap: 14,
|
|
780
|
+
boxShadow: `0 24px 50px -10px rgba(212,102,58,${0.45 + pillGlow * 0.15}), 0 0 80px rgba(212,102,58,${0.25 + pillGlow * 0.2})`,
|
|
781
|
+
}}
|
|
782
|
+
>
|
|
783
|
+
<svg
|
|
784
|
+
width="30"
|
|
785
|
+
height="30"
|
|
786
|
+
viewBox="0 0 24 24"
|
|
787
|
+
fill="none"
|
|
788
|
+
stroke="#fff"
|
|
789
|
+
strokeWidth="2.6"
|
|
790
|
+
strokeLinejoin="round"
|
|
791
|
+
strokeLinecap="round"
|
|
792
|
+
>
|
|
793
|
+
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" />
|
|
794
|
+
</svg>
|
|
795
|
+
<span
|
|
796
|
+
style={{
|
|
797
|
+
fontFamily: FONT,
|
|
798
|
+
fontSize: 28,
|
|
799
|
+
fontWeight: 700,
|
|
800
|
+
color: "#fff",
|
|
801
|
+
letterSpacing: "-0.02em",
|
|
802
|
+
}}
|
|
803
|
+
>
|
|
804
|
+
Like, Follow & Comment{" "}
|
|
805
|
+
<span
|
|
806
|
+
style={{
|
|
807
|
+
fontFamily: MONO,
|
|
808
|
+
background: "rgba(255,255,255,0.2)",
|
|
809
|
+
padding: "2px 12px",
|
|
810
|
+
borderRadius: 8,
|
|
811
|
+
fontSize: 26,
|
|
812
|
+
}}
|
|
813
|
+
>
|
|
814
|
+
AI
|
|
815
|
+
</span>
|
|
816
|
+
</span>
|
|
817
|
+
</div>
|
|
818
|
+
</SafeZone>
|
|
819
|
+
</AbsoluteFill>
|
|
820
|
+
);
|
|
821
|
+
};
|
|
822
|
+
|
|
823
|
+
// ═══════════════════════════════════════════════════════════════
|
|
824
|
+
// ACTION ROW — single card for Like / Follow / Comment
|
|
825
|
+
// ═══════════════════════════════════════════════════════════════
|
|
826
|
+
|
|
827
|
+
const ActionRow: React.FC<{
|
|
828
|
+
anim: { op: number; x: number; scale: number };
|
|
829
|
+
active: boolean;
|
|
830
|
+
labelIdle: string;
|
|
831
|
+
labelActive?: string;
|
|
832
|
+
accent: string;
|
|
833
|
+
icon: React.ReactNode;
|
|
834
|
+
trailing?: React.ReactNode;
|
|
835
|
+
}> = ({ anim, active, labelIdle, labelActive, accent, icon, trailing }) => {
|
|
836
|
+
return (
|
|
837
|
+
<div
|
|
838
|
+
style={{
|
|
839
|
+
opacity: anim.op,
|
|
840
|
+
transform: `translateX(${anim.x}px) scale(${anim.scale})`,
|
|
841
|
+
background: active
|
|
842
|
+
? `linear-gradient(135deg, ${accent}22, ${C.surface})`
|
|
843
|
+
: C.surface,
|
|
844
|
+
border: `1.5px solid ${active ? accent : C.border}`,
|
|
845
|
+
borderRadius: 22,
|
|
846
|
+
padding: "22px 28px",
|
|
847
|
+
display: "flex",
|
|
848
|
+
alignItems: "center",
|
|
849
|
+
gap: 24,
|
|
850
|
+
boxShadow: active
|
|
851
|
+
? `0 0 40px -8px ${accent}66, inset 0 1px 0 rgba(255,255,255,0.04)`
|
|
852
|
+
: `inset 0 1px 0 rgba(255,255,255,0.04)`,
|
|
853
|
+
transition: "none",
|
|
854
|
+
}}
|
|
855
|
+
>
|
|
856
|
+
<div
|
|
857
|
+
style={{
|
|
858
|
+
width: 84,
|
|
859
|
+
height: 84,
|
|
860
|
+
borderRadius: 20,
|
|
861
|
+
background: active ? `${accent}14` : "rgba(255,255,255,0.04)",
|
|
862
|
+
border: `1px solid ${active ? `${accent}55` : C.border}`,
|
|
863
|
+
display: "flex",
|
|
864
|
+
alignItems: "center",
|
|
865
|
+
justifyContent: "center",
|
|
866
|
+
flexShrink: 0,
|
|
867
|
+
}}
|
|
868
|
+
>
|
|
869
|
+
{icon}
|
|
870
|
+
</div>
|
|
871
|
+
<div
|
|
872
|
+
style={{
|
|
873
|
+
fontFamily: FONT,
|
|
874
|
+
fontSize: 44,
|
|
875
|
+
fontWeight: 700,
|
|
876
|
+
color: active ? C.fg : C.fgSoft,
|
|
877
|
+
letterSpacing: "-0.03em",
|
|
878
|
+
flex: 1,
|
|
879
|
+
}}
|
|
880
|
+
>
|
|
881
|
+
{active && labelActive ? labelActive : labelIdle}
|
|
882
|
+
</div>
|
|
883
|
+
{trailing}
|
|
884
|
+
</div>
|
|
885
|
+
);
|
|
886
|
+
};
|
|
887
|
+
|
|
888
|
+
// ═══════════════════════════════════════════════════════════════
|
|
889
|
+
// MAIN
|
|
890
|
+
// ═══════════════════════════════════════════════════════════════
|
|
891
|
+
|
|
892
|
+
export const CodeDropReel: React.FC = () => {
|
|
893
|
+
const frame = useCurrentFrame();
|
|
894
|
+
const { fps } = useVideoConfig();
|
|
895
|
+
|
|
896
|
+
return (
|
|
897
|
+
<AbsoluteFill style={{ backgroundColor: C.bg, fontFamily: FONT }}>
|
|
898
|
+
<Background frame={frame} />
|
|
899
|
+
{/*
|
|
900
|
+
0 → 20s: blank dark canvas. User layers website video clips on top.
|
|
901
|
+
20 → 26s: "Build Within an Hour" Claude pitch screen.
|
|
902
|
+
26 → 37s: CTA — Like, Follow, Comment AI.
|
|
903
|
+
*/}
|
|
904
|
+
<BuildScene frame={frame} fps={fps} />
|
|
905
|
+
<CTAScene frame={frame} fps={fps} />
|
|
906
|
+
</AbsoluteFill>
|
|
907
|
+
);
|
|
908
|
+
};
|