@devinilabs/reelstack 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (145) hide show
  1. package/LICENSE +128 -0
  2. package/README.md +125 -0
  3. package/cli/beats.js +124 -0
  4. package/cli/bootstrap.js +124 -0
  5. package/cli/capture.js +34 -0
  6. package/cli/direction.js +114 -0
  7. package/cli/icons.js +49 -0
  8. package/cli/index.js +101 -0
  9. package/cli/init.js +253 -0
  10. package/cli/license.js +168 -0
  11. package/cli/lint.js +865 -0
  12. package/cli/preview.js +59 -0
  13. package/cli/render.js +404 -0
  14. package/cli/scaffold.js +239 -0
  15. package/cli/smoke.js +76 -0
  16. package/cli/update.js +26 -0
  17. package/cli/utils.js +184 -0
  18. package/docs/buyers-guide.md +220 -0
  19. package/docs/design-discipline.md +130 -0
  20. package/docs/family-galleries/dark.md +95 -0
  21. package/docs/family-galleries/forbidden.md +78 -0
  22. package/docs/family-galleries/glass.md +98 -0
  23. package/docs/family-galleries/paper.md +82 -0
  24. package/docs/family-galleries/warm.md +86 -0
  25. package/docs/superpowers/plans/2026-05-09-reelstack-init-readiness-gate.md +1166 -0
  26. package/docs/superpowers/specs/2026-05-09-reelstack-init-readiness-gate-design.md +233 -0
  27. package/families/dark/components/DriftingSpotlights.tsx +59 -0
  28. package/families/dark/components/FilmGrain.tsx +44 -0
  29. package/families/dark/components/ForestCard.tsx +43 -0
  30. package/families/dark/components/GridBackground.tsx +29 -0
  31. package/families/dark/components/RadialVignette.tsx +21 -0
  32. package/families/dark/components/Scanlines.tsx +35 -0
  33. package/families/dark/components/SegmentOpacity.ts +37 -0
  34. package/families/dark/components/index.ts +13 -0
  35. package/families/dark/index.ts +31 -0
  36. package/families/dark/palette.ts +98 -0
  37. package/families/dark/presets/claudedispatch.ts +46 -0
  38. package/families/dark/presets/codedrop.ts +37 -0
  39. package/families/dark/presets/gpt55.ts +54 -0
  40. package/families/dark/presets/notebooklm.ts +50 -0
  41. package/families/dark/presets/resourcescta.ts +35 -0
  42. package/families/dark/presets/skills.ts +40 -0
  43. package/families/dark/presets/stitch.ts +46 -0
  44. package/families/dark/presets/stitch2.ts +43 -0
  45. package/families/dark/typography.ts +16 -0
  46. package/families/forbidden/components/ForbiddenCausticBlobs.tsx +52 -0
  47. package/families/forbidden/components/NewsprintTexture.tsx +28 -0
  48. package/families/forbidden/components/TintedShadow.tsx +36 -0
  49. package/families/forbidden/components/index.ts +38 -0
  50. package/families/forbidden/index.ts +17 -0
  51. package/families/forbidden/palette.ts +88 -0
  52. package/families/forbidden/presets/heretic.ts +44 -0
  53. package/families/forbidden/typography.ts +18 -0
  54. package/families/glass/components/BreakdownCard.tsx +158 -0
  55. package/families/glass/components/CausticBlobs.tsx +49 -0
  56. package/families/glass/components/Counter.tsx +72 -0
  57. package/families/glass/components/EyebrowPill.tsx +59 -0
  58. package/families/glass/components/FilmStrip.tsx +202 -0
  59. package/families/glass/components/FloatingGlyphs.tsx +78 -0
  60. package/families/glass/components/GlassCard.tsx +58 -0
  61. package/families/glass/components/GlassCardBezel.tsx +45 -0
  62. package/families/glass/components/HairlineGrid.tsx +30 -0
  63. package/families/glass/components/IridescentRing.tsx +114 -0
  64. package/families/glass/components/IridescentText.tsx +98 -0
  65. package/families/glass/components/LightBeam.tsx +46 -0
  66. package/families/glass/components/ParticleBurst.tsx +62 -0
  67. package/families/glass/components/SonarRings.tsx +81 -0
  68. package/families/glass/components/StaggeredWords.tsx +74 -0
  69. package/families/glass/components/index.ts +20 -0
  70. package/families/glass/index.ts +31 -0
  71. package/families/glass/palette.ts +93 -0
  72. package/families/glass/presets/claudewatch.ts +64 -0
  73. package/families/glass/presets/claudewatchcta.ts +43 -0
  74. package/families/glass/presets/graphify.ts +45 -0
  75. package/families/glass/presets/gstack.ts +48 -0
  76. package/families/glass/presets/jcode.ts +50 -0
  77. package/families/glass/presets/lilagents.ts +52 -0
  78. package/families/glass/presets/paperclip.ts +43 -0
  79. package/families/glass/typography.ts +15 -0
  80. package/families/index.ts +49 -0
  81. package/families/paper/components/CardSpring.tsx +42 -0
  82. package/families/paper/components/CreamGrid.tsx +26 -0
  83. package/families/paper/components/EditorialSerifText.tsx +51 -0
  84. package/families/paper/components/GreenAccentCard.tsx +10 -0
  85. package/families/paper/components/PaperShadow.tsx +30 -0
  86. package/families/paper/components/ScaleBlurText.tsx +40 -0
  87. package/families/paper/components/index.ts +11 -0
  88. package/families/paper/index.ts +23 -0
  89. package/families/paper/palette.ts +102 -0
  90. package/families/paper/presets/designreel.ts +32 -0
  91. package/families/paper/presets/devini3d.ts +45 -0
  92. package/families/paper/presets/justdrop.ts +39 -0
  93. package/families/paper/presets/opus.ts +48 -0
  94. package/families/paper/typography.ts +17 -0
  95. package/families/warm/components/AccentGlow.tsx +60 -0
  96. package/families/warm/components/BentoCell.tsx +56 -0
  97. package/families/warm/components/BentoGrid.tsx +30 -0
  98. package/families/warm/components/FilmGrain.tsx +36 -0
  99. package/families/warm/components/ScaleBlurCounter.tsx +71 -0
  100. package/families/warm/components/WarmSurface.tsx +35 -0
  101. package/families/warm/components/index.ts +11 -0
  102. package/families/warm/index.ts +19 -0
  103. package/families/warm/palette.ts +81 -0
  104. package/families/warm/presets/huashu.ts +49 -0
  105. package/families/warm/presets/mempalace.ts +51 -0
  106. package/families/warm/typography.ts +17 -0
  107. package/package.json +85 -0
  108. package/reference/dark/claudedispatch.tsx +2441 -0
  109. package/reference/dark/notebooklm.tsx +2316 -0
  110. package/reference/dark/stitch.tsx +3040 -0
  111. package/reference/forbidden/heretic.tsx +2636 -0
  112. package/reference/glass/claudewatch.tsx +3827 -0
  113. package/reference/glass/graphify.tsx +2418 -0
  114. package/reference/glass/paperclip.tsx +2218 -0
  115. package/reference/paper/designreel.tsx +883 -0
  116. package/reference/paper/justdrop.tsx +1898 -0
  117. package/reference/paper/opus.tsx +1770 -0
  118. package/reference/warm/huashu.tsx +3413 -0
  119. package/reference/warm/mempalace.tsx +2909 -0
  120. package/skill/SKILL.md +229 -0
  121. package/skill/commands/reelstack-beats.md +20 -0
  122. package/skill/commands/reelstack-capture.md +24 -0
  123. package/skill/commands/reelstack-critique.md +15 -0
  124. package/skill/commands/reelstack-dark.md +40 -0
  125. package/skill/commands/reelstack-direction.md +17 -0
  126. package/skill/commands/reelstack-forbidden.md +25 -0
  127. package/skill/commands/reelstack-glass.md +39 -0
  128. package/skill/commands/reelstack-icons.md +22 -0
  129. package/skill/commands/reelstack-init.md +17 -0
  130. package/skill/commands/reelstack-lint.md +22 -0
  131. package/skill/commands/reelstack-paper.md +36 -0
  132. package/skill/commands/reelstack-render.md +20 -0
  133. package/skill/commands/reelstack-warm.md +36 -0
  134. package/templates/dark/template.tsx +115 -0
  135. package/templates/forbidden/template.tsx +111 -0
  136. package/templates/glass/template.tsx +201 -0
  137. package/templates/paper/template.tsx +133 -0
  138. package/templates/warm/template.tsx +210 -0
  139. package/utils/ai-purple-blocklist.ts +13 -0
  140. package/utils/banned-fonts.ts +11 -0
  141. package/utils/cubic-bezier.ts +36 -0
  142. package/utils/easing.ts +84 -0
  143. package/utils/grid.ts +13 -0
  144. package/utils/render-presets.json +56 -0
  145. package/utils/safe-zones.tsx +57 -0
@@ -0,0 +1,3827 @@
1
+ /**
2
+ * REFERENCE — ClaudeWatchReel (Family: glass)
3
+ *
4
+ * Canonical example of the glass family's motion vocabulary, frame-locked
5
+ * BEAT structure, and scene choreography. Bundled with ReelStack v1.1.1+
6
+ * for STUDY and pattern adaptation.
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/ClaudeWatchReel.tsx
15
+ * Bundled at: 2026-05-08T18:50:39.504Z
16
+ */
17
+ import React from "react";
18
+ import {
19
+ AbsoluteFill,
20
+ Audio,
21
+ Easing,
22
+ Img,
23
+ OffthreadVideo,
24
+ Sequence,
25
+ interpolate,
26
+ spring,
27
+ staticFile,
28
+ useCurrentFrame,
29
+ } from "remotion";
30
+ import { ds } from "./designSystem";
31
+
32
+ // ═══════════════════════════════════════════════════════════════
33
+ // TIMING — 105.20s @ 30fps = 3156 frames
34
+ // Beats locked to /tmp/claude-watch.srt (whisper-cli base.en)
35
+ // ═══════════════════════════════════════════════════════════════
36
+ export const CW_TOTAL = 3156;
37
+
38
+ export const CW = {
39
+ hook: 0, // 00.00s — "This custom Claude skill literally watches video..."
40
+ viral: 403, // 13.44s — "Here's how you can use it. Say a YouTube video goes viral."
41
+ demo: 491, // 16.36s — "You just type /claude-watch, paste the URL, hit enter." ← clip 1
42
+ result: 718, // 23.92s — "A 30-minute video, broken down and rewritten..." ← clip 2
43
+ decode: 874, // 29.14s — "You can also do this with every top creator..."
44
+ brain: 1229, // 40.96s — "Your second brain just keeps getting smarter..."
45
+ ytCta: 1429, // 47.64s — "For the full setup tutorial, check my YouTube channel..."
46
+ hood: 1546, // 51.52s — "Now here's how it actually works under the hood..."
47
+ ytdlp: 1852, // 61.72s — "It uses yt-dlp to pull the video from basically any site..."
48
+ ffmpeg: 2153, // 71.76s — "Then FFmpeg rips out frames... transcribed... Whisper on Groq..."
49
+ flipbook: 2501, // 83.36s — "Then everything gets handed to Claude with the timestamps..."
50
+ local: 2819, // 93.96s — "It knows exactly what's on screen... All running locally..."
51
+ comment: 3053, // 101.76s — "Comment AI to get this custom created Claude skill."
52
+ end: 3156, // 105.20s
53
+ } as const;
54
+
55
+ // GSAP-equivalent easing curves mapped onto Remotion's `interpolate`.
56
+ export const ease = {
57
+ power2Out: Easing.bezier(0.165, 0.84, 0.44, 1),
58
+ power3Out: Easing.bezier(0.215, 0.61, 0.355, 1),
59
+ power4Out: Easing.bezier(0.165, 0.84, 0.44, 1),
60
+ expoOut: Easing.bezier(0.19, 1, 0.22, 1),
61
+ expoIn: Easing.bezier(0.7, 0, 0.84, 0),
62
+ backOut: Easing.bezier(0.34, 1.56, 0.64, 1),
63
+ inOut: Easing.bezier(0.45, 0, 0.55, 1),
64
+ };
65
+
66
+ // ═══════════════════════════════════════════════════════════════
67
+ // BRIGHT LAVENDER PALETTE — Graphify-style luminous base + scene accents
68
+ // ═══════════════════════════════════════════════════════════════
69
+ export const C = {
70
+ bg: "#EFEAF2",
71
+ bgWarm: "#E9E2EE",
72
+ bgCool: "#E4E9F2",
73
+ ink: "#0E0E12",
74
+ inkSoft: "#26242C",
75
+ inkMuted: "#5A5867",
76
+ inkDim: "#86848F",
77
+ hairline: "rgba(15,15,22,0.08)",
78
+ // Scene-coded accents — brightened / saturated versions of original six
79
+ claude: "#FF7A4D", // vivid coral — Claude brand (skill identity)
80
+ film: "#E89414", // saturated marigold — frame-strip (visuals/frames) — darker than pale gold for lavender contrast
81
+ vector: "#6E6EFF", // electric indigo — Obsidian / vector library / second brain
82
+ success: "#1FD891", // bright mint — transcribe / free / local
83
+ crimson: "#FF5C7E", // hot rose — anti-pattern (transcript-only / no API)
84
+ plasma: "#B58CFF", // electric violet — Whisper
85
+ // Glass tokens — adopted from GraphifyReel
86
+ glassFill: "rgba(255,255,255,0.42)",
87
+ glassFillStrong: "rgba(255,255,255,0.62)",
88
+ glassBorder: "rgba(255,255,255,0.85)",
89
+ glassInner: "rgba(255,255,255,0.50)",
90
+ } as const;
91
+
92
+ export const FONT = ds.font.sans;
93
+ export const MONO = ds.font.mono;
94
+
95
+ export const GLASS_SHADOW = [
96
+ "0 24px 48px -12px rgba(120,100,180,0.22)",
97
+ "0 8px 16px -4px rgba(120,100,180,0.12)",
98
+ "inset 0 1.5px 0 rgba(255,255,255,0.95)",
99
+ "inset 0 -1px 0 rgba(255,255,255,0.30)",
100
+ ].join(", ");
101
+
102
+ export const glassBase: React.CSSProperties = {
103
+ background: C.glassFill,
104
+ backdropFilter: "blur(32px) saturate(180%)",
105
+ WebkitBackdropFilter: "blur(32px) saturate(180%)",
106
+ border: `1.5px solid ${C.glassBorder}`,
107
+ boxShadow: GLASS_SHADOW,
108
+ };
109
+
110
+ const SAFE_TOP = 290;
111
+
112
+ // ═══════════════════════════════════════════════════════════════
113
+ // PERPETUAL CAUSTIC BLOBS — Claude + film + vector tints
114
+ // ═══════════════════════════════════════════════════════════════
115
+ export const CausticBlobs: React.FC = () => {
116
+ const frame = useCurrentFrame();
117
+ const t = frame / 30;
118
+ const blob = (
119
+ color: string,
120
+ size: number,
121
+ cx: number,
122
+ cy: number,
123
+ speedX: number,
124
+ speedY: number,
125
+ phase: number,
126
+ opacity = 0.55,
127
+ ): React.CSSProperties => ({
128
+ position: "absolute",
129
+ width: size,
130
+ height: size,
131
+ borderRadius: "50%",
132
+ background: `radial-gradient(circle at 50% 50%, ${color} 0%, ${color}99 30%, transparent 70%)`,
133
+ filter: "blur(120px)",
134
+ left: cx + Math.sin(t * speedX + phase) * 220,
135
+ top: cy + Math.cos(t * speedY + phase) * 160,
136
+ opacity,
137
+ pointerEvents: "none",
138
+ willChange: "transform",
139
+ });
140
+ return (
141
+ <AbsoluteFill style={{ overflow: "hidden" }}>
142
+ <div style={blob(C.claude, 720, 80, 220, 0.18, 0.13, 0.0, 0.55)} />
143
+ <div style={blob(C.vector, 820, 600, 540, 0.14, 0.16, 1.2, 0.45)} />
144
+ <div style={blob(C.film, 700, 200, 1100, 0.11, 0.19, 2.4, 0.5)} />
145
+ <div style={blob(C.plasma, 540, 720, 1500, 0.16, 0.12, 3.6, 0.35)} />
146
+ <div style={blob(C.claude, 600, 100, 1700, 0.12, 0.18, 4.2, 0.4)} />
147
+ </AbsoluteFill>
148
+ );
149
+ };
150
+
151
+ // ═══════════════════════════════════════════════════════════════
152
+ // IRIDESCENT RING — conic gradient halo
153
+ // ═══════════════════════════════════════════════════════════════
154
+ const IridescentRing: React.FC<{
155
+ size: number;
156
+ thickness?: number;
157
+ speed?: number;
158
+ borderRadius?: number;
159
+ }> = ({ size, thickness = 4, speed = 0.5, borderRadius = 9999 }) => {
160
+ const frame = useCurrentFrame();
161
+ const angle = (frame * speed) % 360;
162
+ return (
163
+ <div
164
+ style={{
165
+ position: "absolute",
166
+ inset: -thickness,
167
+ width: size + thickness * 2,
168
+ height: size + thickness * 2,
169
+ borderRadius: borderRadius + thickness,
170
+ background: `conic-gradient(from ${angle}deg, ${C.claude}, ${C.film}, ${C.vector}, ${C.plasma}, ${C.claude})`,
171
+ padding: thickness,
172
+ pointerEvents: "none",
173
+ opacity: 0.85,
174
+ }}
175
+ >
176
+ <div
177
+ style={{
178
+ width: "100%",
179
+ height: "100%",
180
+ background: C.bg,
181
+ borderRadius,
182
+ }}
183
+ />
184
+ </div>
185
+ );
186
+ };
187
+
188
+ // ═══════════════════════════════════════════════════════════════
189
+ // GLASS CARD + EYEBROW PILL
190
+ // ═══════════════════════════════════════════════════════════════
191
+ export const EyebrowPill: React.FC<{ children: React.ReactNode; dot?: string }> = ({
192
+ children,
193
+ dot = C.claude,
194
+ }) => (
195
+ <div
196
+ style={{
197
+ ...glassBase,
198
+ borderRadius: 9999,
199
+ padding: "10px 22px",
200
+ display: "inline-flex",
201
+ alignItems: "center",
202
+ gap: 10,
203
+ fontFamily: MONO,
204
+ fontSize: 18,
205
+ fontWeight: 500,
206
+ color: C.inkSoft,
207
+ letterSpacing: "0.18em",
208
+ textTransform: "uppercase",
209
+ }}
210
+ >
211
+ <div
212
+ style={{
213
+ width: 7,
214
+ height: 7,
215
+ borderRadius: "50%",
216
+ background: dot,
217
+ boxShadow: `0 0 14px ${dot}`,
218
+ }}
219
+ />
220
+ {children}
221
+ </div>
222
+ );
223
+
224
+ // ═══════════════════════════════════════════════════════════════
225
+ // STAGGERED WORDS — fade-up + blur, GSAP stagger
226
+ // ═══════════════════════════════════════════════════════════════
227
+ const StaggeredWords: React.FC<{
228
+ text: string;
229
+ startFrame: number;
230
+ perWordDelay?: number;
231
+ duration?: number;
232
+ fontSize: number;
233
+ fontWeight?: number;
234
+ color?: string;
235
+ letterSpacing?: string;
236
+ lineHeight?: number;
237
+ align?: "left" | "center" | "right";
238
+ fontFamily?: string;
239
+ highlight?: string;
240
+ highlightColor?: string;
241
+ }> = ({
242
+ text,
243
+ startFrame,
244
+ perWordDelay = 4,
245
+ duration = 24,
246
+ fontSize,
247
+ fontWeight = 700,
248
+ color = C.ink,
249
+ letterSpacing = "-0.035em",
250
+ lineHeight = 1.02,
251
+ align = "left",
252
+ fontFamily = FONT,
253
+ highlight,
254
+ highlightColor = C.claude,
255
+ }) => {
256
+ const frame = useCurrentFrame();
257
+ const words = text.split(" ");
258
+ return (
259
+ <div
260
+ style={{
261
+ display: "flex",
262
+ flexWrap: "wrap",
263
+ gap: "0.25em",
264
+ justifyContent:
265
+ align === "center" ? "center" : align === "right" ? "flex-end" : "flex-start",
266
+ textAlign: align,
267
+ fontFamily,
268
+ fontSize,
269
+ fontWeight,
270
+ color,
271
+ letterSpacing,
272
+ lineHeight,
273
+ }}
274
+ >
275
+ {words.map((w, i) => {
276
+ const wordStart = startFrame + i * perWordDelay;
277
+ const local = frame - wordStart;
278
+ const opacity = interpolate(local, [0, duration], [0, 1], {
279
+ extrapolateLeft: "clamp",
280
+ extrapolateRight: "clamp",
281
+ easing: ease.expoOut,
282
+ });
283
+ const y = interpolate(local, [0, duration], [40, 0], {
284
+ extrapolateLeft: "clamp",
285
+ extrapolateRight: "clamp",
286
+ easing: ease.power3Out,
287
+ });
288
+ const blur = interpolate(local, [0, duration], [12, 0], {
289
+ extrapolateLeft: "clamp",
290
+ extrapolateRight: "clamp",
291
+ });
292
+ const isHighlight = highlight && w.toLowerCase().includes(highlight.toLowerCase());
293
+ return (
294
+ <span
295
+ key={i}
296
+ style={{
297
+ display: "inline-block",
298
+ opacity,
299
+ transform: `translateY(${y}px)`,
300
+ filter: `blur(${blur}px)`,
301
+ color: isHighlight ? highlightColor : color,
302
+ willChange: "transform, opacity, filter",
303
+ }}
304
+ >
305
+ {w}
306
+ </span>
307
+ );
308
+ })}
309
+ </div>
310
+ );
311
+ };
312
+
313
+ // ═══════════════════════════════════════════════════════════════
314
+ // SONAR RINGS / PARTICLE BURST / LIGHT BEAM / FLOATING GLYPHS
315
+ // ═══════════════════════════════════════════════════════════════
316
+ export const SonarRings: React.FC<{ accent?: string; secondary?: string }> = ({
317
+ accent = C.claude,
318
+ secondary = C.film,
319
+ }) => {
320
+ const frame = useCurrentFrame();
321
+ const rings = [0, 18, 36, 54, 72];
322
+ return (
323
+ <AbsoluteFill style={{ alignItems: "center", justifyContent: "center" }}>
324
+ {rings.map((birth, i) => {
325
+ const local = frame - birth;
326
+ const cycle = local % 90;
327
+ if (local < 0) return null;
328
+ const scale = interpolate(cycle, [0, 90], [0.05, 1.4], {
329
+ extrapolateLeft: "clamp",
330
+ extrapolateRight: "clamp",
331
+ easing: ease.expoOut,
332
+ });
333
+ const op = interpolate(cycle, [0, 30, 90], [0, 0.1, 0], {
334
+ extrapolateLeft: "clamp",
335
+ extrapolateRight: "clamp",
336
+ });
337
+ return (
338
+ <div
339
+ key={i}
340
+ style={{
341
+ position: "absolute",
342
+ width: 1400,
343
+ height: 1400,
344
+ borderRadius: "50%",
345
+ border: `2px solid ${i % 2 === 0 ? `${accent}aa` : `${secondary}aa`}`,
346
+ transform: `scale(${scale})`,
347
+ opacity: op,
348
+ willChange: "transform, opacity",
349
+ }}
350
+ />
351
+ );
352
+ })}
353
+ </AbsoluteFill>
354
+ );
355
+ };
356
+
357
+ export const ParticleBurst: React.FC<{ count?: number; palette?: string[] }> = ({
358
+ count = 48,
359
+ palette = [C.claude, C.film, C.vector, C.plasma],
360
+ }) => {
361
+ const frame = useCurrentFrame();
362
+ const particles = Array.from({ length: count }, (_, i) => {
363
+ const angle = (i / count) * Math.PI * 2 + i * 0.31;
364
+ const distance = 200 + (i % 7) * 80;
365
+ const size = 6 + (i % 4) * 4;
366
+ const color = palette[i % palette.length];
367
+ const delay = (i * 0.7) % 24;
368
+ return { angle, distance, size, color, delay };
369
+ });
370
+ return (
371
+ <AbsoluteFill style={{ alignItems: "center", justifyContent: "center" }}>
372
+ {particles.map((p, i) => {
373
+ const local = Math.max(0, frame - p.delay);
374
+ const burst = interpolate(local, [0, 50], [0, 1], {
375
+ extrapolateLeft: "clamp",
376
+ extrapolateRight: "clamp",
377
+ easing: ease.expoOut,
378
+ });
379
+ const drift = Math.sin((frame + i * 12) * 0.04) * 12;
380
+ const x = Math.cos(p.angle) * p.distance * burst + drift;
381
+ const y = Math.sin(p.angle) * p.distance * burst * 1.4 + drift * 0.6;
382
+ const op = interpolate(local, [0, 20, 100, 180], [0, 0.85, 0.5, 0.2], {
383
+ extrapolateLeft: "clamp",
384
+ extrapolateRight: "clamp",
385
+ });
386
+ return (
387
+ <div
388
+ key={i}
389
+ style={{
390
+ position: "absolute",
391
+ width: p.size,
392
+ height: p.size,
393
+ borderRadius: "50%",
394
+ background: p.color,
395
+ boxShadow: `0 0 ${p.size * 2}px ${p.color}`,
396
+ transform: `translate(${x}px, ${y}px)`,
397
+ opacity: op,
398
+ willChange: "transform, opacity",
399
+ }}
400
+ />
401
+ );
402
+ })}
403
+ </AbsoluteFill>
404
+ );
405
+ };
406
+
407
+ export const LightBeam: React.FC<{ delay: number; angle: number }> = ({ delay, angle }) => {
408
+ const frame = useCurrentFrame();
409
+ const local = frame - delay;
410
+ const progress = interpolate(local, [0, 60], [-0.3, 1.3], {
411
+ extrapolateLeft: "clamp",
412
+ extrapolateRight: "clamp",
413
+ easing: ease.expoOut,
414
+ });
415
+ const op = interpolate(local, [0, 12, 50, 60], [0, 0.7, 0.7, 0], {
416
+ extrapolateLeft: "clamp",
417
+ extrapolateRight: "clamp",
418
+ });
419
+ return (
420
+ <div
421
+ style={{
422
+ position: "absolute",
423
+ top: "50%",
424
+ left: "50%",
425
+ width: 2200,
426
+ height: 100,
427
+ background: `linear-gradient(90deg, transparent 0%, ${C.claude}88 30%, ${C.film}cc 50%, ${C.vector}88 70%, transparent 100%)`,
428
+ filter: "blur(20px)",
429
+ transform: `translate(-50%, -50%) rotate(${angle}deg) translateX(${(progress - 0.5) * 1500}px)`,
430
+ opacity: op,
431
+ willChange: "transform, opacity",
432
+ }}
433
+ />
434
+ );
435
+ };
436
+
437
+ export const FloatingGlyphs: React.FC = () => {
438
+ const frame = useCurrentFrame();
439
+ const glyphs = [
440
+ { x: 120, y: 380, size: 80, delay: 4, rot: -8 },
441
+ { x: 880, y: 340, size: 60, delay: 12, rot: 12 },
442
+ { x: 80, y: 1240, size: 70, delay: 22, rot: 6 },
443
+ { x: 920, y: 1280, size: 90, delay: 32, rot: -10 },
444
+ { x: 200, y: 800, size: 50, delay: 42, rot: 14 },
445
+ { x: 820, y: 760, size: 55, delay: 52, rot: -4 },
446
+ ];
447
+ return (
448
+ <>
449
+ {glyphs.map((g, i) => {
450
+ const local = frame - g.delay;
451
+ const op = interpolate(local, [0, 30], [0, 1], {
452
+ extrapolateLeft: "clamp",
453
+ extrapolateRight: "clamp",
454
+ easing: ease.expoOut,
455
+ });
456
+ const float = Math.sin((frame + i * 22) * 0.05) * 8;
457
+ const scale = spring({
458
+ frame: local,
459
+ fps: 30,
460
+ config: { damping: 12, stiffness: 110 },
461
+ from: 0.6,
462
+ to: 1,
463
+ });
464
+ return (
465
+ <div
466
+ key={i}
467
+ style={{
468
+ position: "absolute",
469
+ left: g.x,
470
+ top: g.y + float,
471
+ width: g.size,
472
+ height: g.size,
473
+ borderRadius: g.size * 0.28,
474
+ background: C.glassFill,
475
+ backdropFilter: "blur(20px) saturate(160%)",
476
+ WebkitBackdropFilter: "blur(20px) saturate(160%)",
477
+ border: `1px solid ${C.glassBorder}`,
478
+ boxShadow: "inset 0 1px 0 rgba(255,255,255,0.85), 0 8px 16px -4px rgba(120,80,120,0.18)",
479
+ transform: `rotate(${g.rot}deg) scale(${scale})`,
480
+ opacity: op * 0.55,
481
+ willChange: "transform, opacity",
482
+ }}
483
+ />
484
+ );
485
+ })}
486
+ </>
487
+ );
488
+ };
489
+
490
+ // ═══════════════════════════════════════════════════════════════
491
+ // LAPTOP FRAME — chrome wrapper for embedded clips
492
+ // ═══════════════════════════════════════════════════════════════
493
+ const LaptopFrame: React.FC<{
494
+ width: number;
495
+ height: number;
496
+ children: React.ReactNode;
497
+ title?: string;
498
+ }> = ({ width, height, children, title = "claude-watch.mov" }) => (
499
+ <div
500
+ style={{
501
+ width,
502
+ height,
503
+ borderRadius: 28,
504
+ overflow: "hidden",
505
+ ...glassBase,
506
+ padding: 0,
507
+ display: "flex",
508
+ flexDirection: "column",
509
+ }}
510
+ >
511
+ {/* Window chrome */}
512
+ <div
513
+ style={{
514
+ height: 44,
515
+ background: "rgba(255,255,255,0.62)",
516
+ borderBottom: `1px solid ${C.hairline}`,
517
+ display: "flex",
518
+ alignItems: "center",
519
+ padding: "0 18px",
520
+ gap: 10,
521
+ }}
522
+ >
523
+ <div style={{ width: 12, height: 12, borderRadius: "50%", background: "#FF5F57" }} />
524
+ <div style={{ width: 12, height: 12, borderRadius: "50%", background: "#FEBC2E" }} />
525
+ <div style={{ width: 12, height: 12, borderRadius: "50%", background: "#28C840" }} />
526
+ <div
527
+ style={{
528
+ marginLeft: 16,
529
+ fontFamily: MONO,
530
+ fontSize: 14,
531
+ color: C.inkMuted,
532
+ letterSpacing: "0.05em",
533
+ }}
534
+ >
535
+ {title}
536
+ </div>
537
+ </div>
538
+ {/* Clip body */}
539
+ <div style={{ flex: 1, position: "relative", background: "#0E0E12" }}>{children}</div>
540
+ </div>
541
+ );
542
+
543
+ // ═══════════════════════════════════════════════════════════════
544
+ // FILM STRIP — sprocket-perforated horizontal frame ribbon
545
+ // ═══════════════════════════════════════════════════════════════
546
+ const FilmStrip: React.FC<{
547
+ speed?: number;
548
+ height?: number;
549
+ width?: number;
550
+ thumbColor?: string;
551
+ thumbCount?: number;
552
+ }> = ({ speed = 1.5, height = 110, width = 1080, thumbColor = C.inkSoft, thumbCount = 8 }) => {
553
+ const frame = useCurrentFrame();
554
+ const offset = (frame * speed) % (140 + 12);
555
+ const thumbW = 140;
556
+ const gap = 12;
557
+ return (
558
+ <div
559
+ style={{
560
+ width,
561
+ height,
562
+ background: C.ink,
563
+ position: "relative",
564
+ overflow: "hidden",
565
+ borderRadius: 6,
566
+ boxShadow: `0 6px 14px -6px rgba(0,0,0,0.35), inset 0 0 0 1px rgba(255,255,255,0.08)`,
567
+ }}
568
+ >
569
+ {/* Sprocket holes top */}
570
+ <div
571
+ style={{
572
+ position: "absolute",
573
+ top: 4,
574
+ left: 0,
575
+ right: 0,
576
+ height: 12,
577
+ backgroundImage: `repeating-linear-gradient(90deg, ${C.bg} 0 14px, transparent 14px 30px)`,
578
+ opacity: 0.85,
579
+ }}
580
+ />
581
+ {/* Sprocket holes bottom */}
582
+ <div
583
+ style={{
584
+ position: "absolute",
585
+ bottom: 4,
586
+ left: 0,
587
+ right: 0,
588
+ height: 12,
589
+ backgroundImage: `repeating-linear-gradient(90deg, ${C.bg} 0 14px, transparent 14px 30px)`,
590
+ opacity: 0.85,
591
+ }}
592
+ />
593
+ {/* Frames */}
594
+ <div
595
+ style={{
596
+ position: "absolute",
597
+ top: 22,
598
+ bottom: 22,
599
+ left: -offset,
600
+ display: "flex",
601
+ gap,
602
+ }}
603
+ >
604
+ {Array.from({ length: thumbCount + 4 }).map((_, i) => {
605
+ const tint = [C.claude, C.film, C.vector, C.plasma, C.success][i % 5];
606
+ return (
607
+ <div
608
+ key={i}
609
+ style={{
610
+ width: thumbW,
611
+ height: "100%",
612
+ borderRadius: 4,
613
+ background: `linear-gradient(135deg, ${thumbColor}, ${tint}88)`,
614
+ boxShadow: "inset 0 0 0 1px rgba(255,255,255,0.18)",
615
+ position: "relative",
616
+ }}
617
+ >
618
+ <div
619
+ style={{
620
+ position: "absolute",
621
+ top: "50%",
622
+ left: "50%",
623
+ transform: "translate(-50%,-50%)",
624
+ width: 0,
625
+ height: 0,
626
+ borderTop: "10px solid transparent",
627
+ borderBottom: "10px solid transparent",
628
+ borderLeft: `16px solid rgba(255,255,255,0.55)`,
629
+ }}
630
+ />
631
+ </div>
632
+ );
633
+ })}
634
+ </div>
635
+ </div>
636
+ );
637
+ };
638
+
639
+ // ═══════════════════════════════════════════════════════════════
640
+ // SCENE 1 — HOOK (frames 0–402, 13.44s)
641
+ // "This custom Claude skill literally watches video frame by frame.
642
+ // Not just the transcript — the actual visuals.
643
+ // Because in any good video, half the meaning is happening on screen, not in the words.
644
+ // Now Claude sees both."
645
+ // ═══════════════════════════════════════════════════════════════
646
+ const HookScene: React.FC = () => {
647
+ const frame = useCurrentFrame();
648
+
649
+ // Claude logo entrance — gentle scale + float
650
+ const logoScale = spring({
651
+ frame: frame - 4,
652
+ fps: 30,
653
+ config: { damping: 13, stiffness: 120 },
654
+ from: 0.4,
655
+ to: 1,
656
+ });
657
+ const logoFloat = Math.sin(frame * 0.04) * 8;
658
+ const logoGlowPulse = interpolate(
659
+ Math.sin(frame * 0.06),
660
+ [-1, 1],
661
+ [0.6, 1],
662
+ );
663
+
664
+ // Strikethrough on "transcript" — kicks in at frame 150
665
+ const strikeWidth = interpolate(frame, [150, 175], [0, 100], {
666
+ extrapolateLeft: "clamp",
667
+ extrapolateRight: "clamp",
668
+ easing: ease.power3Out,
669
+ });
670
+
671
+ // TRANSCRIPT vs VISUALS card collide → BOTH at frame 280
672
+ const collideStart = 260;
673
+ const collideLocal = frame - collideStart;
674
+ const transcriptX = interpolate(collideLocal, [0, 36], [-380, 0], {
675
+ extrapolateLeft: "clamp",
676
+ extrapolateRight: "clamp",
677
+ easing: ease.power3Out,
678
+ });
679
+ const visualsX = interpolate(collideLocal, [0, 36], [380, 0], {
680
+ extrapolateLeft: "clamp",
681
+ extrapolateRight: "clamp",
682
+ easing: ease.power3Out,
683
+ });
684
+ const cardOpacity = interpolate(collideLocal, [0, 12, 60, 75], [0, 1, 1, 0], {
685
+ extrapolateLeft: "clamp",
686
+ extrapolateRight: "clamp",
687
+ });
688
+ const bothScale = spring({
689
+ frame: collideLocal - 65,
690
+ fps: 30,
691
+ config: { damping: 11, stiffness: 130 },
692
+ from: 0.3,
693
+ to: 1,
694
+ });
695
+ const bothOpacity = interpolate(collideLocal, [65, 85], [0, 1], {
696
+ extrapolateLeft: "clamp",
697
+ extrapolateRight: "clamp",
698
+ });
699
+
700
+ return (
701
+ <AbsoluteFill>
702
+ {/* Scene-specific motion atmosphere */}
703
+ <SonarRings accent={C.claude} secondary={C.film} />
704
+ {frame < 90 && <ParticleBurst count={48} />}
705
+ {frame < 180 && (
706
+ <>
707
+ <LightBeam delay={20} angle={-8} />
708
+ <LightBeam delay={50} angle={12} />
709
+ <LightBeam delay={95} angle={-4} />
710
+ </>
711
+ )}
712
+
713
+ {/* Eyebrow */}
714
+ <div style={{ position: "absolute", top: SAFE_TOP, left: 60 }}>
715
+ <EyebrowPill dot={C.claude}>01 / WATCH</EyebrowPill>
716
+ </div>
717
+
718
+ {/* Claude logo — fills the upper band above the headline */}
719
+ <div
720
+ style={{
721
+ position: "absolute",
722
+ top: 440,
723
+ left: "50%",
724
+ width: 300,
725
+ height: 300,
726
+ transform: `translate(-50%, ${logoFloat}px) scale(${logoScale})`,
727
+ display: "flex",
728
+ alignItems: "center",
729
+ justifyContent: "center",
730
+ }}
731
+ >
732
+ <div
733
+ style={{
734
+ position: "absolute",
735
+ inset: -60,
736
+ background: `radial-gradient(circle at 50% 50%, ${C.claude}55 0%, transparent 65%)`,
737
+ filter: "blur(32px)",
738
+ opacity: logoGlowPulse,
739
+ }}
740
+ />
741
+ <Img
742
+ src={staticFile("captures/your-asset.png" /* REFERENCE-STRIP */)}
743
+ style={{
744
+ width: 300,
745
+ height: 300,
746
+ filter: `drop-shadow(0 16px 40px ${C.claude}88)`,
747
+ }}
748
+ />
749
+ </div>
750
+
751
+ {/* HEADLINE BAND */}
752
+ <div style={{ position: "absolute", top: 800, left: 60, right: 60 }}>
753
+ <StaggeredWords
754
+ text="Claude now"
755
+ startFrame={12}
756
+ fontSize={104}
757
+ fontWeight={800}
758
+ letterSpacing="-0.045em"
759
+ align="center"
760
+ />
761
+ <div style={{ marginTop: 2 }}>
762
+ <StaggeredWords
763
+ text="watches video."
764
+ startFrame={32}
765
+ fontSize={104}
766
+ fontWeight={800}
767
+ letterSpacing="-0.045em"
768
+ align="center"
769
+ />
770
+ </div>
771
+ <div style={{ marginTop: 12 }}>
772
+ <StaggeredWords
773
+ text="Frame by frame."
774
+ startFrame={64}
775
+ fontSize={72}
776
+ fontWeight={700}
777
+ highlight="frame"
778
+ highlightColor={C.film}
779
+ letterSpacing="-0.035em"
780
+ align="center"
781
+ />
782
+ </div>
783
+ </div>
784
+
785
+ {/* SUB-LINE BAND — strikethrough transcript */}
786
+ <div
787
+ style={{
788
+ position: "absolute",
789
+ top: 1180,
790
+ left: 60,
791
+ right: 60,
792
+ opacity: interpolate(frame, [130, 160], [0, 1], {
793
+ extrapolateLeft: "clamp",
794
+ extrapolateRight: "clamp",
795
+ }),
796
+ }}
797
+ >
798
+ <div
799
+ style={{
800
+ fontFamily: FONT,
801
+ fontSize: 38,
802
+ fontWeight: 600,
803
+ color: C.inkSoft,
804
+ letterSpacing: "-0.02em",
805
+ display: "flex",
806
+ alignItems: "baseline",
807
+ flexWrap: "wrap",
808
+ justifyContent: "center",
809
+ gap: 10,
810
+ }}
811
+ >
812
+ Not just the
813
+ <div style={{ position: "relative", display: "inline-block" }}>
814
+ <span style={{ color: C.crimson, fontWeight: 700 }}>transcript</span>
815
+ <div
816
+ style={{
817
+ position: "absolute",
818
+ top: "55%",
819
+ left: -2,
820
+ height: 4,
821
+ background: C.crimson,
822
+ width: `calc(${strikeWidth}% + 4px)`,
823
+ transform: "translateY(-50%) rotate(-2deg)",
824
+ boxShadow: `0 0 12px ${C.crimson}`,
825
+ borderRadius: 2,
826
+ }}
827
+ />
828
+ </div>
829
+ <span>—</span>
830
+ </div>
831
+ <div
832
+ style={{
833
+ marginTop: 6,
834
+ fontFamily: FONT,
835
+ fontSize: 56,
836
+ fontWeight: 800,
837
+ color: C.ink,
838
+ letterSpacing: "-0.035em",
839
+ textAlign: "center",
840
+ }}
841
+ >
842
+ the <span style={{ color: C.film }}>actual visuals.</span>
843
+ </div>
844
+ </div>
845
+
846
+ {/* TRANSCRIPT + VISUALS cards collide → BOTH (bottom band) */}
847
+ <div
848
+ style={{
849
+ position: "absolute",
850
+ top: 1380,
851
+ left: 0,
852
+ right: 0,
853
+ height: 110,
854
+ display: "flex",
855
+ justifyContent: "center",
856
+ alignItems: "center",
857
+ }}
858
+ >
859
+ {bothOpacity < 0.5 ? (
860
+ <div style={{ display: "flex", alignItems: "center", gap: 28 }}>
861
+ <div
862
+ style={{
863
+ ...glassBase,
864
+ borderRadius: 18,
865
+ padding: "16px 32px",
866
+ fontFamily: MONO,
867
+ fontSize: 26,
868
+ fontWeight: 700,
869
+ color: C.crimson,
870
+ transform: `translateX(${transcriptX}px)`,
871
+ opacity: cardOpacity,
872
+ letterSpacing: "0.18em",
873
+ border: `1.5px solid ${C.crimson}66`,
874
+ boxShadow: `${GLASS_SHADOW}, 0 0 18px ${C.crimson}44`,
875
+ }}
876
+ >
877
+ TRANSCRIPT
878
+ </div>
879
+ <div
880
+ style={{
881
+ fontFamily: MONO,
882
+ fontSize: 22,
883
+ color: C.inkMuted,
884
+ opacity: cardOpacity,
885
+ }}
886
+ >
887
+ +
888
+ </div>
889
+ <div
890
+ style={{
891
+ ...glassBase,
892
+ borderRadius: 18,
893
+ padding: "16px 32px",
894
+ fontFamily: MONO,
895
+ fontSize: 26,
896
+ fontWeight: 700,
897
+ color: C.film,
898
+ transform: `translateX(${visualsX}px)`,
899
+ opacity: cardOpacity,
900
+ letterSpacing: "0.18em",
901
+ border: `1.5px solid ${C.film}88`,
902
+ boxShadow: `${GLASS_SHADOW}, 0 0 18px ${C.film}66`,
903
+ }}
904
+ >
905
+ VISUALS
906
+ </div>
907
+ </div>
908
+ ) : (
909
+ <div
910
+ style={{
911
+ ...glassBase,
912
+ borderRadius: 22,
913
+ padding: "20px 56px",
914
+ fontFamily: FONT,
915
+ fontSize: 60,
916
+ fontWeight: 800,
917
+ color: C.ink,
918
+ transform: `scale(${bothScale})`,
919
+ opacity: bothOpacity,
920
+ boxShadow: `${GLASS_SHADOW}, 0 0 48px ${C.claude}66`,
921
+ letterSpacing: "-0.04em",
922
+ border: `1.5px solid ${C.claude}55`,
923
+ }}
924
+ >
925
+ Now Claude sees <span style={{ color: C.claude }}>both.</span>
926
+ </div>
927
+ )}
928
+ </div>
929
+ </AbsoluteFill>
930
+ );
931
+ };
932
+
933
+ // ═══════════════════════════════════════════════════════════════
934
+ // SCENE 2 — VIRAL (frames 403–490, 2.93s)
935
+ // "Here's how you can use it. Say a YouTube video goes viral."
936
+ // ═══════════════════════════════════════════════════════════════
937
+ const ViralScene: React.FC = () => {
938
+ const frame = useCurrentFrame();
939
+ const ytScale = spring({
940
+ frame: frame - 6,
941
+ fps: 30,
942
+ config: { damping: 9, stiffness: 130 },
943
+ from: 0,
944
+ to: 1,
945
+ });
946
+ const counterStart = 12;
947
+ return (
948
+ <AbsoluteFill>
949
+ <SonarRings accent={C.crimson} secondary={C.film} />
950
+ {frame < 30 && <ParticleBurst count={36} palette={[C.crimson, C.film, C.claude]} />}
951
+
952
+ <div style={{ position: "absolute", top: SAFE_TOP, right: 60 }}>
953
+ <EyebrowPill dot={C.crimson}>02 / VIRAL</EyebrowPill>
954
+ </div>
955
+
956
+ {/* YouTube logo */}
957
+ <div
958
+ style={{
959
+ position: "absolute",
960
+ top: 720,
961
+ left: "50%",
962
+ transform: `translateX(-50%) scale(${ytScale})`,
963
+ width: 320,
964
+ height: 320,
965
+ display: "flex",
966
+ alignItems: "center",
967
+ justifyContent: "center",
968
+ filter: `drop-shadow(0 0 32px ${C.crimson}66)`,
969
+ }}
970
+ >
971
+ <Img src={staticFile("captures/your-asset.png" /* REFERENCE-STRIP */)} style={{ width: 280, height: 280 }} />
972
+ </div>
973
+
974
+ {/* View counter */}
975
+ <div
976
+ style={{
977
+ position: "absolute",
978
+ top: 1080,
979
+ left: 0,
980
+ right: 0,
981
+ textAlign: "center",
982
+ fontFamily: MONO,
983
+ fontSize: 96,
984
+ fontWeight: 700,
985
+ color: C.ink,
986
+ letterSpacing: "-0.04em",
987
+ opacity: interpolate(frame, [counterStart, counterStart + 8], [0, 1], {
988
+ extrapolateLeft: "clamp",
989
+ extrapolateRight: "clamp",
990
+ }),
991
+ }}
992
+ >
993
+ {Math.round(
994
+ interpolate(frame, [counterStart, counterStart + 50], [0, 1247892], {
995
+ extrapolateLeft: "clamp",
996
+ extrapolateRight: "clamp",
997
+ easing: ease.expoOut,
998
+ }),
999
+ )
1000
+ .toString()
1001
+ .replace(/\B(?=(\d{3})+(?!\d))/g, ",")}
1002
+ </div>
1003
+ <div
1004
+ style={{
1005
+ position: "absolute",
1006
+ top: 1208,
1007
+ left: 0,
1008
+ right: 0,
1009
+ textAlign: "center",
1010
+ fontFamily: MONO,
1011
+ fontSize: 28,
1012
+ color: C.inkMuted,
1013
+ letterSpacing: "0.18em",
1014
+ textTransform: "uppercase",
1015
+ opacity: interpolate(frame, [40, 56], [0, 1], {
1016
+ extrapolateLeft: "clamp",
1017
+ extrapolateRight: "clamp",
1018
+ }),
1019
+ }}
1020
+ >
1021
+ views • trending #1
1022
+ </div>
1023
+
1024
+ {/* VIRAL stamps */}
1025
+ {[-1, 0, 1].map((idx) => {
1026
+ const stampLocal = frame - 24 - idx * 6;
1027
+ const op = interpolate(stampLocal, [0, 12, 50, 70], [0, 1, 1, 0], {
1028
+ extrapolateLeft: "clamp",
1029
+ extrapolateRight: "clamp",
1030
+ });
1031
+ const x = idx * 280;
1032
+ const y = -80 - Math.abs(idx) * 20;
1033
+ const rot = idx * 8;
1034
+ return (
1035
+ <div
1036
+ key={idx}
1037
+ style={{
1038
+ position: "absolute",
1039
+ top: 1380,
1040
+ left: "50%",
1041
+ transform: `translate(calc(-50% + ${x}px), ${y}px) rotate(${rot}deg) scale(${op})`,
1042
+ ...glassBase,
1043
+ borderRadius: 12,
1044
+ padding: "8px 22px",
1045
+ fontFamily: MONO,
1046
+ fontSize: 22,
1047
+ fontWeight: 700,
1048
+ color: C.crimson,
1049
+ letterSpacing: "0.18em",
1050
+ border: `2px solid ${C.crimson}`,
1051
+ boxShadow: `0 0 24px ${C.crimson}55`,
1052
+ opacity: op,
1053
+ }}
1054
+ >
1055
+ VIRAL
1056
+ </div>
1057
+ );
1058
+ })}
1059
+ </AbsoluteFill>
1060
+ );
1061
+ };
1062
+
1063
+ // ═══════════════════════════════════════════════════════════════
1064
+ // SCENE 3 — DEMO (frames 491–717, 7.57s) — embeds clip 1
1065
+ // "You just type slash claude watch, paste the URL, hit enter.
1066
+ // Claude studies the whole thing frame by frame and writes you a full video break down."
1067
+ // ═══════════════════════════════════════════════════════════════
1068
+ const DemoScene: React.FC = () => {
1069
+ const frame = useCurrentFrame();
1070
+ const cmd = "/claude-watch ";
1071
+ const url = "https://youtu.be/bV-N_d7vZJM";
1072
+ const cmdLen = Math.min(cmd.length, Math.floor(frame / 2));
1073
+ const showUrl = frame >= 30;
1074
+ const enterPulse = interpolate(frame, [54, 62, 76], [0, 1, 0], {
1075
+ extrapolateLeft: "clamp",
1076
+ extrapolateRight: "clamp",
1077
+ });
1078
+
1079
+ // Cursor blink
1080
+ const cursor = Math.floor(frame / 12) % 2 === 0;
1081
+
1082
+ // Cut to motion-art screen at local frame 100 (≈19.7s global, just after ENTER pulse).
1083
+ // VO "Plot studies the whole thing frame by frame..." starts at local frame 112 (20.12s).
1084
+ const ART_IN = 100;
1085
+ const ART_FULL = 125;
1086
+ const STUDIES_VO = 112;
1087
+ const terminalFade = interpolate(frame, [ART_IN - 6, ART_FULL - 6], [1, 0], {
1088
+ extrapolateLeft: "clamp",
1089
+ extrapolateRight: "clamp",
1090
+ });
1091
+ const artFade = interpolate(frame, [ART_IN, ART_FULL], [0, 1], {
1092
+ extrapolateLeft: "clamp",
1093
+ extrapolateRight: "clamp",
1094
+ easing: ease.expoOut,
1095
+ });
1096
+ const breakdownLines = [
1097
+ { txt: "→ Hook 0:00–0:14", color: C.claude },
1098
+ { txt: "→ Cuts 6 scene changes", color: C.film },
1099
+ { txt: "→ Beat 1:24 reveal", color: C.vector },
1100
+ { txt: "→ Takeaway captured", color: C.success },
1101
+ ];
1102
+ const lineOp = (i: number) =>
1103
+ interpolate(frame, [STUDIES_VO + i * 18, STUDIES_VO + i * 18 + 14], [0, 1], {
1104
+ extrapolateLeft: "clamp",
1105
+ extrapolateRight: "clamp",
1106
+ easing: ease.expoOut,
1107
+ });
1108
+ const lineY = (i: number) =>
1109
+ interpolate(frame, [STUDIES_VO + i * 18, STUDIES_VO + i * 18 + 14], [22, 0], {
1110
+ extrapolateLeft: "clamp",
1111
+ extrapolateRight: "clamp",
1112
+ easing: ease.power3Out,
1113
+ });
1114
+ // Scan beam sweeps across filmstrip
1115
+ const scanPct = ((Math.sin((frame - ART_IN) * 0.075) + 1) / 2) * 100;
1116
+ // Flow pulse between strip and notes card (3 dots cycling)
1117
+ const flowDot = (i: number) => {
1118
+ const t = ((frame - ART_IN) * 0.12 + i * 0.33) % 1;
1119
+ return { y: t * 60, op: Math.sin(t * Math.PI) };
1120
+ };
1121
+
1122
+ return (
1123
+ <AbsoluteFill>
1124
+ <SonarRings accent={C.claude} secondary={C.film} />
1125
+
1126
+ <div style={{ position: "absolute", top: SAFE_TOP, left: 60 }}>
1127
+ <EyebrowPill dot={C.claude}>03 / RUN IT</EyebrowPill>
1128
+ </div>
1129
+
1130
+ {/* Headline */}
1131
+ <div style={{ position: "absolute", top: SAFE_TOP + 60, left: 60, right: 60 }}>
1132
+ <StaggeredWords
1133
+ text="Type. Paste. Watch."
1134
+ startFrame={4}
1135
+ fontSize={88}
1136
+ fontWeight={700}
1137
+ align="left"
1138
+ highlight="watch"
1139
+ highlightColor={C.claude}
1140
+ />
1141
+ </div>
1142
+
1143
+ {/* Terminal mock card */}
1144
+ <div
1145
+ style={{
1146
+ position: "absolute",
1147
+ top: 540,
1148
+ left: 60,
1149
+ right: 60,
1150
+ ...glassBase,
1151
+ background: C.glassFillStrong,
1152
+ borderRadius: 24,
1153
+ padding: "28px 32px",
1154
+ fontFamily: MONO,
1155
+ fontSize: 38,
1156
+ color: C.ink,
1157
+ opacity: terminalFade,
1158
+ pointerEvents: terminalFade < 0.05 ? "none" : "auto",
1159
+ }}
1160
+ >
1161
+ <div style={{ display: "flex", alignItems: "center", gap: 14, marginBottom: 16 }}>
1162
+ <div style={{ width: 11, height: 11, borderRadius: "50%", background: "#FF5F57" }} />
1163
+ <div style={{ width: 11, height: 11, borderRadius: "50%", background: "#FEBC2E" }} />
1164
+ <div style={{ width: 11, height: 11, borderRadius: "50%", background: "#28C840" }} />
1165
+ <div
1166
+ style={{
1167
+ marginLeft: 16,
1168
+ fontSize: 18,
1169
+ color: C.inkMuted,
1170
+ letterSpacing: "0.05em",
1171
+ }}
1172
+ >
1173
+ ~/claude-projects
1174
+ </div>
1175
+ </div>
1176
+ <div style={{ display: "flex", alignItems: "baseline", flexWrap: "wrap", gap: 4 }}>
1177
+ <span style={{ color: C.success, marginRight: 12 }}>$</span>
1178
+ <span style={{ color: C.claude, fontWeight: 600 }}>{cmd.slice(0, cmdLen)}</span>
1179
+ {showUrl && (
1180
+ <span style={{ color: C.inkMuted }}>
1181
+ {url}
1182
+ </span>
1183
+ )}
1184
+ {cursor && frame < 80 && (
1185
+ <span
1186
+ style={{
1187
+ display: "inline-block",
1188
+ width: 16,
1189
+ height: 38,
1190
+ background: C.ink,
1191
+ marginLeft: 4,
1192
+ verticalAlign: "middle",
1193
+ }}
1194
+ />
1195
+ )}
1196
+ </div>
1197
+
1198
+ {/* ENTER pill */}
1199
+ <div
1200
+ style={{
1201
+ marginTop: 22,
1202
+ display: "inline-flex",
1203
+ alignItems: "center",
1204
+ gap: 10,
1205
+ ...glassBase,
1206
+ borderRadius: 12,
1207
+ padding: "8px 18px",
1208
+ fontFamily: MONO,
1209
+ fontSize: 22,
1210
+ color: C.inkSoft,
1211
+ letterSpacing: "0.18em",
1212
+ transform: `scale(${1 + enterPulse * 0.15})`,
1213
+ boxShadow: `${GLASS_SHADOW}, 0 0 ${enterPulse * 32}px ${C.claude}99`,
1214
+ }}
1215
+ >
1216
+ <span>↵</span>
1217
+ <span>ENTER</span>
1218
+ </div>
1219
+ </div>
1220
+
1221
+ {/* MOTION-ART SCREEN — "Plot studies frame by frame and writes a breakdown" (local 100→end) */}
1222
+ {frame >= ART_IN - 6 && (
1223
+ <div
1224
+ style={{
1225
+ position: "absolute",
1226
+ top: 540,
1227
+ left: 0,
1228
+ right: 0,
1229
+ opacity: artFade,
1230
+ }}
1231
+ >
1232
+ {/* Section caption with pulsing dot */}
1233
+ <div
1234
+ style={{
1235
+ display: "flex",
1236
+ alignItems: "center",
1237
+ justifyContent: "center",
1238
+ gap: 12,
1239
+ fontFamily: MONO,
1240
+ fontSize: 22,
1241
+ fontWeight: 700,
1242
+ color: C.claude,
1243
+ letterSpacing: "0.22em",
1244
+ marginBottom: 22,
1245
+ }}
1246
+ >
1247
+ <div
1248
+ style={{
1249
+ width: 10,
1250
+ height: 10,
1251
+ borderRadius: "50%",
1252
+ background: C.claude,
1253
+ boxShadow: `0 0 14px ${C.claude}`,
1254
+ opacity: 0.4 + 0.6 * ((Math.sin(frame * 0.18) + 1) / 2),
1255
+ }}
1256
+ />
1257
+ FRAME-BY-FRAME ANALYSIS
1258
+ </div>
1259
+
1260
+ {/* Filmstrip + scan beam */}
1261
+ <div style={{ position: "relative", margin: "0 60px" }}>
1262
+ <FilmStrip speed={6} height={210} width={960} thumbCount={9} />
1263
+ {/* Vertical scan beam */}
1264
+ <div
1265
+ style={{
1266
+ position: "absolute",
1267
+ top: -8,
1268
+ bottom: -8,
1269
+ left: `${scanPct}%`,
1270
+ width: 4,
1271
+ background: `linear-gradient(180deg, transparent, ${C.claude}, transparent)`,
1272
+ boxShadow: `0 0 24px ${C.claude}, 0 0 48px ${C.claude}88`,
1273
+ transform: "translateX(-50%)",
1274
+ pointerEvents: "none",
1275
+ }}
1276
+ />
1277
+ {/* Scan beam glow halo */}
1278
+ <div
1279
+ style={{
1280
+ position: "absolute",
1281
+ top: 0,
1282
+ bottom: 0,
1283
+ left: `${scanPct}%`,
1284
+ width: 120,
1285
+ background: `radial-gradient(ellipse at center, ${C.claude}33, transparent 60%)`,
1286
+ transform: "translateX(-50%)",
1287
+ pointerEvents: "none",
1288
+ }}
1289
+ />
1290
+ </div>
1291
+
1292
+ {/* Flow connector — 3 cycling dots between strip and notes */}
1293
+ <div
1294
+ style={{
1295
+ position: "relative",
1296
+ height: 80,
1297
+ display: "flex",
1298
+ justifyContent: "center",
1299
+ alignItems: "flex-start",
1300
+ gap: 36,
1301
+ marginTop: 8,
1302
+ }}
1303
+ >
1304
+ {[0, 1, 2].map((i) => {
1305
+ const f = flowDot(i);
1306
+ return (
1307
+ <div
1308
+ key={i}
1309
+ style={{
1310
+ width: 12,
1311
+ height: 12,
1312
+ borderRadius: "50%",
1313
+ background: C.claude,
1314
+ transform: `translateY(${f.y}px)`,
1315
+ opacity: f.op * 0.95,
1316
+ boxShadow: `0 0 18px ${C.claude}`,
1317
+ }}
1318
+ />
1319
+ );
1320
+ })}
1321
+ </div>
1322
+
1323
+ {/* Breakdown notes card */}
1324
+ <div
1325
+ style={{
1326
+ margin: "0 60px",
1327
+ ...glassBase,
1328
+ background: C.glassFillStrong,
1329
+ borderRadius: 22,
1330
+ padding: "26px 30px",
1331
+ fontFamily: MONO,
1332
+ }}
1333
+ >
1334
+ <div
1335
+ style={{
1336
+ display: "flex",
1337
+ alignItems: "center",
1338
+ gap: 12,
1339
+ marginBottom: 18,
1340
+ fontSize: 18,
1341
+ color: C.inkMuted,
1342
+ letterSpacing: "0.14em",
1343
+ fontWeight: 700,
1344
+ }}
1345
+ >
1346
+ <div style={{ width: 10, height: 10, borderRadius: "50%", background: C.success }} />
1347
+ BREAKDOWN.MD
1348
+ <span
1349
+ style={{
1350
+ marginLeft: "auto",
1351
+ display: "inline-block",
1352
+ width: 12,
1353
+ height: 22,
1354
+ background: cursor ? C.ink : "transparent",
1355
+ }}
1356
+ />
1357
+ </div>
1358
+ {breakdownLines.map((line, i) => (
1359
+ <div
1360
+ key={i}
1361
+ style={{
1362
+ fontSize: 30,
1363
+ fontWeight: 600,
1364
+ color: line.color,
1365
+ letterSpacing: "0.02em",
1366
+ lineHeight: 1.55,
1367
+ opacity: lineOp(i),
1368
+ transform: `translateY(${lineY(i)}px)`,
1369
+ willChange: "transform, opacity",
1370
+ }}
1371
+ >
1372
+ {line.txt}
1373
+ </div>
1374
+ ))}
1375
+ </div>
1376
+ </div>
1377
+ )}
1378
+
1379
+ </AbsoluteFill>
1380
+ );
1381
+ };
1382
+
1383
+ // ═══════════════════════════════════════════════════════════════
1384
+ // SCENE 4 — RESULT (frames 718–873, 5.20s) — embeds clip 2
1385
+ // "A 30-minute video, broken down and rewritten, in under two minutes."
1386
+ // ═══════════════════════════════════════════════════════════════
1387
+ const ResultScene: React.FC = () => {
1388
+ const frame = useCurrentFrame();
1389
+ const num30Scale = spring({
1390
+ frame: frame - 4,
1391
+ fps: 30,
1392
+ config: { damping: 12, stiffness: 130 },
1393
+ from: 0.4,
1394
+ to: 1,
1395
+ });
1396
+ const num2Scale = spring({
1397
+ frame: frame - 38,
1398
+ fps: 30,
1399
+ config: { damping: 11, stiffness: 130 },
1400
+ from: 0.3,
1401
+ to: 1,
1402
+ });
1403
+ const arrowProgress = interpolate(frame, [44, 72], [0, 1], {
1404
+ extrapolateLeft: "clamp",
1405
+ extrapolateRight: "clamp",
1406
+ easing: ease.power3Out,
1407
+ });
1408
+
1409
+ // Timer ring sweep — 360deg → ~24deg (2/30 ratio)
1410
+ const ringSweep = interpolate(frame, [10, 90], [360, 24], {
1411
+ extrapolateLeft: "clamp",
1412
+ extrapolateRight: "clamp",
1413
+ easing: ease.expoOut,
1414
+ });
1415
+
1416
+ return (
1417
+ <AbsoluteFill>
1418
+ <SonarRings accent={C.success} secondary={C.claude} />
1419
+
1420
+ <div style={{ position: "absolute", top: SAFE_TOP, left: 60 }}>
1421
+ <EyebrowPill dot={C.success}>04 / RESULT</EyebrowPill>
1422
+ </div>
1423
+
1424
+ {/* Hero numerals — compact, fit-to-1080 */}
1425
+ <div
1426
+ style={{
1427
+ position: "absolute",
1428
+ top: 440,
1429
+ left: 0,
1430
+ right: 0,
1431
+ display: "flex",
1432
+ justifyContent: "center",
1433
+ alignItems: "center",
1434
+ gap: 24,
1435
+ fontFamily: MONO,
1436
+ fontWeight: 800,
1437
+ letterSpacing: "-0.06em",
1438
+ color: C.ink,
1439
+ }}
1440
+ >
1441
+ <div
1442
+ style={{
1443
+ display: "flex",
1444
+ alignItems: "baseline",
1445
+ gap: 6,
1446
+ transform: `scale(${num30Scale})`,
1447
+ transformOrigin: "right",
1448
+ }}
1449
+ >
1450
+ <div
1451
+ style={{
1452
+ fontSize: 140,
1453
+ color: C.crimson,
1454
+ lineHeight: 1,
1455
+ textDecoration: "line-through",
1456
+ textDecorationThickness: 6,
1457
+ textDecorationColor: `${C.crimson}cc`,
1458
+ }}
1459
+ >
1460
+ 30
1461
+ </div>
1462
+ <div style={{ fontSize: 52, color: C.inkMuted, lineHeight: 1 }}>min</div>
1463
+ </div>
1464
+
1465
+ {/* Arrow */}
1466
+ <div
1467
+ style={{
1468
+ position: "relative",
1469
+ width: 140,
1470
+ height: 4,
1471
+ background: `linear-gradient(90deg, ${C.crimson}, ${C.claude}, ${C.success})`,
1472
+ transform: `scaleX(${arrowProgress})`,
1473
+ transformOrigin: "left",
1474
+ opacity: arrowProgress,
1475
+ }}
1476
+ >
1477
+ <div
1478
+ style={{
1479
+ position: "absolute",
1480
+ right: -2,
1481
+ top: -10,
1482
+ width: 0,
1483
+ height: 0,
1484
+ borderLeft: `22px solid ${C.success}`,
1485
+ borderTop: "12px solid transparent",
1486
+ borderBottom: "12px solid transparent",
1487
+ }}
1488
+ />
1489
+ </div>
1490
+
1491
+ <div
1492
+ style={{
1493
+ display: "flex",
1494
+ alignItems: "baseline",
1495
+ gap: 6,
1496
+ transform: `scale(${num2Scale})`,
1497
+ transformOrigin: "left",
1498
+ filter: `drop-shadow(0 0 16px ${C.success}55)`,
1499
+ }}
1500
+ >
1501
+ <div style={{ fontSize: 180, color: C.success, lineHeight: 1 }}>2</div>
1502
+ <div style={{ fontSize: 64, color: C.inkMuted, lineHeight: 1 }}>min</div>
1503
+ </div>
1504
+ </div>
1505
+
1506
+ {/* Timer ring */}
1507
+ <svg
1508
+ width={100}
1509
+ height={100}
1510
+ viewBox="0 0 100 100"
1511
+ style={{
1512
+ position: "absolute",
1513
+ top: 460,
1514
+ right: 60,
1515
+ opacity: 0.85,
1516
+ }}
1517
+ >
1518
+ <circle cx={50} cy={50} r={42} fill="none" stroke={C.hairline} strokeWidth={5} />
1519
+ <circle
1520
+ cx={50}
1521
+ cy={50}
1522
+ r={42}
1523
+ fill="none"
1524
+ stroke={C.success}
1525
+ strokeWidth={5}
1526
+ strokeDasharray={`${(ringSweep / 360) * 264} 264`}
1527
+ strokeLinecap="round"
1528
+ transform="rotate(-90 50 50)"
1529
+ style={{ filter: `drop-shadow(0 0 8px ${C.success})` }}
1530
+ />
1531
+ </svg>
1532
+
1533
+ {/* Subtitle — above clip */}
1534
+ <div
1535
+ style={{
1536
+ position: "absolute",
1537
+ top: 720,
1538
+ left: 60,
1539
+ right: 60,
1540
+ textAlign: "center",
1541
+ fontFamily: FONT,
1542
+ fontSize: 40,
1543
+ fontWeight: 700,
1544
+ color: C.inkSoft,
1545
+ letterSpacing: "-0.02em",
1546
+ opacity: interpolate(frame, [50, 80], [0, 1], {
1547
+ extrapolateLeft: "clamp",
1548
+ extrapolateRight: "clamp",
1549
+ }),
1550
+ }}
1551
+ >
1552
+ Broken down. Rewritten. <span style={{ color: C.claude }}>Done.</span>
1553
+ </div>
1554
+
1555
+ {/* Embedded clip 2 — entire scene */}
1556
+ <Sequence from={0} durationInFrames={156}>
1557
+ <div
1558
+ style={{
1559
+ position: "absolute",
1560
+ top: 860,
1561
+ left: 60,
1562
+ width: 960,
1563
+ height: 540,
1564
+ }}
1565
+ >
1566
+ <LaptopFrame width={960} height={540} title="claude-watch-result.mov">
1567
+ {/* REFERENCE-STRIP: <OffthreadVideo> removed — bring your own clip */}
1568
+ </LaptopFrame>
1569
+ </div>
1570
+ </Sequence>
1571
+ </AbsoluteFill>
1572
+ );
1573
+ };
1574
+
1575
+ // ═══════════════════════════════════════════════════════════════
1576
+ // SCENE 5 — DECODE (frames 874–1228, 11.83s)
1577
+ // "You can also do this with every top creator in your niche.
1578
+ // Paste the URL, Claude breaks down what's working — the hooks, the visuals, the trends —
1579
+ // and pipes it straight into your Obsidian vector library."
1580
+ // ═══════════════════════════════════════════════════════════════
1581
+ const DecodeScene: React.FC = () => {
1582
+ const frame = useCurrentFrame();
1583
+
1584
+ const creators = [
1585
+ { handle: "@mrbeast", color: C.crimson },
1586
+ { handle: "@garrytan", color: C.claude },
1587
+ { handle: "@aliabdaal", color: C.vector },
1588
+ ];
1589
+ const chips = ["HOOK", "VISUALS", "TRENDS"];
1590
+
1591
+ return (
1592
+ <AbsoluteFill>
1593
+ <SonarRings accent={C.vector} secondary={C.claude} />
1594
+
1595
+ <div style={{ position: "absolute", top: SAFE_TOP, left: 60 }}>
1596
+ <EyebrowPill dot={C.vector}>05 / DECODE</EyebrowPill>
1597
+ </div>
1598
+
1599
+ {/* Headline */}
1600
+ <div style={{ position: "absolute", top: SAFE_TOP + 60, left: 60, right: 60 }}>
1601
+ <StaggeredWords
1602
+ text="Every top creator. Decoded."
1603
+ startFrame={4}
1604
+ fontSize={84}
1605
+ fontWeight={700}
1606
+ highlight="decoded"
1607
+ highlightColor={C.vector}
1608
+ />
1609
+ </div>
1610
+
1611
+ {/* 3 creator cards */}
1612
+ {creators.map((c, i) => {
1613
+ const cardLocal = frame - (10 + i * 12);
1614
+ const cardScale = spring({
1615
+ frame: cardLocal,
1616
+ fps: 30,
1617
+ config: { damping: 12, stiffness: 120 },
1618
+ from: 0.5,
1619
+ to: 1,
1620
+ });
1621
+ const op = interpolate(cardLocal, [0, 24], [0, 1], {
1622
+ extrapolateLeft: "clamp",
1623
+ extrapolateRight: "clamp",
1624
+ easing: ease.expoOut,
1625
+ });
1626
+ const x = 60 + i * 320;
1627
+ return (
1628
+ <div
1629
+ key={c.handle}
1630
+ style={{
1631
+ position: "absolute",
1632
+ top: 600,
1633
+ left: x,
1634
+ width: 300,
1635
+ height: 300,
1636
+ ...glassBase,
1637
+ borderRadius: 24,
1638
+ padding: 18,
1639
+ transform: `scale(${cardScale})`,
1640
+ opacity: op,
1641
+ display: "flex",
1642
+ flexDirection: "column",
1643
+ gap: 10,
1644
+ }}
1645
+ >
1646
+ <div
1647
+ style={{
1648
+ width: "100%",
1649
+ height: 200,
1650
+ borderRadius: 16,
1651
+ background: `linear-gradient(135deg, ${c.color}, ${C.ink})`,
1652
+ position: "relative",
1653
+ overflow: "hidden",
1654
+ }}
1655
+ >
1656
+ <div
1657
+ style={{
1658
+ position: "absolute",
1659
+ top: "50%",
1660
+ left: "50%",
1661
+ transform: "translate(-50%, -50%)",
1662
+ width: 0,
1663
+ height: 0,
1664
+ borderLeft: "32px solid white",
1665
+ borderTop: "20px solid transparent",
1666
+ borderBottom: "20px solid transparent",
1667
+ }}
1668
+ />
1669
+ </div>
1670
+ <div
1671
+ style={{
1672
+ fontFamily: MONO,
1673
+ fontSize: 22,
1674
+ fontWeight: 600,
1675
+ color: C.ink,
1676
+ letterSpacing: "0.02em",
1677
+ }}
1678
+ >
1679
+ {c.handle}
1680
+ </div>
1681
+ </div>
1682
+ );
1683
+ })}
1684
+
1685
+ {/* Chips above each card */}
1686
+ {chips.map((label, i) => {
1687
+ const chipLocal = frame - (80 + i * 8);
1688
+ const op = interpolate(chipLocal, [0, 16], [0, 1], {
1689
+ extrapolateLeft: "clamp",
1690
+ extrapolateRight: "clamp",
1691
+ easing: ease.expoOut,
1692
+ });
1693
+ const yDrift = interpolate(chipLocal, [0, 24], [12, 0], {
1694
+ extrapolateLeft: "clamp",
1695
+ extrapolateRight: "clamp",
1696
+ easing: ease.power3Out,
1697
+ });
1698
+ const x = 110 + i * 320;
1699
+ return (
1700
+ <div
1701
+ key={label}
1702
+ style={{
1703
+ position: "absolute",
1704
+ top: 540,
1705
+ left: x,
1706
+ transform: `translateY(${yDrift}px)`,
1707
+ opacity: op,
1708
+ ...glassBase,
1709
+ borderRadius: 8,
1710
+ padding: "6px 16px",
1711
+ fontFamily: MONO,
1712
+ fontSize: 18,
1713
+ fontWeight: 700,
1714
+ letterSpacing: "0.18em",
1715
+ color: [C.claude, C.film, C.vector][i],
1716
+ border: `1.5px solid ${[C.claude, C.film, C.vector][i]}`,
1717
+ boxShadow: `0 0 16px ${[C.claude, C.film, C.vector][i]}55`,
1718
+ }}
1719
+ >
1720
+ {label}
1721
+ </div>
1722
+ );
1723
+ })}
1724
+
1725
+ {/* Bezier path-draw lines from cards → Obsidian */}
1726
+ <svg
1727
+ width={1080}
1728
+ height={500}
1729
+ viewBox="0 0 1080 500"
1730
+ style={{ position: "absolute", top: 920, left: 0, pointerEvents: "none" }}
1731
+ >
1732
+ {creators.map((_, i) => {
1733
+ const lineLocal = frame - (140 + i * 6);
1734
+ const drawProgress = interpolate(lineLocal, [0, 30], [0, 1], {
1735
+ extrapolateLeft: "clamp",
1736
+ extrapolateRight: "clamp",
1737
+ easing: ease.power3Out,
1738
+ });
1739
+ const startX = 210 + i * 320;
1740
+ const endX = 540;
1741
+ return (
1742
+ <path
1743
+ key={i}
1744
+ d={`M ${startX},10 C ${startX},200 ${endX},200 ${endX},400`}
1745
+ fill="none"
1746
+ stroke={[C.claude, C.film, C.vector][i]}
1747
+ strokeWidth={3}
1748
+ strokeDasharray="600"
1749
+ strokeDashoffset={(1 - drawProgress) * 600}
1750
+ style={{
1751
+ filter: `drop-shadow(0 0 8px ${[C.claude, C.film, C.vector][i]})`,
1752
+ opacity: drawProgress,
1753
+ }}
1754
+ />
1755
+ );
1756
+ })}
1757
+ </svg>
1758
+
1759
+ {/* Obsidian logo + node graph */}
1760
+ <div
1761
+ style={{
1762
+ position: "absolute",
1763
+ top: 1280,
1764
+ left: "50%",
1765
+ transform: "translateX(-50%)",
1766
+ display: "flex",
1767
+ flexDirection: "column",
1768
+ alignItems: "center",
1769
+ gap: 14,
1770
+ opacity: interpolate(frame, [180, 210], [0, 1], {
1771
+ extrapolateLeft: "clamp",
1772
+ extrapolateRight: "clamp",
1773
+ }),
1774
+ }}
1775
+ >
1776
+ <div
1777
+ style={{
1778
+ width: 140,
1779
+ height: 140,
1780
+ ...glassBase,
1781
+ borderRadius: 28,
1782
+ display: "flex",
1783
+ alignItems: "center",
1784
+ justifyContent: "center",
1785
+ boxShadow: `${GLASS_SHADOW}, 0 0 32px ${C.vector}66`,
1786
+ }}
1787
+ >
1788
+ <Img
1789
+ src={staticFile("captures/your-asset.png" /* REFERENCE-STRIP */)}
1790
+ style={{
1791
+ width: 80,
1792
+ height: 80,
1793
+ filter: `drop-shadow(0 0 12px ${C.vector})`,
1794
+ }}
1795
+ />
1796
+ </div>
1797
+ <div
1798
+ style={{
1799
+ fontFamily: MONO,
1800
+ fontSize: 22,
1801
+ color: C.inkSoft,
1802
+ letterSpacing: "0.18em",
1803
+ textTransform: "uppercase",
1804
+ }}
1805
+ >
1806
+ obsidian vault
1807
+ </div>
1808
+ </div>
1809
+
1810
+ {/* Mini node graph — appears post-Obsidian */}
1811
+ <svg
1812
+ width={1080}
1813
+ height={200}
1814
+ viewBox="0 0 1080 200"
1815
+ style={{ position: "absolute", top: 1610, left: 0, pointerEvents: "none" }}
1816
+ >
1817
+ {Array.from({ length: 9 }).map((_, i) => {
1818
+ const nodeLocal = frame - (210 + i * 4);
1819
+ const op = interpolate(nodeLocal, [0, 12], [0, 1], {
1820
+ extrapolateLeft: "clamp",
1821
+ extrapolateRight: "clamp",
1822
+ });
1823
+ const cx = 60 + (i % 9) * 120 + Math.sin(i) * 20;
1824
+ const cy = 60 + (i % 3) * 50;
1825
+ return (
1826
+ <circle
1827
+ key={i}
1828
+ cx={cx}
1829
+ cy={cy}
1830
+ r={6}
1831
+ fill={C.vector}
1832
+ opacity={op * 0.85}
1833
+ style={{ filter: `drop-shadow(0 0 6px ${C.vector})` }}
1834
+ />
1835
+ );
1836
+ })}
1837
+ {Array.from({ length: 8 }).map((_, i) => {
1838
+ const lineLocal = frame - (224 + i * 3);
1839
+ const op = interpolate(lineLocal, [0, 12], [0, 0.4], {
1840
+ extrapolateLeft: "clamp",
1841
+ extrapolateRight: "clamp",
1842
+ });
1843
+ const x1 = 60 + (i % 9) * 120;
1844
+ const y1 = 60 + (i % 3) * 50;
1845
+ const x2 = 60 + ((i + 1) % 9) * 120;
1846
+ const y2 = 60 + ((i + 1) % 3) * 50;
1847
+ return <line key={i} x1={x1} y1={y1} x2={x2} y2={y2} stroke={C.vector} strokeWidth={1.5} opacity={op} />;
1848
+ })}
1849
+ </svg>
1850
+ </AbsoluteFill>
1851
+ );
1852
+ };
1853
+
1854
+ // ═══════════════════════════════════════════════════════════════
1855
+ // SCENE 6 — BRAIN (frames 1229–1428, 6.67s)
1856
+ // "Your second brain just keeps getting smarter on autopilot.
1857
+ // You are literally never out of content ideas."
1858
+ // ═══════════════════════════════════════════════════════════════
1859
+ const BrainScene: React.FC = () => {
1860
+ const frame = useCurrentFrame();
1861
+ // Brain SVG built from scattered nodes
1862
+ const nodes = [
1863
+ { x: 540, y: 760, r: 18, delay: 0, key: "n0" },
1864
+ { x: 420, y: 700, r: 14, delay: 4, key: "n1" },
1865
+ { x: 660, y: 700, r: 14, delay: 8, key: "n2" },
1866
+ { x: 360, y: 800, r: 12, delay: 12, key: "n3" },
1867
+ { x: 720, y: 800, r: 12, delay: 16, key: "n4" },
1868
+ { x: 480, y: 880, r: 12, delay: 20, key: "n5" },
1869
+ { x: 600, y: 880, r: 12, delay: 24, key: "n6" },
1870
+ { x: 420, y: 600, r: 10, delay: 28, key: "n7" },
1871
+ { x: 660, y: 600, r: 10, delay: 32, key: "n8" },
1872
+ { x: 540, y: 560, r: 10, delay: 36, key: "n9" },
1873
+ { x: 320, y: 750, r: 9, delay: 40, key: "n10" },
1874
+ { x: 760, y: 750, r: 9, delay: 44, key: "n11" },
1875
+ ];
1876
+ const edges = [
1877
+ [0, 1], [0, 2], [0, 5], [0, 6], [1, 3], [2, 4], [1, 7], [2, 8],
1878
+ [7, 9], [8, 9], [3, 10], [4, 11], [5, 6], [9, 0], [1, 5], [2, 6],
1879
+ ];
1880
+
1881
+ // Lightbulbs popping
1882
+ const bulbPositions = [
1883
+ { x: 180, y: 760, delay: 70 },
1884
+ { x: 920, y: 760, delay: 86 },
1885
+ { x: 200, y: 1080, delay: 102 },
1886
+ { x: 880, y: 1080, delay: 118 },
1887
+ { x: 540, y: 1380, delay: 134 },
1888
+ ];
1889
+
1890
+ return (
1891
+ <AbsoluteFill>
1892
+ <SonarRings accent={C.vector} secondary={C.film} />
1893
+
1894
+ <div style={{ position: "absolute", top: SAFE_TOP, left: 60 }}>
1895
+ <EyebrowPill dot={C.vector}>06 / BRAIN</EyebrowPill>
1896
+ </div>
1897
+
1898
+ <div style={{ position: "absolute", top: SAFE_TOP + 60, left: 60, right: 60 }}>
1899
+ <StaggeredWords
1900
+ text="Second brain. On autopilot."
1901
+ startFrame={4}
1902
+ fontSize={88}
1903
+ fontWeight={700}
1904
+ highlight="autopilot"
1905
+ highlightColor={C.claude}
1906
+ />
1907
+ </div>
1908
+
1909
+ {/* Brain node graph */}
1910
+ <svg width={1080} height={1920} style={{ position: "absolute", inset: 0, pointerEvents: "none" }}>
1911
+ {edges.map(([a, b], i) => {
1912
+ const eLocal = frame - (50 + i * 2);
1913
+ const op = interpolate(eLocal, [0, 18], [0, 0.5], {
1914
+ extrapolateLeft: "clamp",
1915
+ extrapolateRight: "clamp",
1916
+ });
1917
+ const draw = interpolate(eLocal, [0, 18], [0, 1], {
1918
+ extrapolateLeft: "clamp",
1919
+ extrapolateRight: "clamp",
1920
+ easing: ease.power3Out,
1921
+ });
1922
+ const na = nodes[a];
1923
+ const nb = nodes[b];
1924
+ const dx = nb.x - na.x;
1925
+ const dy = nb.y - na.y;
1926
+ return (
1927
+ <line
1928
+ key={i}
1929
+ x1={na.x}
1930
+ y1={na.y}
1931
+ x2={na.x + dx * draw}
1932
+ y2={na.y + dy * draw}
1933
+ stroke={C.vector}
1934
+ strokeWidth={2}
1935
+ opacity={op}
1936
+ />
1937
+ );
1938
+ })}
1939
+ {nodes.map((n) => {
1940
+ const nLocal = frame - n.delay;
1941
+ const sc = spring({
1942
+ frame: nLocal,
1943
+ fps: 30,
1944
+ config: { damping: 10, stiffness: 130 },
1945
+ from: 0,
1946
+ to: 1,
1947
+ });
1948
+ return (
1949
+ <circle
1950
+ key={n.key}
1951
+ cx={n.x}
1952
+ cy={n.y}
1953
+ r={n.r * sc}
1954
+ fill={C.vector}
1955
+ opacity={0.95}
1956
+ style={{ filter: `drop-shadow(0 0 8px ${C.vector})` }}
1957
+ />
1958
+ );
1959
+ })}
1960
+ </svg>
1961
+
1962
+ {/* Lightbulbs */}
1963
+ {bulbPositions.map((b, i) => {
1964
+ const bLocal = frame - b.delay;
1965
+ const sc = spring({
1966
+ frame: bLocal,
1967
+ fps: 30,
1968
+ config: { damping: 9, stiffness: 130 },
1969
+ from: 0,
1970
+ to: 1,
1971
+ });
1972
+ const op = interpolate(bLocal, [0, 12, 60, 80], [0, 1, 1, 0], {
1973
+ extrapolateLeft: "clamp",
1974
+ extrapolateRight: "clamp",
1975
+ });
1976
+ return (
1977
+ <div
1978
+ key={i}
1979
+ style={{
1980
+ position: "absolute",
1981
+ left: b.x - 35,
1982
+ top: b.y - 35,
1983
+ width: 70,
1984
+ height: 70,
1985
+ transform: `scale(${sc})`,
1986
+ opacity: op,
1987
+ filter: `drop-shadow(0 0 16px ${C.film})`,
1988
+ }}
1989
+ >
1990
+ <Img src={staticFile("captures/your-asset.png" /* REFERENCE-STRIP */)} style={{ width: 70, height: 70 }} />
1991
+ <div
1992
+ style={{
1993
+ position: "absolute",
1994
+ inset: 0,
1995
+ borderRadius: "50%",
1996
+ background: `radial-gradient(circle, ${C.film}66 0%, transparent 70%)`,
1997
+ pointerEvents: "none",
1998
+ }}
1999
+ />
2000
+ </div>
2001
+ );
2002
+ })}
2003
+
2004
+ {/* Subline */}
2005
+ <div
2006
+ style={{
2007
+ position: "absolute",
2008
+ top: 1180,
2009
+ left: 60,
2010
+ right: 60,
2011
+ textAlign: "center",
2012
+ fontFamily: FONT,
2013
+ fontSize: 42,
2014
+ fontWeight: 600,
2015
+ color: C.inkSoft,
2016
+ letterSpacing: "-0.02em",
2017
+ opacity: interpolate(frame, [120, 150], [0, 1], {
2018
+ extrapolateLeft: "clamp",
2019
+ extrapolateRight: "clamp",
2020
+ }),
2021
+ }}
2022
+ >
2023
+ Never out of <span style={{ color: C.film, fontWeight: 700 }}>ideas.</span>
2024
+ </div>
2025
+ </AbsoluteFill>
2026
+ );
2027
+ };
2028
+
2029
+ // ═══════════════════════════════════════════════════════════════
2030
+ // SCENE 7 — YT MID CTA (frames 1429–1545, 3.90s)
2031
+ // "For the full setup tutorial, check my YouTube channel in my profile."
2032
+ // ═══════════════════════════════════════════════════════════════
2033
+ const YtMidCtaScene: React.FC = () => {
2034
+ const frame = useCurrentFrame();
2035
+ const cardScale = spring({
2036
+ frame,
2037
+ fps: 30,
2038
+ config: { damping: 12, stiffness: 120 },
2039
+ from: 0.6,
2040
+ to: 1,
2041
+ });
2042
+ return (
2043
+ <AbsoluteFill>
2044
+ <SonarRings accent={C.crimson} secondary={C.claude} />
2045
+ <LightBeam delay={20} angle={-6} />
2046
+
2047
+ <div style={{ position: "absolute", top: SAFE_TOP, left: 60 }}>
2048
+ <EyebrowPill dot={C.crimson}>07 / TUTORIAL</EyebrowPill>
2049
+ </div>
2050
+
2051
+ {/* Centered glass CTA card */}
2052
+ <div
2053
+ style={{
2054
+ position: "absolute",
2055
+ top: 720,
2056
+ left: "50%",
2057
+ transform: `translateX(-50%) scale(${cardScale})`,
2058
+ width: 880,
2059
+ ...glassBase,
2060
+ borderRadius: 32,
2061
+ padding: "48px 56px",
2062
+ display: "flex",
2063
+ flexDirection: "column",
2064
+ alignItems: "center",
2065
+ gap: 28,
2066
+ boxShadow: `${GLASS_SHADOW}, 0 0 48px ${C.crimson}44`,
2067
+ }}
2068
+ >
2069
+ <Img
2070
+ src={staticFile("captures/your-asset.png" /* REFERENCE-STRIP */)}
2071
+ style={{
2072
+ width: 180,
2073
+ height: 180,
2074
+ filter: `drop-shadow(0 0 24px ${C.crimson}aa)`,
2075
+ }}
2076
+ />
2077
+ <div
2078
+ style={{
2079
+ fontFamily: FONT,
2080
+ fontSize: 56,
2081
+ fontWeight: 800,
2082
+ color: C.ink,
2083
+ textAlign: "center",
2084
+ letterSpacing: "-0.03em",
2085
+ lineHeight: 1.05,
2086
+ }}
2087
+ >
2088
+ Full tutorial<br />on <span style={{ color: C.crimson }}>YouTube</span>.
2089
+ </div>
2090
+ <div
2091
+ style={{
2092
+ fontFamily: MONO,
2093
+ fontSize: 32,
2094
+ fontWeight: 700,
2095
+ color: C.crimson,
2096
+ letterSpacing: "-0.01em",
2097
+ filter: `drop-shadow(0 0 12px ${C.crimson}66)`,
2098
+ }}
2099
+ >
2100
+ @DeviniLabs
2101
+ </div>
2102
+ <div
2103
+ style={{
2104
+ ...glassBase,
2105
+ borderRadius: 12,
2106
+ padding: "10px 22px",
2107
+ fontFamily: MONO,
2108
+ fontSize: 22,
2109
+ color: C.inkSoft,
2110
+ letterSpacing: "0.16em",
2111
+ textTransform: "uppercase",
2112
+ }}
2113
+ >
2114
+ ↗ link in profile
2115
+ </div>
2116
+ </div>
2117
+ </AbsoluteFill>
2118
+ );
2119
+ };
2120
+
2121
+ // ═══════════════════════════════════════════════════════════════
2122
+ // SCENE 8 — UNDER THE HOOD (frames 1546–1851, 10.20s)
2123
+ // "Now here's how it actually works under the hood.
2124
+ // Claude doesn't have a native video model — so the skill splits every video
2125
+ // into the two things Claude does understand: images and text."
2126
+ // ═══════════════════════════════════════════════════════════════
2127
+ const HoodScene: React.FC = () => {
2128
+ const frame = useCurrentFrame();
2129
+
2130
+ // Frame splits at frame 60
2131
+ const splitProgress = interpolate(frame, [60, 100], [0, 1], {
2132
+ extrapolateLeft: "clamp",
2133
+ extrapolateRight: "clamp",
2134
+ easing: ease.power3Out,
2135
+ });
2136
+ const leftX = -240 * splitProgress;
2137
+ const rightX = 240 * splitProgress;
2138
+
2139
+ // Image grid + text grid materialize after split
2140
+ const gridFade = interpolate(frame, [100, 130], [0, 1], {
2141
+ extrapolateLeft: "clamp",
2142
+ extrapolateRight: "clamp",
2143
+ });
2144
+
2145
+ // No-native-video stamp
2146
+ const stampLocal = frame - 150;
2147
+ const stampScale = spring({
2148
+ frame: stampLocal,
2149
+ fps: 30,
2150
+ config: { damping: 8, stiffness: 200 },
2151
+ from: 0,
2152
+ to: 1,
2153
+ });
2154
+ const stampOp = interpolate(stampLocal, [0, 8, 60, 80], [0, 1, 1, 0], {
2155
+ extrapolateLeft: "clamp",
2156
+ extrapolateRight: "clamp",
2157
+ });
2158
+
2159
+ return (
2160
+ <AbsoluteFill>
2161
+ <SonarRings accent={C.claude} secondary={C.film} />
2162
+
2163
+ <div style={{ position: "absolute", top: SAFE_TOP, left: 60 }}>
2164
+ <EyebrowPill dot={C.claude}>08 / UNDER THE HOOD</EyebrowPill>
2165
+ </div>
2166
+
2167
+ {/* Headline */}
2168
+ <div style={{ position: "absolute", top: SAFE_TOP + 60, left: 60, right: 60 }}>
2169
+ <StaggeredWords
2170
+ text="Images + text. That's all Claude needs."
2171
+ startFrame={4}
2172
+ fontSize={72}
2173
+ fontWeight={700}
2174
+ highlight="text"
2175
+ highlightColor={C.vector}
2176
+ />
2177
+ </div>
2178
+
2179
+ {/* Original video frame splitting */}
2180
+ <div
2181
+ style={{
2182
+ position: "absolute",
2183
+ top: 840,
2184
+ left: "50%",
2185
+ transform: "translateX(-50%)",
2186
+ width: 480,
2187
+ height: 280,
2188
+ display: "flex",
2189
+ }}
2190
+ >
2191
+ {/* Left half */}
2192
+ <div
2193
+ style={{
2194
+ width: 240,
2195
+ height: 280,
2196
+ background: "linear-gradient(135deg, #1a1a1f, #2a2a30)",
2197
+ borderTopLeftRadius: 16,
2198
+ borderBottomLeftRadius: 16,
2199
+ transform: `translateX(${leftX}px)`,
2200
+ position: "relative",
2201
+ overflow: "hidden",
2202
+ }}
2203
+ >
2204
+ {/* Play triangle shows before split */}
2205
+ {splitProgress < 0.3 && (
2206
+ <div
2207
+ style={{
2208
+ position: "absolute",
2209
+ top: "50%",
2210
+ left: "100%",
2211
+ transform: "translate(-50%, -50%)",
2212
+ width: 0,
2213
+ height: 0,
2214
+ borderLeft: "28px solid white",
2215
+ borderTop: "18px solid transparent",
2216
+ borderBottom: "18px solid transparent",
2217
+ }}
2218
+ />
2219
+ )}
2220
+ {/* 4×3 image grid materializes after split */}
2221
+ <div
2222
+ style={{
2223
+ position: "absolute",
2224
+ inset: 8,
2225
+ display: "grid",
2226
+ gridTemplateColumns: "repeat(3, 1fr)",
2227
+ gridTemplateRows: "repeat(3, 1fr)",
2228
+ gap: 4,
2229
+ opacity: gridFade,
2230
+ }}
2231
+ >
2232
+ {Array.from({ length: 9 }).map((_, i) => (
2233
+ <div
2234
+ key={i}
2235
+ style={{
2236
+ background: `linear-gradient(135deg, ${[C.claude, C.film, C.vector, C.plasma, C.success][i % 5]}, ${C.ink})`,
2237
+ borderRadius: 4,
2238
+ }}
2239
+ />
2240
+ ))}
2241
+ </div>
2242
+ </div>
2243
+ {/* Right half */}
2244
+ <div
2245
+ style={{
2246
+ width: 240,
2247
+ height: 280,
2248
+ background: "linear-gradient(135deg, #2a2a30, #1a1a1f)",
2249
+ borderTopRightRadius: 16,
2250
+ borderBottomRightRadius: 16,
2251
+ transform: `translateX(${rightX}px)`,
2252
+ position: "relative",
2253
+ overflow: "hidden",
2254
+ }}
2255
+ >
2256
+ {/* Text lines materialize after split */}
2257
+ <div
2258
+ style={{
2259
+ position: "absolute",
2260
+ inset: 16,
2261
+ display: "flex",
2262
+ flexDirection: "column",
2263
+ gap: 8,
2264
+ opacity: gridFade,
2265
+ fontFamily: MONO,
2266
+ fontSize: 12,
2267
+ color: C.bg,
2268
+ lineHeight: 1.4,
2269
+ }}
2270
+ >
2271
+ {[
2272
+ "[00:14] Subject enters",
2273
+ "the frame from left,",
2274
+ "wearing a red jacket.",
2275
+ "[00:27] Cut to product",
2276
+ "shot, white background,",
2277
+ "soft lighting.",
2278
+ "[00:42] Speaker walks",
2279
+ "and talks to camera.",
2280
+ ].map((line, i) => (
2281
+ <div key={i} style={{ opacity: 0.85 }}>
2282
+ {line}
2283
+ </div>
2284
+ ))}
2285
+ </div>
2286
+ </div>
2287
+ </div>
2288
+
2289
+ {/* Stream labels */}
2290
+ <div
2291
+ style={{
2292
+ position: "absolute",
2293
+ top: 1160,
2294
+ left: 60,
2295
+ width: 480,
2296
+ textAlign: "center",
2297
+ fontFamily: MONO,
2298
+ fontSize: 26,
2299
+ fontWeight: 700,
2300
+ color: C.film,
2301
+ letterSpacing: "0.18em",
2302
+ opacity: gridFade,
2303
+ }}
2304
+ >
2305
+ IMAGES
2306
+ </div>
2307
+ <div
2308
+ style={{
2309
+ position: "absolute",
2310
+ top: 1160,
2311
+ right: 60,
2312
+ width: 480,
2313
+ textAlign: "center",
2314
+ fontFamily: MONO,
2315
+ fontSize: 26,
2316
+ fontWeight: 700,
2317
+ color: C.vector,
2318
+ letterSpacing: "0.18em",
2319
+ opacity: gridFade,
2320
+ }}
2321
+ >
2322
+ TEXT
2323
+ </div>
2324
+
2325
+ {/* No-native-video stamp */}
2326
+ <div
2327
+ style={{
2328
+ position: "absolute",
2329
+ top: 1280,
2330
+ left: "50%",
2331
+ transform: `translateX(-50%) scale(${stampScale}) rotate(-6deg)`,
2332
+ opacity: stampOp,
2333
+ ...glassBase,
2334
+ borderRadius: 12,
2335
+ padding: "12px 28px",
2336
+ fontFamily: MONO,
2337
+ fontSize: 26,
2338
+ fontWeight: 700,
2339
+ letterSpacing: "0.16em",
2340
+ color: C.crimson,
2341
+ border: `2px solid ${C.crimson}`,
2342
+ boxShadow: `${GLASS_SHADOW}, 0 0 24px ${C.crimson}88`,
2343
+ }}
2344
+ >
2345
+ × NO NATIVE VIDEO MODEL
2346
+ </div>
2347
+ </AbsoluteFill>
2348
+ );
2349
+ };
2350
+
2351
+ // ═══════════════════════════════════════════════════════════════
2352
+ // SCENE 9 — yt-dlp (frames 1852–2152, 10.03s)
2353
+ // "It uses yt-dlp to pull the video from basically any site on the internet —
2354
+ // YouTube, Loom, Instagram and over all platforms."
2355
+ // ═══════════════════════════════════════════════════════════════
2356
+ const YtDlpScene: React.FC = () => {
2357
+ const frame = useCurrentFrame();
2358
+ const platforms = [
2359
+ { icon: "icons/youtube.svg", label: "YouTube", color: C.crimson },
2360
+ { icon: "icons/loom.svg", label: "Loom", color: C.vector },
2361
+ { icon: "icons/instagram.svg", label: "Instagram", color: C.plasma },
2362
+ { icon: "icons/web.svg", label: "+ more", color: C.film },
2363
+ ];
2364
+
2365
+ return (
2366
+ <AbsoluteFill>
2367
+ <SonarRings accent={C.success} secondary={C.claude} />
2368
+
2369
+ <div style={{ position: "absolute", top: SAFE_TOP, left: 60 }}>
2370
+ <EyebrowPill dot={C.success}>09 / yt-dlp</EyebrowPill>
2371
+ </div>
2372
+
2373
+ {/* Headline */}
2374
+ <div style={{ position: "absolute", top: SAFE_TOP + 60, left: 60, right: 60 }}>
2375
+ <StaggeredWords
2376
+ text="Pull from anywhere."
2377
+ startFrame={4}
2378
+ fontSize={92}
2379
+ fontWeight={700}
2380
+ highlight="anywhere"
2381
+ highlightColor={C.success}
2382
+ />
2383
+ </div>
2384
+
2385
+ {/* Terminal pill */}
2386
+ <div
2387
+ style={{
2388
+ position: "absolute",
2389
+ top: 580,
2390
+ left: 60,
2391
+ right: 60,
2392
+ ...glassBase,
2393
+ background: C.glassFillStrong,
2394
+ borderRadius: 18,
2395
+ padding: "20px 28px",
2396
+ fontFamily: MONO,
2397
+ fontSize: 32,
2398
+ color: C.ink,
2399
+ }}
2400
+ >
2401
+ <span style={{ color: C.success }}>$</span>{" "}
2402
+ <span style={{ color: C.claude, fontWeight: 600 }}>yt-dlp</span>{" "}
2403
+ <span style={{ color: C.inkMuted }}>https://...</span>
2404
+ {Math.floor(frame / 12) % 2 === 0 && (
2405
+ <span
2406
+ style={{
2407
+ display: "inline-block",
2408
+ width: 14,
2409
+ height: 32,
2410
+ background: C.ink,
2411
+ marginLeft: 4,
2412
+ verticalAlign: "middle",
2413
+ }}
2414
+ />
2415
+ )}
2416
+ </div>
2417
+
2418
+ {/* Platform constellation arc */}
2419
+ {platforms.map((p, i) => {
2420
+ const pLocal = frame - (40 + i * 9);
2421
+ const sc = spring({
2422
+ frame: pLocal,
2423
+ fps: 30,
2424
+ config: { damping: 10, stiffness: 120 },
2425
+ from: 0.4,
2426
+ to: 1,
2427
+ });
2428
+ const op = interpolate(pLocal, [0, 16], [0, 1], {
2429
+ extrapolateLeft: "clamp",
2430
+ extrapolateRight: "clamp",
2431
+ });
2432
+ const arcAngle = (-Math.PI / 2) + (i / 3) * (Math.PI * 0.6) - Math.PI * 0.3;
2433
+ const cx = 540 + Math.cos(arcAngle) * 320;
2434
+ const cy = 1080 + Math.sin(arcAngle) * 200;
2435
+ return (
2436
+ <div
2437
+ key={p.label}
2438
+ style={{
2439
+ position: "absolute",
2440
+ left: cx - 110,
2441
+ top: cy - 110,
2442
+ width: 220,
2443
+ height: 220,
2444
+ ...glassBase,
2445
+ borderRadius: 28,
2446
+ padding: 24,
2447
+ transform: `scale(${sc})`,
2448
+ opacity: op,
2449
+ display: "flex",
2450
+ flexDirection: "column",
2451
+ alignItems: "center",
2452
+ justifyContent: "center",
2453
+ gap: 12,
2454
+ boxShadow: `${GLASS_SHADOW}, 0 0 28px ${p.color}55`,
2455
+ }}
2456
+ >
2457
+ {p.icon === "icons/web.svg" ? (
2458
+ <div
2459
+ style={{
2460
+ width: 90,
2461
+ height: 90,
2462
+ borderRadius: 22,
2463
+ background: `linear-gradient(135deg, ${p.color}, ${C.claude})`,
2464
+ display: "flex",
2465
+ alignItems: "center",
2466
+ justifyContent: "center",
2467
+ fontFamily: FONT,
2468
+ fontSize: 56,
2469
+ fontWeight: 800,
2470
+ color: "white",
2471
+ letterSpacing: "-0.04em",
2472
+ boxShadow: `inset 0 0 0 2px rgba(255,255,255,0.3), 0 0 12px ${p.color}99`,
2473
+ }}
2474
+ >
2475
+ +
2476
+ </div>
2477
+ ) : (
2478
+ <Img
2479
+ src={staticFile("captures/your-asset.png" /* REFERENCE-STRIP */)}
2480
+ style={{
2481
+ width: 90,
2482
+ height: 90,
2483
+ filter: `drop-shadow(0 0 12px ${p.color}99)`,
2484
+ }}
2485
+ />
2486
+ )}
2487
+ <div
2488
+ style={{
2489
+ fontFamily: MONO,
2490
+ fontSize: 18,
2491
+ fontWeight: 600,
2492
+ color: C.inkSoft,
2493
+ letterSpacing: "0.05em",
2494
+ }}
2495
+ >
2496
+ {p.label}
2497
+ </div>
2498
+ </div>
2499
+ );
2500
+ })}
2501
+
2502
+ {/* Pull lines from each platform → central video file card */}
2503
+ <svg
2504
+ width={1080}
2505
+ height={500}
2506
+ viewBox="0 0 1080 500"
2507
+ style={{ position: "absolute", top: 1080, left: 0, pointerEvents: "none" }}
2508
+ >
2509
+ {platforms.map((p, i) => {
2510
+ const lineLocal = frame - (140 + i * 4);
2511
+ const draw = interpolate(lineLocal, [0, 30], [0, 1], {
2512
+ extrapolateLeft: "clamp",
2513
+ extrapolateRight: "clamp",
2514
+ easing: ease.power3Out,
2515
+ });
2516
+ const arcAngle = (-Math.PI / 2) + (i / 3) * (Math.PI * 0.6) - Math.PI * 0.3;
2517
+ const startX = 540 + Math.cos(arcAngle) * 320;
2518
+ const startY = 0 + Math.sin(arcAngle) * 200 + 110;
2519
+ const endX = 540;
2520
+ const endY = 380;
2521
+ return (
2522
+ <line
2523
+ key={i}
2524
+ x1={startX}
2525
+ y1={startY}
2526
+ x2={startX + (endX - startX) * draw}
2527
+ y2={startY + (endY - startY) * draw}
2528
+ stroke={p.color}
2529
+ strokeWidth={2}
2530
+ strokeDasharray="4 6"
2531
+ opacity={0.75}
2532
+ style={{ filter: `drop-shadow(0 0 4px ${p.color})` }}
2533
+ />
2534
+ );
2535
+ })}
2536
+ </svg>
2537
+
2538
+ {/* Central video file card */}
2539
+ <div
2540
+ style={{
2541
+ position: "absolute",
2542
+ top: 1440,
2543
+ left: "50%",
2544
+ transform: `translateX(-50%) scale(${spring({
2545
+ frame: frame - 170,
2546
+ fps: 30,
2547
+ config: { damping: 10, stiffness: 130 },
2548
+ from: 0,
2549
+ to: 1,
2550
+ })})`,
2551
+ width: 360,
2552
+ ...glassBase,
2553
+ borderRadius: 20,
2554
+ padding: "20px 28px",
2555
+ display: "flex",
2556
+ alignItems: "center",
2557
+ gap: 14,
2558
+ boxShadow: `${GLASS_SHADOW}, 0 0 32px ${C.success}55`,
2559
+ }}
2560
+ >
2561
+ <div
2562
+ style={{
2563
+ width: 56,
2564
+ height: 56,
2565
+ borderRadius: 12,
2566
+ background: `linear-gradient(135deg, ${C.claude}, ${C.film})`,
2567
+ display: "flex",
2568
+ alignItems: "center",
2569
+ justifyContent: "center",
2570
+ }}
2571
+ >
2572
+ <div
2573
+ style={{
2574
+ width: 0,
2575
+ height: 0,
2576
+ borderLeft: "16px solid white",
2577
+ borderTop: "10px solid transparent",
2578
+ borderBottom: "10px solid transparent",
2579
+ marginLeft: 4,
2580
+ }}
2581
+ />
2582
+ </div>
2583
+ <div>
2584
+ <div
2585
+ style={{ fontFamily: MONO, fontSize: 22, fontWeight: 600, color: C.ink }}
2586
+ >
2587
+ video.mp4
2588
+ </div>
2589
+ <div
2590
+ style={{
2591
+ fontFamily: MONO,
2592
+ fontSize: 16,
2593
+ color: C.success,
2594
+ letterSpacing: "0.12em",
2595
+ }}
2596
+ >
2597
+ ✓ DOWNLOADED
2598
+ </div>
2599
+ </div>
2600
+ </div>
2601
+ </AbsoluteFill>
2602
+ );
2603
+ };
2604
+
2605
+ // ═══════════════════════════════════════════════════════════════
2606
+ // SCENE 10 — FFMPEG + WHISPER (frames 2153–2500, 11.60s)
2607
+ // "Then FFmpeg rips out frames across the whole video and pulls a clean audio track.
2608
+ // The audio gets transcribed — free if YouTube has captions,
2609
+ // or through Whisper on Groq's free tier if not."
2610
+ // ═══════════════════════════════════════════════════════════════
2611
+ const FfmpegScene: React.FC = () => {
2612
+ const frame = useCurrentFrame();
2613
+ // Part A (0–180): frame-rip
2614
+ // Part B (180–end): transcribe paths
2615
+
2616
+ // Audio waveform bars
2617
+ const bars = Array.from({ length: 64 });
2618
+
2619
+ // Eyebrow swap at frame 180
2620
+ const showExtract = frame < 180;
2621
+
2622
+ return (
2623
+ <AbsoluteFill>
2624
+ <SonarRings accent={C.film} secondary={C.plasma} />
2625
+
2626
+ <div style={{ position: "absolute", top: SAFE_TOP, left: 60 }}>
2627
+ <EyebrowPill dot={showExtract ? C.film : C.plasma}>
2628
+ {showExtract ? "10 / EXTRACT" : "11 / TRANSCRIBE"}
2629
+ </EyebrowPill>
2630
+ </div>
2631
+
2632
+ {/* PART A — FFmpeg blade */}
2633
+ {showExtract && (
2634
+ <>
2635
+ <div style={{ position: "absolute", top: SAFE_TOP + 60, left: 60, right: 60 }}>
2636
+ <StaggeredWords
2637
+ text="FFmpeg rips frames + audio."
2638
+ startFrame={4}
2639
+ fontSize={72}
2640
+ fontWeight={700}
2641
+ highlight="ffmpeg"
2642
+ highlightColor={C.film}
2643
+ />
2644
+ </div>
2645
+
2646
+ {/* FFmpeg logo center */}
2647
+ <div
2648
+ style={{
2649
+ position: "absolute",
2650
+ top: 600,
2651
+ left: "50%",
2652
+ transform: `translateX(-50%) rotate(${frame * 6}deg)`,
2653
+ width: 140,
2654
+ height: 140,
2655
+ display: "flex",
2656
+ alignItems: "center",
2657
+ justifyContent: "center",
2658
+ filter: `drop-shadow(0 0 20px ${C.film}88)`,
2659
+ }}
2660
+ >
2661
+ <Img src={staticFile("captures/your-asset.png" /* REFERENCE-STRIP */)} style={{ width: 120, height: 120 }} />
2662
+ </div>
2663
+
2664
+ {/* Top stream — film strip */}
2665
+ <div
2666
+ style={{
2667
+ position: "absolute",
2668
+ top: 820,
2669
+ left: 0,
2670
+ right: 0,
2671
+ padding: "0 60px",
2672
+ opacity: interpolate(frame, [40, 70], [0, 1], {
2673
+ extrapolateLeft: "clamp",
2674
+ extrapolateRight: "clamp",
2675
+ }),
2676
+ }}
2677
+ >
2678
+ <FilmStrip speed={3} height={120} width={960} />
2679
+ </div>
2680
+ <div
2681
+ style={{
2682
+ position: "absolute",
2683
+ top: 960,
2684
+ left: 60,
2685
+ fontFamily: MONO,
2686
+ fontSize: 22,
2687
+ color: C.film,
2688
+ fontWeight: 600,
2689
+ letterSpacing: "0.18em",
2690
+ textTransform: "uppercase",
2691
+ opacity: interpolate(frame, [60, 80], [0, 1], {
2692
+ extrapolateLeft: "clamp",
2693
+ extrapolateRight: "clamp",
2694
+ }),
2695
+ }}
2696
+ >
2697
+ ▶ frame stream
2698
+ </div>
2699
+
2700
+ {/* Bottom stream — waveform */}
2701
+ <div
2702
+ style={{
2703
+ position: "absolute",
2704
+ top: 1100,
2705
+ left: 60,
2706
+ right: 60,
2707
+ height: 160,
2708
+ ...glassBase,
2709
+ borderRadius: 16,
2710
+ padding: "20px 24px",
2711
+ display: "flex",
2712
+ alignItems: "center",
2713
+ gap: 6,
2714
+ opacity: interpolate(frame, [80, 110], [0, 1], {
2715
+ extrapolateLeft: "clamp",
2716
+ extrapolateRight: "clamp",
2717
+ }),
2718
+ }}
2719
+ >
2720
+ {bars.map((_, i) => {
2721
+ const h = Math.abs(Math.sin(i * 0.4 + frame * 0.2)) * 70 + 12;
2722
+ return (
2723
+ <div
2724
+ key={i}
2725
+ style={{
2726
+ width: 8,
2727
+ height: h,
2728
+ background: `linear-gradient(180deg, ${C.plasma}, ${C.vector})`,
2729
+ borderRadius: 4,
2730
+ boxShadow: `0 0 6px ${C.plasma}55`,
2731
+ }}
2732
+ />
2733
+ );
2734
+ })}
2735
+ </div>
2736
+ <div
2737
+ style={{
2738
+ position: "absolute",
2739
+ top: 1280,
2740
+ left: 60,
2741
+ fontFamily: MONO,
2742
+ fontSize: 22,
2743
+ color: C.plasma,
2744
+ fontWeight: 600,
2745
+ letterSpacing: "0.18em",
2746
+ textTransform: "uppercase",
2747
+ opacity: interpolate(frame, [110, 130], [0, 1], {
2748
+ extrapolateLeft: "clamp",
2749
+ extrapolateRight: "clamp",
2750
+ }),
2751
+ }}
2752
+ >
2753
+ ♪ audio stream
2754
+ </div>
2755
+ </>
2756
+ )}
2757
+
2758
+ {/* PART B — Transcribe branches */}
2759
+ {!showExtract && (
2760
+ <>
2761
+ <div style={{ position: "absolute", top: SAFE_TOP + 60, left: 60, right: 60 }}>
2762
+ <StaggeredWords
2763
+ text="Transcribe. Free."
2764
+ startFrame={184}
2765
+ fontSize={92}
2766
+ fontWeight={700}
2767
+ highlight="free"
2768
+ highlightColor={C.success}
2769
+ />
2770
+ </div>
2771
+
2772
+ {/* Persistent waveform on top */}
2773
+ <div
2774
+ style={{
2775
+ position: "absolute",
2776
+ top: 540,
2777
+ left: 60,
2778
+ right: 60,
2779
+ height: 100,
2780
+ ...glassBase,
2781
+ borderRadius: 14,
2782
+ padding: "12px 20px",
2783
+ display: "flex",
2784
+ alignItems: "center",
2785
+ justifyContent: "space-between",
2786
+ }}
2787
+ >
2788
+ {bars.map((_, i) => {
2789
+ const h = Math.abs(Math.sin(i * 0.4 + frame * 0.2)) * 50 + 8;
2790
+ return (
2791
+ <div
2792
+ key={i}
2793
+ style={{
2794
+ width: 6,
2795
+ height: h,
2796
+ background: `linear-gradient(180deg, ${C.plasma}, ${C.vector})`,
2797
+ borderRadius: 4,
2798
+ }}
2799
+ />
2800
+ );
2801
+ })}
2802
+ </div>
2803
+
2804
+ {/* LEFT branch — YouTube CC */}
2805
+ {(() => {
2806
+ const lLocal = frame - 195;
2807
+ const sc = spring({
2808
+ frame: lLocal,
2809
+ fps: 30,
2810
+ config: { damping: 11, stiffness: 130 },
2811
+ from: 0.5,
2812
+ to: 1,
2813
+ });
2814
+ const op = interpolate(lLocal, [0, 18], [0, 1], {
2815
+ extrapolateLeft: "clamp",
2816
+ extrapolateRight: "clamp",
2817
+ });
2818
+ return (
2819
+ <div
2820
+ style={{
2821
+ position: "absolute",
2822
+ top: 740,
2823
+ left: 60,
2824
+ width: 440,
2825
+ height: 340,
2826
+ ...glassBase,
2827
+ borderRadius: 24,
2828
+ padding: 24,
2829
+ transform: `scale(${sc})`,
2830
+ opacity: op,
2831
+ display: "flex",
2832
+ flexDirection: "column",
2833
+ alignItems: "center",
2834
+ justifyContent: "center",
2835
+ gap: 14,
2836
+ boxShadow: `${GLASS_SHADOW}, 0 0 24px ${C.success}55`,
2837
+ }}
2838
+ >
2839
+ <Img
2840
+ src={staticFile("captures/your-asset.png" /* REFERENCE-STRIP */)}
2841
+ style={{
2842
+ width: 100,
2843
+ height: 100,
2844
+ filter: `drop-shadow(0 0 12px ${C.crimson}88)`,
2845
+ }}
2846
+ />
2847
+ <div
2848
+ style={{
2849
+ fontFamily: MONO,
2850
+ fontSize: 24,
2851
+ fontWeight: 700,
2852
+ color: C.ink,
2853
+ letterSpacing: "0.06em",
2854
+ }}
2855
+ >
2856
+ CAPTIONS
2857
+ </div>
2858
+ <div
2859
+ style={{
2860
+ ...glassBase,
2861
+ borderRadius: 8,
2862
+ padding: "6px 16px",
2863
+ fontFamily: MONO,
2864
+ fontSize: 18,
2865
+ color: C.success,
2866
+ fontWeight: 700,
2867
+ letterSpacing: "0.18em",
2868
+ border: `1.5px solid ${C.success}`,
2869
+ boxShadow: `0 0 12px ${C.success}55`,
2870
+ }}
2871
+ >
2872
+ $0 — FREE
2873
+ </div>
2874
+ </div>
2875
+ );
2876
+ })()}
2877
+
2878
+ {/* OR divider */}
2879
+ <div
2880
+ style={{
2881
+ position: "absolute",
2882
+ top: 896,
2883
+ left: 540,
2884
+ transform: "translateX(-50%)",
2885
+ fontFamily: MONO,
2886
+ fontSize: 28,
2887
+ fontWeight: 700,
2888
+ color: C.inkMuted,
2889
+ letterSpacing: "0.18em",
2890
+ opacity: interpolate(frame, [205, 225], [0, 1], {
2891
+ extrapolateLeft: "clamp",
2892
+ extrapolateRight: "clamp",
2893
+ }),
2894
+ }}
2895
+ >
2896
+ OR
2897
+ </div>
2898
+
2899
+ {/* RIGHT branch — Whisper / Groq */}
2900
+ {(() => {
2901
+ const rLocal = frame - 215;
2902
+ const sc = spring({
2903
+ frame: rLocal,
2904
+ fps: 30,
2905
+ config: { damping: 11, stiffness: 130 },
2906
+ from: 0.5,
2907
+ to: 1,
2908
+ });
2909
+ const op = interpolate(rLocal, [0, 18], [0, 1], {
2910
+ extrapolateLeft: "clamp",
2911
+ extrapolateRight: "clamp",
2912
+ });
2913
+ return (
2914
+ <div
2915
+ style={{
2916
+ position: "absolute",
2917
+ top: 740,
2918
+ right: 60,
2919
+ width: 440,
2920
+ height: 340,
2921
+ ...glassBase,
2922
+ borderRadius: 24,
2923
+ padding: 24,
2924
+ transform: `scale(${sc})`,
2925
+ opacity: op,
2926
+ display: "flex",
2927
+ flexDirection: "column",
2928
+ alignItems: "center",
2929
+ justifyContent: "center",
2930
+ gap: 14,
2931
+ boxShadow: `${GLASS_SHADOW}, 0 0 24px ${C.film}55`,
2932
+ }}
2933
+ >
2934
+ <div style={{ display: "flex", gap: 14, alignItems: "center" }}>
2935
+ <Img
2936
+ src={staticFile("captures/your-asset.png" /* REFERENCE-STRIP */)}
2937
+ style={{
2938
+ width: 80,
2939
+ height: 80,
2940
+ filter: `drop-shadow(0 0 12px ${C.plasma}88)`,
2941
+ }}
2942
+ />
2943
+ <div
2944
+ style={{
2945
+ fontFamily: MONO,
2946
+ fontSize: 28,
2947
+ fontWeight: 600,
2948
+ color: C.plasma,
2949
+ letterSpacing: "-0.02em",
2950
+ }}
2951
+ >
2952
+ WHISPER
2953
+ </div>
2954
+ </div>
2955
+ <div
2956
+ style={{
2957
+ width: 80,
2958
+ height: 4,
2959
+ background: `linear-gradient(90deg, ${C.plasma}, ${C.film})`,
2960
+ borderRadius: 2,
2961
+ }}
2962
+ />
2963
+ <div style={{ display: "flex", gap: 14, alignItems: "center" }}>
2964
+ <Img
2965
+ src={staticFile("captures/your-asset.png" /* REFERENCE-STRIP */)}
2966
+ style={{
2967
+ width: 80,
2968
+ height: 80,
2969
+ filter: `drop-shadow(0 0 12px ${C.film}88)`,
2970
+ }}
2971
+ />
2972
+ <div
2973
+ style={{
2974
+ fontFamily: MONO,
2975
+ fontSize: 28,
2976
+ fontWeight: 700,
2977
+ color: C.film,
2978
+ letterSpacing: "0.04em",
2979
+ }}
2980
+ >
2981
+ GROQ
2982
+ </div>
2983
+ </div>
2984
+ <div
2985
+ style={{
2986
+ ...glassBase,
2987
+ borderRadius: 8,
2988
+ padding: "6px 16px",
2989
+ fontFamily: MONO,
2990
+ fontSize: 16,
2991
+ color: C.success,
2992
+ fontWeight: 700,
2993
+ letterSpacing: "0.18em",
2994
+ border: `1.5px solid ${C.success}`,
2995
+ }}
2996
+ >
2997
+ FREE TIER
2998
+ </div>
2999
+ </div>
3000
+ );
3001
+ })()}
3002
+
3003
+ {/* Output text rolling */}
3004
+ <div
3005
+ style={{
3006
+ position: "absolute",
3007
+ top: 1240,
3008
+ left: 60,
3009
+ right: 60,
3010
+ ...glassBase,
3011
+ borderRadius: 14,
3012
+ padding: "16px 20px",
3013
+ fontFamily: MONO,
3014
+ fontSize: 20,
3015
+ color: C.inkSoft,
3016
+ lineHeight: 1.5,
3017
+ opacity: interpolate(frame, [240, 270], [0, 1], {
3018
+ extrapolateLeft: "clamp",
3019
+ extrapolateRight: "clamp",
3020
+ }),
3021
+ }}
3022
+ >
3023
+ <div style={{ color: C.success, marginBottom: 4 }}>✓ transcript.srt</div>
3024
+ <div>[00:00:00] Welcome back to the channel...</div>
3025
+ <div>[00:00:14] Today we're going to look at...</div>
3026
+ <div>[00:00:27] So the first thing you need is...</div>
3027
+ </div>
3028
+ </>
3029
+ )}
3030
+ </AbsoluteFill>
3031
+ );
3032
+ };
3033
+
3034
+ // ═══════════════════════════════════════════════════════════════
3035
+ // SCENE 11 — FLIPBOOK (frames 2501–2818, 10.60s)
3036
+ // "Then everything gets handed to Claude with the timestamps lined up.
3037
+ // So Claude isn't guessing from a transcript anymore — it's flipping through
3038
+ // the frames like a flipbook while reading the script."
3039
+ // ═══════════════════════════════════════════════════════════════
3040
+ const FlipbookScene: React.FC = () => {
3041
+ const frame = useCurrentFrame();
3042
+ const pageIdx = Math.floor(frame / 9) % 5;
3043
+ const captions = [
3044
+ "[00:14] Subject enters frame, red jacket.",
3045
+ "[00:27] Cut to product shot, white bg.",
3046
+ "[00:42] Speaker walks toward camera.",
3047
+ "[00:58] Title card: 'How it works'.",
3048
+ "[01:14] Demo on screen, terminal visible.",
3049
+ ];
3050
+
3051
+ return (
3052
+ <AbsoluteFill>
3053
+ <SonarRings accent={C.claude} secondary={C.film} />
3054
+ {frame > 60 && frame < 90 && <LightBeam delay={60} angle={-4} />}
3055
+
3056
+ <div style={{ position: "absolute", top: SAFE_TOP, left: 60 }}>
3057
+ <EyebrowPill dot={C.claude}>12 / SYNC</EyebrowPill>
3058
+ </div>
3059
+
3060
+ <div style={{ position: "absolute", top: SAFE_TOP + 60, left: 60, right: 60 }}>
3061
+ <StaggeredWords
3062
+ text="Claude reads. Claude watches."
3063
+ startFrame={4}
3064
+ fontSize={76}
3065
+ fontWeight={700}
3066
+ highlight="watches"
3067
+ highlightColor={C.film}
3068
+ />
3069
+ <div style={{ marginTop: 4 }}>
3070
+ <StaggeredWords
3071
+ text="Same time."
3072
+ startFrame={36}
3073
+ fontSize={76}
3074
+ fontWeight={700}
3075
+ />
3076
+ </div>
3077
+ </div>
3078
+
3079
+ {/* Flipbook stack */}
3080
+ <div
3081
+ style={{
3082
+ position: "absolute",
3083
+ top: 700,
3084
+ left: 80,
3085
+ width: 480,
3086
+ height: 320,
3087
+ perspective: 1200,
3088
+ }}
3089
+ >
3090
+ {Array.from({ length: 5 }).map((_, i) => {
3091
+ const isCurrent = i === pageIdx;
3092
+ const offset = i - pageIdx;
3093
+ return (
3094
+ <div
3095
+ key={i}
3096
+ style={{
3097
+ position: "absolute",
3098
+ inset: 0,
3099
+ background: `linear-gradient(135deg, ${[C.claude, C.film, C.vector, C.plasma, C.success][i]}, ${C.ink})`,
3100
+ borderRadius: 16,
3101
+ transform: `translate3d(${offset * 12}px, ${-offset * 8}px, ${-Math.abs(offset) * 30}px) rotateY(${offset * -8}deg)`,
3102
+ boxShadow: "0 12px 32px -8px rgba(0,0,0,0.4)",
3103
+ opacity: isCurrent ? 1 : 0.5,
3104
+ transition: "all 0.12s ease",
3105
+ }}
3106
+ >
3107
+ <div
3108
+ style={{
3109
+ position: "absolute",
3110
+ bottom: 14,
3111
+ left: 14,
3112
+ right: 14,
3113
+ fontFamily: MONO,
3114
+ fontSize: 16,
3115
+ color: "white",
3116
+ background: "rgba(0,0,0,0.4)",
3117
+ padding: "6px 12px",
3118
+ borderRadius: 6,
3119
+ }}
3120
+ >
3121
+ {captions[i]}
3122
+ </div>
3123
+ <div
3124
+ style={{
3125
+ position: "absolute",
3126
+ top: "50%",
3127
+ left: "50%",
3128
+ transform: "translate(-50%,-50%)",
3129
+ width: 0,
3130
+ height: 0,
3131
+ borderLeft: "24px solid rgba(255,255,255,0.5)",
3132
+ borderTop: "16px solid transparent",
3133
+ borderBottom: "16px solid transparent",
3134
+ }}
3135
+ />
3136
+ </div>
3137
+ );
3138
+ })}
3139
+ </div>
3140
+
3141
+ {/* Claude logo */}
3142
+ <div
3143
+ style={{
3144
+ position: "absolute",
3145
+ top: 700,
3146
+ right: 80,
3147
+ width: 280,
3148
+ height: 320,
3149
+ display: "flex",
3150
+ flexDirection: "column",
3151
+ alignItems: "center",
3152
+ justifyContent: "center",
3153
+ gap: 18,
3154
+ }}
3155
+ >
3156
+ <div
3157
+ style={{
3158
+ width: 200,
3159
+ height: 200,
3160
+ ...glassBase,
3161
+ borderRadius: "50%",
3162
+ display: "flex",
3163
+ alignItems: "center",
3164
+ justifyContent: "center",
3165
+ boxShadow: `${GLASS_SHADOW}, 0 0 40px ${C.claude}77`,
3166
+ }}
3167
+ >
3168
+ <Img
3169
+ src={staticFile("captures/your-asset.png" /* REFERENCE-STRIP */)}
3170
+ style={{
3171
+ width: 120,
3172
+ height: 120,
3173
+ filter: `drop-shadow(0 0 12px ${C.claude})`,
3174
+ }}
3175
+ />
3176
+ </div>
3177
+ <div
3178
+ style={{
3179
+ fontFamily: MONO,
3180
+ fontSize: 18,
3181
+ color: C.inkMuted,
3182
+ letterSpacing: "0.18em",
3183
+ textTransform: "uppercase",
3184
+ }}
3185
+ >
3186
+ claude
3187
+ </div>
3188
+ </div>
3189
+
3190
+ {/* Caption-bubble showing what Claude sees on the current page */}
3191
+ <div
3192
+ style={{
3193
+ position: "absolute",
3194
+ top: 1080,
3195
+ left: 60,
3196
+ right: 60,
3197
+ ...glassBase,
3198
+ borderRadius: 18,
3199
+ padding: "20px 28px",
3200
+ fontFamily: MONO,
3201
+ fontSize: 26,
3202
+ color: C.ink,
3203
+ letterSpacing: "-0.01em",
3204
+ lineHeight: 1.4,
3205
+ boxShadow: `${GLASS_SHADOW}, 0 0 24px ${C.claude}33`,
3206
+ }}
3207
+ >
3208
+ <div
3209
+ style={{
3210
+ fontSize: 16,
3211
+ color: C.claude,
3212
+ letterSpacing: "0.18em",
3213
+ marginBottom: 6,
3214
+ textTransform: "uppercase",
3215
+ fontWeight: 700,
3216
+ }}
3217
+ >
3218
+ ▸ claude sees:
3219
+ </div>
3220
+ {captions[pageIdx]}
3221
+ </div>
3222
+
3223
+ {/* Timestamp pills along the bottom */}
3224
+ <div
3225
+ style={{
3226
+ position: "absolute",
3227
+ top: 1300,
3228
+ left: 60,
3229
+ right: 60,
3230
+ display: "flex",
3231
+ gap: 12,
3232
+ flexWrap: "wrap",
3233
+ justifyContent: "center",
3234
+ opacity: interpolate(frame, [80, 120], [0, 1], {
3235
+ extrapolateLeft: "clamp",
3236
+ extrapolateRight: "clamp",
3237
+ }),
3238
+ }}
3239
+ >
3240
+ {["00:14", "00:27", "00:42", "00:58", "01:14"].map((ts, i) => (
3241
+ <div
3242
+ key={ts}
3243
+ style={{
3244
+ ...glassBase,
3245
+ borderRadius: 8,
3246
+ padding: "6px 14px",
3247
+ fontFamily: MONO,
3248
+ fontSize: 18,
3249
+ fontWeight: 600,
3250
+ color: i === pageIdx ? C.claude : C.inkMuted,
3251
+ border: `1px solid ${i === pageIdx ? C.claude : C.hairline}`,
3252
+ boxShadow: i === pageIdx ? `0 0 16px ${C.claude}55` : "none",
3253
+ letterSpacing: "0.05em",
3254
+ }}
3255
+ >
3256
+ {ts}
3257
+ </div>
3258
+ ))}
3259
+ </div>
3260
+ </AbsoluteFill>
3261
+ );
3262
+ };
3263
+
3264
+ // ═══════════════════════════════════════════════════════════════
3265
+ // SCENE 12 — LOCAL (frames 2819–3052, 7.80s)
3266
+ // "It knows exactly what's on screen the moment something is being said.
3267
+ // All running locally. No expensive video API."
3268
+ // ═══════════════════════════════════════════════════════════════
3269
+ const LocalScene: React.FC = () => {
3270
+ const frame = useCurrentFrame();
3271
+ const localScale = spring({
3272
+ frame: frame - 4,
3273
+ fps: 30,
3274
+ config: { damping: 10, stiffness: 130 },
3275
+ from: 0.4,
3276
+ to: 1,
3277
+ });
3278
+ const noApiScale = spring({
3279
+ frame: frame - 24,
3280
+ fps: 30,
3281
+ config: { damping: 10, stiffness: 130 },
3282
+ from: 0.4,
3283
+ to: 1,
3284
+ });
3285
+
3286
+ return (
3287
+ <AbsoluteFill>
3288
+ <SonarRings accent={C.success} secondary={C.crimson} />
3289
+
3290
+ <div style={{ position: "absolute", top: SAFE_TOP, left: 60 }}>
3291
+ <EyebrowPill dot={C.success}>13 / LOCAL</EyebrowPill>
3292
+ </div>
3293
+
3294
+ {/* Headline */}
3295
+ <div style={{ position: "absolute", top: SAFE_TOP + 60, left: 60, right: 60 }}>
3296
+ <StaggeredWords
3297
+ text="Runs on your machine."
3298
+ startFrame={4}
3299
+ fontSize={88}
3300
+ fontWeight={700}
3301
+ highlight="your"
3302
+ highlightColor={C.claude}
3303
+ />
3304
+ </div>
3305
+
3306
+ {/* LOCAL chip */}
3307
+ <div
3308
+ style={{
3309
+ position: "absolute",
3310
+ top: 880,
3311
+ left: 100,
3312
+ width: 400,
3313
+ height: 300,
3314
+ ...glassBase,
3315
+ borderRadius: 24,
3316
+ padding: "32px 28px",
3317
+ transform: `scale(${localScale})`,
3318
+ display: "flex",
3319
+ flexDirection: "column",
3320
+ alignItems: "center",
3321
+ justifyContent: "center",
3322
+ gap: 18,
3323
+ border: `2px solid ${C.success}`,
3324
+ boxShadow: `${GLASS_SHADOW}, 0 0 28px ${C.success}55`,
3325
+ }}
3326
+ >
3327
+ <Img
3328
+ src={staticFile("captures/your-asset.png" /* REFERENCE-STRIP */)}
3329
+ style={{
3330
+ width: 110,
3331
+ height: 110,
3332
+ filter: `drop-shadow(0 0 12px ${C.success}aa)`,
3333
+ }}
3334
+ />
3335
+ <div
3336
+ style={{
3337
+ fontFamily: MONO,
3338
+ fontSize: 32,
3339
+ fontWeight: 700,
3340
+ color: C.success,
3341
+ letterSpacing: "0.18em",
3342
+ }}
3343
+ >
3344
+ LOCAL
3345
+ </div>
3346
+ <div
3347
+ style={{
3348
+ fontFamily: MONO,
3349
+ fontSize: 16,
3350
+ color: C.inkMuted,
3351
+ letterSpacing: "0.12em",
3352
+ }}
3353
+ >
3354
+ your machine
3355
+ </div>
3356
+ </div>
3357
+
3358
+ {/* $0 API chip */}
3359
+ <div
3360
+ style={{
3361
+ position: "absolute",
3362
+ top: 880,
3363
+ right: 100,
3364
+ width: 400,
3365
+ height: 300,
3366
+ ...glassBase,
3367
+ borderRadius: 24,
3368
+ padding: "32px 28px",
3369
+ transform: `scale(${noApiScale})`,
3370
+ display: "flex",
3371
+ flexDirection: "column",
3372
+ alignItems: "center",
3373
+ justifyContent: "center",
3374
+ gap: 18,
3375
+ border: `2px solid ${C.crimson}`,
3376
+ boxShadow: `${GLASS_SHADOW}, 0 0 28px ${C.crimson}55`,
3377
+ }}
3378
+ >
3379
+ <div style={{ position: "relative" }}>
3380
+ <div
3381
+ style={{
3382
+ fontFamily: MONO,
3383
+ fontSize: 96,
3384
+ fontWeight: 800,
3385
+ color: C.crimson,
3386
+ letterSpacing: "-0.04em",
3387
+ filter: `drop-shadow(0 0 12px ${C.crimson}66)`,
3388
+ }}
3389
+ >
3390
+ $0
3391
+ </div>
3392
+ {/* Strikethrough */}
3393
+ <div
3394
+ style={{
3395
+ position: "absolute",
3396
+ top: "50%",
3397
+ left: -8,
3398
+ right: -8,
3399
+ height: 4,
3400
+ background: C.crimson,
3401
+ transform: `translateY(-50%) scaleX(${interpolate(frame, [40, 60], [0, 1], {
3402
+ extrapolateLeft: "clamp",
3403
+ extrapolateRight: "clamp",
3404
+ })})`,
3405
+ transformOrigin: "left",
3406
+ boxShadow: `0 0 8px ${C.crimson}`,
3407
+ }}
3408
+ />
3409
+ </div>
3410
+ <div
3411
+ style={{
3412
+ fontFamily: MONO,
3413
+ fontSize: 24,
3414
+ fontWeight: 700,
3415
+ color: C.crimson,
3416
+ letterSpacing: "0.18em",
3417
+ }}
3418
+ >
3419
+ NO API
3420
+ </div>
3421
+ <div
3422
+ style={{
3423
+ fontFamily: MONO,
3424
+ fontSize: 16,
3425
+ color: C.inkMuted,
3426
+ letterSpacing: "0.12em",
3427
+ }}
3428
+ >
3429
+ no monthly bills
3430
+ </div>
3431
+ </div>
3432
+
3433
+ {/* Subline */}
3434
+ <div
3435
+ style={{
3436
+ position: "absolute",
3437
+ top: 1380,
3438
+ left: 60,
3439
+ right: 60,
3440
+ textAlign: "center",
3441
+ fontFamily: FONT,
3442
+ fontSize: 36,
3443
+ fontWeight: 600,
3444
+ color: C.inkSoft,
3445
+ letterSpacing: "-0.02em",
3446
+ opacity: interpolate(frame, [60, 90], [0, 1], {
3447
+ extrapolateLeft: "clamp",
3448
+ extrapolateRight: "clamp",
3449
+ }),
3450
+ }}
3451
+ >
3452
+ Frame-perfect. <span style={{ color: C.success, fontWeight: 700 }}>Wallet-perfect.</span>
3453
+ </div>
3454
+ </AbsoluteFill>
3455
+ );
3456
+ };
3457
+
3458
+ // ═══════════════════════════════════════════════════════════════
3459
+ // SCENE 13 — COMMENT CTA (frames 3053–3155, 3.43s)
3460
+ // "Comment AI to get this custom created Claude skill."
3461
+ // ═══════════════════════════════════════════════════════════════
3462
+ const CommentCtaScene: React.FC = () => {
3463
+ const frame = useCurrentFrame();
3464
+ const text = "AI";
3465
+ const typed = Math.min(text.length, Math.floor(frame / 9));
3466
+
3467
+ const sendPulse = interpolate(frame, [40, 50, 60], [1, 1.18, 1], {
3468
+ extrapolateLeft: "clamp",
3469
+ extrapolateRight: "clamp",
3470
+ });
3471
+ const sendGlow = interpolate(frame, [40, 50, 60, 80], [0, 1, 0.6, 0], {
3472
+ extrapolateLeft: "clamp",
3473
+ extrapolateRight: "clamp",
3474
+ });
3475
+
3476
+ // Engagement chips — Like / Follow / Comment AI
3477
+ const chipSpring = (delay: number) =>
3478
+ spring({
3479
+ frame: frame - delay,
3480
+ fps: 30,
3481
+ config: { damping: 11, stiffness: 150 },
3482
+ from: 0,
3483
+ to: 1,
3484
+ });
3485
+ const likeScale = chipSpring(18);
3486
+ const followScale = chipSpring(28);
3487
+ const heartBeat = 1 + 0.08 * ((Math.sin(frame * 0.22) + 1) / 2);
3488
+ const followBeat = 1 + 0.08 * ((Math.sin(frame * 0.22 + 2) + 1) / 2);
3489
+ // Floating heart particles emitted from the LIKE chip
3490
+ const hearts = Array.from({ length: 5 }).map((_, i) => {
3491
+ const cycle = (frame + i * 14) % 70;
3492
+ const t = cycle / 70;
3493
+ return {
3494
+ key: i,
3495
+ x: -10 + Math.sin((frame + i * 14) * 0.08) * 18,
3496
+ y: -t * 140,
3497
+ op: Math.sin(t * Math.PI),
3498
+ scale: 0.6 + t * 0.8,
3499
+ };
3500
+ });
3501
+
3502
+ return (
3503
+ <AbsoluteFill>
3504
+ <SonarRings accent={C.claude} secondary={C.film} />
3505
+ {frame > 50 && (
3506
+ <ParticleBurst count={36} palette={[C.claude, C.film, C.success]} />
3507
+ )}
3508
+
3509
+ <div style={{ position: "absolute", top: SAFE_TOP, left: 60 }}>
3510
+ <EyebrowPill dot={C.claude}>14 / GET IT</EyebrowPill>
3511
+ </div>
3512
+
3513
+ {/* Big "Comment AI" */}
3514
+ <div
3515
+ style={{
3516
+ position: "absolute",
3517
+ top: 540,
3518
+ left: 60,
3519
+ right: 60,
3520
+ textAlign: "center",
3521
+ fontFamily: FONT,
3522
+ fontWeight: 800,
3523
+ color: C.ink,
3524
+ letterSpacing: "-0.04em",
3525
+ lineHeight: 1.0,
3526
+ }}
3527
+ >
3528
+ <div style={{ fontSize: 96 }}>Comment</div>
3529
+ <div
3530
+ style={{
3531
+ fontSize: 200,
3532
+ color: C.claude,
3533
+ filter: `drop-shadow(0 0 24px ${C.claude}77)`,
3534
+ transform: `scale(${spring({
3535
+ frame: frame - 8,
3536
+ fps: 30,
3537
+ config: { damping: 9, stiffness: 130 },
3538
+ from: 0.4,
3539
+ to: 1,
3540
+ })})`,
3541
+ }}
3542
+ >
3543
+ AI
3544
+ </div>
3545
+ </div>
3546
+
3547
+ {/* ENGAGEMENT MOTION-ART — Like / Follow / Comment AI */}
3548
+ <div
3549
+ style={{
3550
+ position: "absolute",
3551
+ top: 880,
3552
+ left: 0,
3553
+ right: 0,
3554
+ display: "flex",
3555
+ justifyContent: "center",
3556
+ alignItems: "center",
3557
+ gap: 18,
3558
+ }}
3559
+ >
3560
+ {/* LIKE chip */}
3561
+ <div
3562
+ style={{
3563
+ position: "relative",
3564
+ transform: `scale(${likeScale * heartBeat})`,
3565
+ transformOrigin: "center",
3566
+ }}
3567
+ >
3568
+ <div
3569
+ style={{
3570
+ ...glassBase,
3571
+ background: C.glassFillStrong,
3572
+ borderRadius: 999,
3573
+ padding: "14px 22px 14px 16px",
3574
+ display: "flex",
3575
+ alignItems: "center",
3576
+ gap: 12,
3577
+ border: `2px solid ${C.crimson}77`,
3578
+ boxShadow: `${GLASS_SHADOW}, 0 0 24px ${C.crimson}55`,
3579
+ }}
3580
+ >
3581
+ <div
3582
+ style={{
3583
+ width: 44,
3584
+ height: 44,
3585
+ borderRadius: "50%",
3586
+ background: `linear-gradient(135deg, ${C.crimson}, #FF8FAE)`,
3587
+ display: "flex",
3588
+ alignItems: "center",
3589
+ justifyContent: "center",
3590
+ color: "white",
3591
+ fontSize: 24,
3592
+ fontWeight: 800,
3593
+ boxShadow: `0 0 14px ${C.crimson}88`,
3594
+ }}
3595
+ >
3596
+
3597
+ </div>
3598
+ <div
3599
+ style={{
3600
+ fontFamily: MONO,
3601
+ fontSize: 22,
3602
+ fontWeight: 700,
3603
+ color: C.crimson,
3604
+ letterSpacing: "0.18em",
3605
+ }}
3606
+ >
3607
+ LIKE
3608
+ </div>
3609
+ </div>
3610
+ {/* Floating hearts above the chip */}
3611
+ {hearts.map((h) => (
3612
+ <div
3613
+ key={h.key}
3614
+ style={{
3615
+ position: "absolute",
3616
+ top: -10,
3617
+ left: "50%",
3618
+ transform: `translate(${h.x}px, ${h.y}px) scale(${h.scale})`,
3619
+ color: C.crimson,
3620
+ fontSize: 22,
3621
+ opacity: h.op * 0.9,
3622
+ pointerEvents: "none",
3623
+ filter: `drop-shadow(0 0 6px ${C.crimson}aa)`,
3624
+ }}
3625
+ >
3626
+
3627
+ </div>
3628
+ ))}
3629
+ </div>
3630
+
3631
+ {/* FOLLOW chip */}
3632
+ <div
3633
+ style={{
3634
+ transform: `scale(${followScale * followBeat})`,
3635
+ transformOrigin: "center",
3636
+ ...glassBase,
3637
+ background: C.glassFillStrong,
3638
+ borderRadius: 999,
3639
+ padding: "14px 22px 14px 16px",
3640
+ display: "flex",
3641
+ alignItems: "center",
3642
+ gap: 12,
3643
+ border: `2px solid ${C.success}77`,
3644
+ boxShadow: `${GLASS_SHADOW}, 0 0 24px ${C.success}55`,
3645
+ }}
3646
+ >
3647
+ <div
3648
+ style={{
3649
+ width: 44,
3650
+ height: 44,
3651
+ borderRadius: "50%",
3652
+ background: `linear-gradient(135deg, ${C.success}, #5DEDB1)`,
3653
+ display: "flex",
3654
+ alignItems: "center",
3655
+ justifyContent: "center",
3656
+ color: "white",
3657
+ fontSize: 28,
3658
+ fontWeight: 800,
3659
+ lineHeight: 1,
3660
+ boxShadow: `0 0 14px ${C.success}88`,
3661
+ }}
3662
+ >
3663
+ +
3664
+ </div>
3665
+ <div
3666
+ style={{
3667
+ fontFamily: MONO,
3668
+ fontSize: 22,
3669
+ fontWeight: 700,
3670
+ color: C.success,
3671
+ letterSpacing: "0.18em",
3672
+ }}
3673
+ >
3674
+ FOLLOW
3675
+ </div>
3676
+ </div>
3677
+
3678
+ </div>
3679
+
3680
+ {/* Comment box mockup */}
3681
+ <div
3682
+ style={{
3683
+ position: "absolute",
3684
+ top: 1080,
3685
+ left: 60,
3686
+ right: 60,
3687
+ ...glassBase,
3688
+ background: C.glassFillStrong,
3689
+ borderRadius: 999,
3690
+ padding: "20px 28px",
3691
+ display: "flex",
3692
+ alignItems: "center",
3693
+ gap: 16,
3694
+ fontFamily: FONT,
3695
+ fontSize: 32,
3696
+ }}
3697
+ >
3698
+ <div
3699
+ style={{
3700
+ width: 56,
3701
+ height: 56,
3702
+ borderRadius: "50%",
3703
+ background: `linear-gradient(135deg, ${C.claude}, ${C.film})`,
3704
+ flexShrink: 0,
3705
+ }}
3706
+ />
3707
+ <div style={{ flex: 1, color: C.ink, fontWeight: 600 }}>
3708
+ {text.slice(0, typed)}
3709
+ {Math.floor(frame / 12) % 2 === 0 && typed < text.length && (
3710
+ <span
3711
+ style={{
3712
+ display: "inline-block",
3713
+ width: 4,
3714
+ height: 32,
3715
+ background: C.ink,
3716
+ marginLeft: 4,
3717
+ verticalAlign: "middle",
3718
+ }}
3719
+ />
3720
+ )}
3721
+ </div>
3722
+ <div
3723
+ style={{
3724
+ width: 64,
3725
+ height: 64,
3726
+ borderRadius: "50%",
3727
+ background: C.claude,
3728
+ display: "flex",
3729
+ alignItems: "center",
3730
+ justifyContent: "center",
3731
+ color: "white",
3732
+ fontFamily: MONO,
3733
+ fontSize: 28,
3734
+ transform: `scale(${sendPulse})`,
3735
+ boxShadow: `0 0 ${sendGlow * 32}px ${C.claude}`,
3736
+ }}
3737
+ >
3738
+
3739
+ </div>
3740
+ </div>
3741
+
3742
+ {/* CTA tail */}
3743
+ <div
3744
+ style={{
3745
+ position: "absolute",
3746
+ top: 1280,
3747
+ left: 60,
3748
+ right: 60,
3749
+ textAlign: "center",
3750
+ fontFamily: MONO,
3751
+ fontSize: 24,
3752
+ color: C.inkMuted,
3753
+ letterSpacing: "0.18em",
3754
+ textTransform: "uppercase",
3755
+ opacity: interpolate(frame, [40, 70], [0, 1], {
3756
+ extrapolateLeft: "clamp",
3757
+ extrapolateRight: "clamp",
3758
+ }),
3759
+ }}
3760
+ >
3761
+ get the custom claude skill
3762
+ </div>
3763
+ </AbsoluteFill>
3764
+ );
3765
+ };
3766
+
3767
+ // ═══════════════════════════════════════════════════════════════
3768
+ // MAIN COMPOSITION
3769
+ // ═══════════════════════════════════════════════════════════════
3770
+ export const ClaudeWatchReel: React.FC<{ embedded?: boolean }> = ({
3771
+ embedded = false,
3772
+ }) => {
3773
+ return (
3774
+ <AbsoluteFill
3775
+ style={{
3776
+ background: embedded ? "transparent" : C.bg,
3777
+ fontFamily: FONT,
3778
+ }}
3779
+ >
3780
+ {!embedded && {/* REFERENCE-STRIP: <Audio> tag removed — bring your own voiceover */}}
3781
+
3782
+ {/* Perpetual atmosphere — runs all scenes (skipped when embedded; host provides bg+caustics) */}
3783
+ {!embedded && <CausticBlobs />}
3784
+ <FloatingGlyphs />
3785
+
3786
+ <Sequence from={CW.hook} durationInFrames={CW.viral - CW.hook}>
3787
+ <HookScene />
3788
+ </Sequence>
3789
+ <Sequence from={CW.viral} durationInFrames={CW.demo - CW.viral}>
3790
+ <ViralScene />
3791
+ </Sequence>
3792
+ <Sequence from={CW.demo} durationInFrames={CW.result - CW.demo}>
3793
+ <DemoScene />
3794
+ </Sequence>
3795
+ <Sequence from={CW.result} durationInFrames={CW.decode - CW.result}>
3796
+ <ResultScene />
3797
+ </Sequence>
3798
+ <Sequence from={CW.decode} durationInFrames={CW.brain - CW.decode}>
3799
+ <DecodeScene />
3800
+ </Sequence>
3801
+ <Sequence from={CW.brain} durationInFrames={CW.ytCta - CW.brain}>
3802
+ <BrainScene />
3803
+ </Sequence>
3804
+ <Sequence from={CW.ytCta} durationInFrames={CW.hood - CW.ytCta}>
3805
+ <YtMidCtaScene />
3806
+ </Sequence>
3807
+ <Sequence from={CW.hood} durationInFrames={CW.ytdlp - CW.hood}>
3808
+ <HoodScene />
3809
+ </Sequence>
3810
+ <Sequence from={CW.ytdlp} durationInFrames={CW.ffmpeg - CW.ytdlp}>
3811
+ <YtDlpScene />
3812
+ </Sequence>
3813
+ <Sequence from={CW.ffmpeg} durationInFrames={CW.flipbook - CW.ffmpeg}>
3814
+ <FfmpegScene />
3815
+ </Sequence>
3816
+ <Sequence from={CW.flipbook} durationInFrames={CW.local - CW.flipbook}>
3817
+ <FlipbookScene />
3818
+ </Sequence>
3819
+ <Sequence from={CW.local} durationInFrames={CW.comment - CW.local}>
3820
+ <LocalScene />
3821
+ </Sequence>
3822
+ <Sequence from={CW.comment} durationInFrames={CW.end - CW.comment}>
3823
+ <CommentCtaScene />
3824
+ </Sequence>
3825
+ </AbsoluteFill>
3826
+ );
3827
+ };