@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,883 @@
1
+ /**
2
+ * REFERENCE — DesignReel (Family: paper)
3
+ *
4
+ * Canonical example of the paper 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/DesignReel.tsx
15
+ * Bundled at: 2026-05-08T18:50:39.512Z
16
+ */
17
+ import {
18
+ useCurrentFrame,
19
+ useVideoConfig,
20
+ interpolate,
21
+ spring,
22
+ Easing,
23
+ AbsoluteFill,
24
+ Img,
25
+ staticFile,
26
+ } from "remotion";
27
+ import { Audio } from "@remotion/media";
28
+ import { ds } from "./designSystem";
29
+
30
+ // ═══════════════════════════════════════════════════════════════
31
+ // CONSTANTS — matches JustDropReel (cream paper + terracotta)
32
+ // ═══════════════════════════════════════════════════════════════
33
+
34
+ const FONT = ds.font.sans;
35
+ const MONO = ds.font.mono;
36
+ const G = ds.card3d.green;
37
+
38
+ const T = {
39
+ heading: "#1a1a1a",
40
+ body: "#2d2d2d",
41
+ muted: "#6b6b6b",
42
+ accent: ds.color.claude, // #D4663A
43
+ success: "#1e7a45",
44
+ } as const;
45
+
46
+ // 10s total = 300 frames @ 30fps
47
+ // Beats compressed for a fast teaser cut
48
+ const SEGS = [
49
+ { s: 0, e: 4.0 }, // Hook: "Describe it. Get it."
50
+ { s: 3.7, e: 7.0 }, // Tagline: "Design at the speed of thought"
51
+ { s: 6.7, e: 10 }, // CTA: "Drop 'Design' in comments"
52
+ ] as const;
53
+
54
+ // ═══════════════════════════════════════════════════════════════
55
+ // UTILITIES
56
+ // ═══════════════════════════════════════════════════════════════
57
+
58
+ const so = (
59
+ frame: number,
60
+ fps: number,
61
+ startS: number,
62
+ endS: number,
63
+ fadeIn = 0.35,
64
+ fadeOut = 0.35,
65
+ ) =>
66
+ interpolate(
67
+ frame,
68
+ [startS * fps, (startS + fadeIn) * fps, (endS - fadeOut) * fps, endS * fps],
69
+ [0, 1, 1, 0],
70
+ { extrapolateLeft: "clamp", extrapolateRight: "clamp" },
71
+ );
72
+
73
+ const lf = (frame: number, fps: number, startSec: number) =>
74
+ Math.max(0, frame - startSec * fps);
75
+
76
+ interface SP {
77
+ frame: number;
78
+ fps: number;
79
+ }
80
+
81
+ // ═══════════════════════════════════════════════════════════════
82
+ // BACKGROUND — warm cream paper (matches JustDropReel)
83
+ // ═══════════════════════════════════════════════════════════════
84
+
85
+ const Background: React.FC<{ frame: number }> = ({ frame }) => {
86
+ const paperBase = "#e8e4de";
87
+ const paperLight = "#edeae4";
88
+ const gridLine = "rgba(180,175,165,0.25)";
89
+ const vignetteOp = 0.12 + Math.sin(frame * 0.006) * 0.03;
90
+
91
+ return (
92
+ <AbsoluteFill>
93
+ <div
94
+ style={{
95
+ width: "100%",
96
+ height: "100%",
97
+ background: `radial-gradient(ellipse at 50% 40%, ${paperLight} 0%, ${paperBase} 55%, #d9d4cc 100%)`,
98
+ }}
99
+ />
100
+ <div
101
+ style={{
102
+ position: "absolute",
103
+ inset: 0,
104
+ backgroundImage: `
105
+ linear-gradient(${gridLine} 1px, transparent 1px),
106
+ linear-gradient(90deg, ${gridLine} 1px, transparent 1px)
107
+ `,
108
+ backgroundSize: "48px 48px",
109
+ }}
110
+ />
111
+ <div
112
+ style={{
113
+ position: "absolute",
114
+ inset: 0,
115
+ background: `radial-gradient(ellipse at 50% 45%, rgba(255,255,255,0.3) 0%, transparent 60%)`,
116
+ }}
117
+ />
118
+ <div
119
+ style={{
120
+ position: "absolute",
121
+ inset: 0,
122
+ background: `radial-gradient(ellipse at 50% 50%, transparent 50%, rgba(0,0,0,${vignetteOp}) 100%)`,
123
+ }}
124
+ />
125
+ </AbsoluteFill>
126
+ );
127
+ };
128
+
129
+ // ═══════════════════════════════════════════════════════════════
130
+ // 3D FLOATING CARD (same pattern as JustDrop)
131
+ // ═══════════════════════════════════════════════════════════════
132
+
133
+ const FloatingCard: React.FC<{
134
+ children: React.ReactNode;
135
+ width?: number | string;
136
+ rotateX?: number;
137
+ rotateY?: number;
138
+ scale?: number;
139
+ style?: React.CSSProperties;
140
+ }> = ({ children, width = "85%", rotateX = 3, rotateY = -1, scale = 1, style }) => (
141
+ <div
142
+ style={{
143
+ width,
144
+ maxWidth: 900,
145
+ perspective: 1200,
146
+ ...style,
147
+ }}
148
+ >
149
+ <div
150
+ style={{
151
+ ...ds.card3d.cardStyle,
152
+ transform: `rotateX(${rotateX}deg) rotateY(${rotateY}deg) scale(${scale})`,
153
+ transformStyle: "preserve-3d",
154
+ padding: "26px 30px",
155
+ position: "relative",
156
+ overflow: "hidden",
157
+ }}
158
+ >
159
+ <div
160
+ style={{
161
+ position: "absolute",
162
+ top: 0,
163
+ left: 20,
164
+ right: 20,
165
+ height: 1,
166
+ background:
167
+ "linear-gradient(90deg, transparent, rgba(255,255,255,0.12), transparent)",
168
+ }}
169
+ />
170
+ {children}
171
+ </div>
172
+ </div>
173
+ );
174
+
175
+ // ═══════════════════════════════════════════════════════════════
176
+ // ICONS
177
+ // ═══════════════════════════════════════════════════════════════
178
+
179
+ const Icon = {
180
+ Sparkle: ({ color = "#fff", size = 28 }) => (
181
+ <svg width={size} height={size} viewBox="0 0 24 24" fill="none">
182
+ <path d="M12 3l2 6 6 2-6 2-2 6-2-6-6-2 6-2 2-6z" fill={color} />
183
+ </svg>
184
+ ),
185
+ ArrowDown: ({ color = "#fff", size = 32 }) => (
186
+ <svg width={size} height={size} viewBox="0 0 24 24" fill="none">
187
+ <path
188
+ d="M12 5v14M6 13l6 6 6-6"
189
+ stroke={color}
190
+ strokeWidth="2.2"
191
+ strokeLinecap="round"
192
+ strokeLinejoin="round"
193
+ />
194
+ </svg>
195
+ ),
196
+ Chat: ({ color = "#fff", size = 28 }) => (
197
+ <svg width={size} height={size} viewBox="0 0 24 24" fill="none">
198
+ <path
199
+ d="M3 6a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2h-8l-5 4v-4H5a2 2 0 0 1-2-2V6z"
200
+ stroke={color}
201
+ strokeWidth="1.8"
202
+ strokeLinejoin="round"
203
+ />
204
+ </svg>
205
+ ),
206
+ Bolt: ({ color = "#fff", size = 28 }) => (
207
+ <svg width={size} height={size} viewBox="0 0 24 24" fill="none">
208
+ <path
209
+ d="M13 2L4 14h7l-1 8 9-12h-7l1-8z"
210
+ fill={color}
211
+ stroke={color}
212
+ strokeWidth="1.2"
213
+ strokeLinejoin="round"
214
+ />
215
+ </svg>
216
+ ),
217
+ };
218
+
219
+ // ═══════════════════════════════════════════════════════════════
220
+ // FLOATING ORBIT RING
221
+ // ═══════════════════════════════════════════════════════════════
222
+
223
+ const OrbitRing: React.FC<{
224
+ size: number;
225
+ rotation: number;
226
+ tilt: number;
227
+ color?: string;
228
+ }> = ({ size, rotation, tilt, color = "rgba(212,102,58,0.4)" }) => (
229
+ <div
230
+ style={{
231
+ width: size,
232
+ height: size,
233
+ borderRadius: "50%",
234
+ border: `1.5px solid ${color}`,
235
+ transform: `rotateX(${tilt}deg) rotateZ(${rotation}deg)`,
236
+ transformStyle: "preserve-3d",
237
+ }}
238
+ />
239
+ );
240
+
241
+ // ═══════════════════════════════════════════════════════════════
242
+ // SCENE 1: HOOK — "Describe it. Get it."
243
+ // ═══════════════════════════════════════════════════════════════
244
+
245
+ const HookScene: React.FC<SP> = ({ frame, fps }) => {
246
+ const op = so(frame, fps, SEGS[0].s, SEGS[0].e, 0.3, 0.4);
247
+ if (op === 0) return null;
248
+ const f = lf(frame, fps, SEGS[0].s);
249
+
250
+ const badgeSpring = spring({
251
+ frame: f,
252
+ fps,
253
+ delay: Math.round(0.15 * fps),
254
+ config: ds.spring.bouncy,
255
+ });
256
+
257
+ const word1Spring = spring({
258
+ frame: f,
259
+ fps,
260
+ delay: Math.round(0.6 * fps),
261
+ config: { damping: 11, stiffness: 100 },
262
+ });
263
+
264
+ const word2Spring = spring({
265
+ frame: f,
266
+ fps,
267
+ delay: Math.round(2.0 * fps),
268
+ config: { damping: 11, stiffness: 100 },
269
+ });
270
+
271
+ const sparklePulse = 1 + Math.sin((f / fps) * 4.5) * 0.08;
272
+ const ringRot = (f / fps) * 70;
273
+
274
+ return (
275
+ <AbsoluteFill style={{ opacity: op }}>
276
+ {/* Orbit rings */}
277
+ <div
278
+ style={{
279
+ position: "absolute",
280
+ top: "30%",
281
+ left: "50%",
282
+ transform: "translate(-50%, 0)",
283
+ opacity: interpolate(badgeSpring, [0, 1], [0, 0.5]),
284
+ }}
285
+ >
286
+ <div style={{ position: "absolute", top: -120, left: -200 }}>
287
+ <OrbitRing size={400} rotation={ringRot} tilt={72} />
288
+ </div>
289
+ <div style={{ position: "absolute", top: -90, left: -170 }}>
290
+ <OrbitRing
291
+ size={340}
292
+ rotation={-ringRot * 0.6}
293
+ tilt={68}
294
+ color="rgba(30,60,40,0.3)"
295
+ />
296
+ </div>
297
+ </div>
298
+
299
+ {/* Top pill */}
300
+ <div
301
+ style={{
302
+ position: "absolute",
303
+ top: "17%",
304
+ left: "50%",
305
+ transform: `translate(-50%, 0) scale(${interpolate(badgeSpring, [0, 1], [0.5, 1])})`,
306
+ opacity: interpolate(badgeSpring, [0, 0.3], [0, 1], {
307
+ extrapolateLeft: "clamp",
308
+ extrapolateRight: "clamp",
309
+ }),
310
+ ...ds.card3d.pillStyle,
311
+ display: "flex",
312
+ alignItems: "center",
313
+ gap: 10,
314
+ }}
315
+ >
316
+ <div
317
+ style={{
318
+ width: 8,
319
+ height: 8,
320
+ borderRadius: "50%",
321
+ background: ds.color.claude,
322
+ boxShadow: `0 0 10px ${ds.color.claude}`,
323
+ }}
324
+ />
325
+ <span
326
+ style={{
327
+ fontFamily: MONO,
328
+ fontSize: 15,
329
+ fontWeight: ds.text.medium,
330
+ color: G.text,
331
+ letterSpacing: 1.2,
332
+ }}
333
+ >
334
+ DESIGN, REIMAGINED
335
+ </span>
336
+ </div>
337
+
338
+ {/* Claude logo with glow + subtle rotation */}
339
+ <div
340
+ style={{
341
+ position: "absolute",
342
+ top: "25%",
343
+ left: "50%",
344
+ transform: `translate(-50%, 0) scale(${sparklePulse * interpolate(badgeSpring, [0, 1], [0.3, 1])})`,
345
+ opacity: interpolate(badgeSpring, [0.1, 1], [0, 1], {
346
+ extrapolateLeft: "clamp",
347
+ extrapolateRight: "clamp",
348
+ }),
349
+ perspective: 800,
350
+ }}
351
+ >
352
+ <div
353
+ style={{
354
+ width: 200,
355
+ height: 200,
356
+ borderRadius: "50%",
357
+ background: `radial-gradient(circle, rgba(212,102,58,0.4) 0%, transparent 65%)`,
358
+ filter: "blur(24px)",
359
+ position: "absolute",
360
+ top: -40,
361
+ left: -40,
362
+ }}
363
+ />
364
+ <div
365
+ style={{
366
+ transformStyle: "preserve-3d",
367
+ transform: `rotateY(${interpolate(f, [0, 4 * fps], [-20, 20])}deg) rotateX(-6deg)`,
368
+ }}
369
+ >
370
+ <Img
371
+ src={staticFile("captures/your-asset.png" /* REFERENCE-STRIP */)}
372
+ style={{
373
+ width: 120,
374
+ height: 120,
375
+ display: "block",
376
+ filter: "drop-shadow(0 18px 28px rgba(150,60,30,0.42))",
377
+ }}
378
+ />
379
+ </div>
380
+ </div>
381
+
382
+ {/* Two-word reveal */}
383
+ <div
384
+ style={{
385
+ position: "absolute",
386
+ top: "46%",
387
+ width: "100%",
388
+ textAlign: "center",
389
+ padding: "0 48px",
390
+ }}
391
+ >
392
+ <div
393
+ style={{
394
+ fontFamily: FONT,
395
+ fontSize: 84,
396
+ fontWeight: ds.text.bold,
397
+ color: T.heading,
398
+ lineHeight: 1.05,
399
+ letterSpacing: ds.text.tighter,
400
+ }}
401
+ >
402
+ <div
403
+ style={{
404
+ transform: `translateY(${interpolate(word1Spring, [0, 1], [40, 0])}px) scale(${interpolate(word1Spring, [0, 1], [0.85, 1])})`,
405
+ opacity: interpolate(word1Spring, [0, 0.3], [0, 1], {
406
+ extrapolateLeft: "clamp",
407
+ extrapolateRight: "clamp",
408
+ }),
409
+ }}
410
+ >
411
+ Describe it.
412
+ </div>
413
+ <div
414
+ style={{
415
+ marginTop: 8,
416
+ transform: `translateY(${interpolate(word2Spring, [0, 1], [40, 0])}px) scale(${interpolate(word2Spring, [0, 1], [0.85, 1])})`,
417
+ opacity: interpolate(word2Spring, [0, 0.3], [0, 1], {
418
+ extrapolateLeft: "clamp",
419
+ extrapolateRight: "clamp",
420
+ }),
421
+ color: ds.color.claude,
422
+ }}
423
+ >
424
+ Get it.
425
+ </div>
426
+ </div>
427
+ </div>
428
+
429
+ {/* Subtitle */}
430
+ <div
431
+ style={{
432
+ position: "absolute",
433
+ top: "72%",
434
+ width: "100%",
435
+ textAlign: "center",
436
+ padding: "0 60px",
437
+ opacity: interpolate(f, [2.8 * fps, 3.4 * fps], [0, 1], {
438
+ extrapolateLeft: "clamp",
439
+ extrapolateRight: "clamp",
440
+ }),
441
+ }}
442
+ >
443
+ <div
444
+ style={{
445
+ fontFamily: FONT,
446
+ fontSize: 26,
447
+ fontWeight: ds.text.regular,
448
+ color: T.muted,
449
+ lineHeight: 1.35,
450
+ }}
451
+ >
452
+ Just describe what's
453
+ <br />
454
+ <span style={{ color: T.heading, fontWeight: ds.text.semibold }}>
455
+ in your head.
456
+ </span>
457
+ </div>
458
+ </div>
459
+ </AbsoluteFill>
460
+ );
461
+ };
462
+
463
+ // ═══════════════════════════════════════════════════════════════
464
+ // SCENE 2: TAGLINE — "Design at the speed of thought"
465
+ // ═══════════════════════════════════════════════════════════════
466
+
467
+ const TaglineScene: React.FC<SP> = ({ frame, fps }) => {
468
+ const op = so(frame, fps, SEGS[1].s, SEGS[1].e, 0.3, 0.4);
469
+ if (op === 0) return null;
470
+ const f = lf(frame, fps, SEGS[1].s);
471
+
472
+ const labelOp = interpolate(f, [0.2 * fps, 0.7 * fps], [0, 1], {
473
+ extrapolateLeft: "clamp",
474
+ extrapolateRight: "clamp",
475
+ });
476
+
477
+ // Word-by-word cascade
478
+ const words = ["Design", "at the", "speed of"];
479
+ const accentDelay = 1.4;
480
+
481
+ const accentSpring = spring({
482
+ frame: f,
483
+ fps,
484
+ delay: Math.round(accentDelay * fps),
485
+ config: { damping: 9, stiffness: 110 },
486
+ });
487
+
488
+ const accentPulse = 1 + Math.sin((f / fps) * 3.2) * 0.03;
489
+ const boltPulse = 1 + Math.sin((f / fps) * 6) * 0.12;
490
+
491
+ return (
492
+ <AbsoluteFill style={{ opacity: op }}>
493
+ {/* Small label */}
494
+ <div
495
+ style={{
496
+ position: "absolute",
497
+ top: "18%",
498
+ width: "100%",
499
+ textAlign: "center",
500
+ opacity: labelOp,
501
+ transform: `translateY(${interpolate(labelOp, [0, 1], [-10, 0])}px)`,
502
+ }}
503
+ >
504
+ <div
505
+ style={{
506
+ display: "inline-flex",
507
+ alignItems: "center",
508
+ gap: 12,
509
+ fontFamily: MONO,
510
+ fontSize: 17,
511
+ fontWeight: ds.text.semibold,
512
+ color: T.muted,
513
+ letterSpacing: ds.text.wider,
514
+ textTransform: "uppercase",
515
+ }}
516
+ >
517
+ <span
518
+ style={{
519
+ display: "inline-flex",
520
+ transform: `scale(${boltPulse})`,
521
+ color: ds.color.claude,
522
+ }}
523
+ >
524
+ <Icon.Bolt color={ds.color.claude} size={22} />
525
+ </span>
526
+ Welcome to
527
+ </div>
528
+ </div>
529
+
530
+ {/* Main tagline — stacked big type */}
531
+ <div
532
+ style={{
533
+ position: "absolute",
534
+ top: "30%",
535
+ width: "100%",
536
+ textAlign: "center",
537
+ padding: "0 40px",
538
+ }}
539
+ >
540
+ <div
541
+ style={{
542
+ fontFamily: FONT,
543
+ fontSize: 90,
544
+ fontWeight: ds.text.bold,
545
+ color: T.heading,
546
+ lineHeight: 1.0,
547
+ letterSpacing: ds.text.tighter,
548
+ }}
549
+ >
550
+ {words.map((w, i) => {
551
+ const delay = Math.round((0.4 + i * 0.22) * fps);
552
+ const s = spring({
553
+ frame: f,
554
+ fps,
555
+ delay,
556
+ config: { damping: 12, stiffness: 105 },
557
+ });
558
+ return (
559
+ <div
560
+ key={w}
561
+ style={{
562
+ transform: `translateY(${interpolate(s, [0, 1], [60, 0])}px)`,
563
+ opacity: interpolate(s, [0, 0.3], [0, 1], {
564
+ extrapolateLeft: "clamp",
565
+ extrapolateRight: "clamp",
566
+ }),
567
+ marginBottom: 4,
568
+ }}
569
+ >
570
+ {w}
571
+ </div>
572
+ );
573
+ })}
574
+
575
+ {/* Accent word */}
576
+ <div
577
+ style={{
578
+ marginTop: 12,
579
+ transform: `translateY(${interpolate(accentSpring, [0, 1], [80, 0])}px) scale(${interpolate(accentSpring, [0, 1], [0.7, 1]) * accentPulse})`,
580
+ opacity: interpolate(accentSpring, [0, 0.3], [0, 1], {
581
+ extrapolateLeft: "clamp",
582
+ extrapolateRight: "clamp",
583
+ }),
584
+ color: ds.color.claude,
585
+ textShadow: `0 0 40px rgba(212,102,58,0.35)`,
586
+ fontSize: 110,
587
+ letterSpacing: "-0.055em",
588
+ }}
589
+ >
590
+ thought.
591
+ </div>
592
+ </div>
593
+ </div>
594
+
595
+ {/* Tiny footer */}
596
+ <div
597
+ style={{
598
+ position: "absolute",
599
+ bottom: "23%",
600
+ width: "100%",
601
+ textAlign: "center",
602
+ opacity: interpolate(f, [2.4 * fps, 3.0 * fps], [0, 1], {
603
+ extrapolateLeft: "clamp",
604
+ extrapolateRight: "clamp",
605
+ }),
606
+ }}
607
+ >
608
+ <div
609
+ style={{
610
+ fontFamily: FONT,
611
+ fontSize: 22,
612
+ fontWeight: ds.text.medium,
613
+ color: T.muted,
614
+ letterSpacing: 1.5,
615
+ }}
616
+ >
617
+ No tools. No friction. Just flow.
618
+ </div>
619
+ </div>
620
+ </AbsoluteFill>
621
+ );
622
+ };
623
+
624
+ // ═══════════════════════════════════════════════════════════════
625
+ // SCENE 3: CTA — "Drop 'Design' in the comments"
626
+ // ═══════════════════════════════════════════════════════════════
627
+
628
+ const CTAScene: React.FC<SP> = ({ frame, fps }) => {
629
+ const op = so(frame, fps, SEGS[2].s, SEGS[2].e, 0.35, 0.5);
630
+ if (op === 0) return null;
631
+ const f = lf(frame, fps, SEGS[2].s);
632
+
633
+ const mainSpring = spring({
634
+ frame: f,
635
+ fps,
636
+ delay: Math.round(0.15 * fps),
637
+ config: { damping: 10, stiffness: 95 },
638
+ });
639
+
640
+ const bubbleSpring = spring({
641
+ frame: f,
642
+ fps,
643
+ delay: Math.round(1.1 * fps),
644
+ config: { damping: 11, stiffness: 100 },
645
+ });
646
+
647
+ // "Design" typed into comment
648
+ const typed = "Design";
649
+ const chars = Math.floor(
650
+ interpolate(f, [1.7 * fps, 2.5 * fps], [0, typed.length], {
651
+ extrapolateLeft: "clamp",
652
+ extrapolateRight: "clamp",
653
+ }),
654
+ );
655
+
656
+ const arrowY = Math.sin((f / fps) * 3.5) * 10;
657
+ const hintOp = interpolate(f, [2.2 * fps, 2.8 * fps], [0, 1], {
658
+ extrapolateLeft: "clamp",
659
+ extrapolateRight: "clamp",
660
+ });
661
+
662
+ return (
663
+ <AbsoluteFill style={{ opacity: op }}>
664
+ {/* Headline */}
665
+ <div
666
+ style={{
667
+ position: "absolute",
668
+ top: "24%",
669
+ width: "100%",
670
+ textAlign: "center",
671
+ padding: "0 44px",
672
+ transform: `scale(${interpolate(mainSpring, [0, 1], [0.88, 1])})`,
673
+ opacity: interpolate(mainSpring, [0, 0.2], [0, 1], {
674
+ extrapolateLeft: "clamp",
675
+ extrapolateRight: "clamp",
676
+ }),
677
+ }}
678
+ >
679
+ <div
680
+ style={{
681
+ fontFamily: FONT,
682
+ fontSize: 24,
683
+ fontWeight: ds.text.medium,
684
+ color: T.muted,
685
+ letterSpacing: ds.text.wider,
686
+ textTransform: "uppercase",
687
+ marginBottom: 14,
688
+ }}
689
+ >
690
+ Want the setup guide?
691
+ </div>
692
+ <div
693
+ style={{
694
+ fontFamily: FONT,
695
+ fontSize: 62,
696
+ fontWeight: ds.text.bold,
697
+ color: T.heading,
698
+ letterSpacing: ds.text.tighter,
699
+ lineHeight: 1.05,
700
+ }}
701
+ >
702
+ Drop{" "}
703
+ <span
704
+ style={{
705
+ color: ds.color.claude,
706
+ textShadow: "0 0 30px rgba(212,102,58,0.35)",
707
+ }}
708
+ >
709
+ "Design"
710
+ </span>
711
+ <br />
712
+ in the comments
713
+ </div>
714
+ </div>
715
+
716
+ {/* Comment bubble mockup */}
717
+ <div
718
+ style={{
719
+ position: "absolute",
720
+ top: "54%",
721
+ left: "50%",
722
+ transform: `translate(-50%, 0) scale(${interpolate(bubbleSpring, [0, 1], [0.6, 1])})`,
723
+ opacity: interpolate(bubbleSpring, [0, 0.3], [0, 1], {
724
+ extrapolateLeft: "clamp",
725
+ extrapolateRight: "clamp",
726
+ }),
727
+ width: "82%",
728
+ maxWidth: 740,
729
+ perspective: 1000,
730
+ }}
731
+ >
732
+ <FloatingCard rotateX={4} rotateY={-2}>
733
+ <div
734
+ style={{
735
+ display: "flex",
736
+ alignItems: "center",
737
+ gap: 16,
738
+ }}
739
+ >
740
+ {/* Claude logo avatar */}
741
+ <div
742
+ style={{
743
+ width: 56,
744
+ height: 56,
745
+ borderRadius: "50%",
746
+ display: "flex",
747
+ alignItems: "center",
748
+ justifyContent: "center",
749
+ flexShrink: 0,
750
+ background: "rgba(212,102,58,0.18)",
751
+ border: "1px solid rgba(212,102,58,0.35)",
752
+ padding: 6,
753
+ }}
754
+ >
755
+ <Img
756
+ src={staticFile("captures/your-asset.png" /* REFERENCE-STRIP */)}
757
+ style={{ width: "100%", height: "100%", display: "block" }}
758
+ />
759
+ </div>
760
+
761
+ {/* Comment text */}
762
+ <div style={{ flex: 1 }}>
763
+ <div
764
+ style={{
765
+ fontFamily: FONT,
766
+ fontSize: 13,
767
+ color: G.textMuted,
768
+ letterSpacing: 0.5,
769
+ }}
770
+ >
771
+ you · just now
772
+ </div>
773
+ <div
774
+ style={{
775
+ fontFamily: FONT,
776
+ fontSize: 30,
777
+ fontWeight: ds.text.semibold,
778
+ color: G.text,
779
+ marginTop: 2,
780
+ letterSpacing: -0.3,
781
+ display: "flex",
782
+ alignItems: "center",
783
+ }}
784
+ >
785
+ {typed.slice(0, chars)}
786
+ {chars < typed.length && (
787
+ <span
788
+ style={{
789
+ opacity: Math.sin(f * 0.4) > 0 ? 1 : 0,
790
+ color: ds.color.claude,
791
+ marginLeft: 2,
792
+ }}
793
+ >
794
+ |
795
+ </span>
796
+ )}
797
+ </div>
798
+ </div>
799
+
800
+ {/* Send button */}
801
+ <div
802
+ style={{
803
+ width: 44,
804
+ height: 44,
805
+ borderRadius: 12,
806
+ background: "rgba(212,102,58,0.22)",
807
+ border: "1px solid rgba(212,102,58,0.45)",
808
+ display: "flex",
809
+ alignItems: "center",
810
+ justifyContent: "center",
811
+ flexShrink: 0,
812
+ }}
813
+ >
814
+ <svg width={20} height={20} viewBox="0 0 24 24" fill="none">
815
+ <path
816
+ d="M3 12l18-8-5 18-4-8-9-2z"
817
+ stroke={ds.color.claude}
818
+ strokeWidth="1.8"
819
+ strokeLinejoin="round"
820
+ />
821
+ </svg>
822
+ </div>
823
+ </div>
824
+ </FloatingCard>
825
+ </div>
826
+
827
+ {/* Bottom hint + arrow */}
828
+ <div
829
+ style={{
830
+ position: "absolute",
831
+ bottom: "24%",
832
+ width: "100%",
833
+ textAlign: "center",
834
+ opacity: hintOp,
835
+ }}
836
+ >
837
+ <div
838
+ style={{
839
+ display: "inline-flex",
840
+ flexDirection: "column",
841
+ alignItems: "center",
842
+ gap: 8,
843
+ }}
844
+ >
845
+ <div
846
+ style={{
847
+ fontFamily: FONT,
848
+ fontSize: 20,
849
+ fontWeight: ds.text.medium,
850
+ color: T.muted,
851
+ letterSpacing: 2,
852
+ textTransform: "uppercase",
853
+ }}
854
+ >
855
+ DM'd straight to you
856
+ </div>
857
+ <div style={{ transform: `translateY(${arrowY}px)` }}>
858
+ <Icon.ArrowDown color={ds.color.claude} size={36} />
859
+ </div>
860
+ </div>
861
+ </div>
862
+ </AbsoluteFill>
863
+ );
864
+ };
865
+
866
+ // ═══════════════════════════════════════════════════════════════
867
+ // MAIN COMPOSITION — 10s @ 30fps = 300 frames
868
+ // ═══════════════════════════════════════════════════════════════
869
+
870
+ export const DesignReel: React.FC = () => {
871
+ const frame = useCurrentFrame();
872
+ const { fps } = useVideoConfig();
873
+
874
+ return (
875
+ <AbsoluteFill style={{ backgroundColor: "#e8e4de", fontFamily: FONT }}>
876
+ {/* REFERENCE-STRIP: <Audio> tag removed — bring your own voiceover */}
877
+ <Background frame={frame} />
878
+ <HookScene frame={frame} fps={fps} />
879
+ <TaglineScene frame={frame} fps={fps} />
880
+ <CTAScene frame={frame} fps={fps} />
881
+ </AbsoluteFill>
882
+ );
883
+ };