@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,3413 @@
1
+ /**
2
+ * REFERENCE — HuashuReel (Family: warm)
3
+ *
4
+ * Canonical example of the warm 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/HuashuReel.tsx
15
+ * Bundled at: 2026-05-08T18:50:39.536Z
16
+ */
17
+ import React from "react";
18
+ import {
19
+ AbsoluteFill,
20
+ Audio,
21
+ Easing,
22
+ OffthreadVideo,
23
+ Sequence,
24
+ interpolate,
25
+ spring,
26
+ staticFile,
27
+ useCurrentFrame,
28
+ useVideoConfig,
29
+ } from "remotion";
30
+ import { ds } from "./designSystem";
31
+
32
+ // ═══════════════════════════════════════════════════════════════════════════
33
+ // CONSTANTS — 9:16 reel · 30fps · 99.44s voiceover (2984 frames)
34
+ // ═══════════════════════════════════════════════════════════════════════════
35
+
36
+ const W = 1080;
37
+ const H = 1920;
38
+
39
+ // IG safe zones per project memory: top ~280, bottom ~410 covered by chrome.
40
+ const SAFE_TOP = 280;
41
+ const SAFE_BOT_Y = 1510;
42
+ const SAFE_L = 60;
43
+ const SAFE_R = 60;
44
+
45
+ export const HUASHU_TOTAL = 2984;
46
+
47
+ const SCENES = {
48
+ S1: { from: 0, dur: 150 }, // 5.0s — Hook: "Never hit Claude Design limits again"
49
+ S2: { from: 150, dur: 180 }, // 6.0s — Name reveal + just-dropped on GitHub
50
+ S3: { from: 330, dur: 270 }, // 9.0s — Drops into ANY agent (Claude Code / Cursor / Codex)
51
+ S4: { from: 600, dur: 210 }, // 7.0s — Install command (one-liner)
52
+ S5: { from: 810, dur: 180 }, // 6.0s — Capabilities bento (prototypes / decks / motion / infographics)
53
+ S6: { from: 990, dur: 330 }, // 11.0s — Under the hood + claude-design.mov clip
54
+ S7: { from: 1320, dur: 360 }, // 12.0s — Variants + Tweaks panel
55
+ S8: { from: 1680, dur: 390 }, // 13.0s — Payoff: no separate cap, no design quota
56
+ S9: { from: 2070, dur: 360 }, // 12.0s — Honest take: GUI vs Terminal
57
+ S10: { from: 2430, dur: 300 }, // 10.0s — License: personal use only
58
+ S11: { from: 2730, dur: 254 }, // 8.47s — CTA: comment "AI"
59
+ } as const;
60
+
61
+ // ═══════════════════════════════════════════════════════════════════════════
62
+ // COLOR — zinc neutral base + warm amber (Huashu signature) + claude-warm
63
+ // One accent rule. Emerald only for "no quota" payoff. Red only for the wall.
64
+ // ═══════════════════════════════════════════════════════════════════════════
65
+
66
+ const C = {
67
+ bg: "#09090b",
68
+ bgLift: "#111114",
69
+ surface: "#15151c",
70
+ surfaceWarm: "#1a1610",
71
+ border: "rgba(255,255,255,0.08)",
72
+ borderLoud: "rgba(255,255,255,0.16)",
73
+ fg: "#fafafa",
74
+ fgSoft: "#d4d4d8",
75
+ fgMuted: "#9f9fa9",
76
+ fgDim: "#71717b",
77
+
78
+ // Huashu warm signature
79
+ amber: "#f99c00",
80
+ amberSoft: "#fcbb00",
81
+ amberDeep: "#9a5e00",
82
+ amberGlow: "rgba(249,156,0,0.20)",
83
+
84
+ // Claude brand (used sparingly, for context only)
85
+ claudeWarm: "#d4663a",
86
+
87
+ // Payoff
88
+ emerald: "#00d294",
89
+ emeraldSoft: "#5ee9b5",
90
+ emeraldDeep: "#00875a",
91
+
92
+ // Wall / limit
93
+ red: "#ef4444",
94
+ redSoft: "#fca5a5",
95
+ redDeep: "#7f1d1d",
96
+
97
+ // Agent chip tints
98
+ cursor: "#000000",
99
+ vscode: "#0098ff",
100
+ openai: "#10a37f",
101
+ } as const;
102
+
103
+ const FONT = ds.font.sans;
104
+ const MONO = ds.font.mono;
105
+
106
+ // ═══════════════════════════════════════════════════════════════════════════
107
+ // MOTION HELPERS — GSAP-flavored timeline ergonomics on Remotion springs
108
+ // ═══════════════════════════════════════════════════════════════════════════
109
+
110
+ type SpringKind = "smooth" | "snappy" | "gentle" | "bouncy" | "glass";
111
+
112
+ const sp = (
113
+ frame: number,
114
+ fps: number,
115
+ delaySec: number,
116
+ kind: SpringKind = "snappy",
117
+ ) =>
118
+ spring({ frame, fps, delay: Math.round(delaySec * fps), config: ds.spring[kind] });
119
+
120
+ const fadeIn = (f: number, at: number, len = 12) =>
121
+ interpolate(f, [at, at + len], [0, 1], {
122
+ extrapolateLeft: "clamp",
123
+ extrapolateRight: "clamp",
124
+ });
125
+
126
+ const fadeOut = (f: number, at: number, len = 12) =>
127
+ interpolate(f, [at, at + len], [1, 0], {
128
+ extrapolateLeft: "clamp",
129
+ extrapolateRight: "clamp",
130
+ });
131
+
132
+ // power3.out feel
133
+ const expoOut = Easing.bezier(0.16, 1, 0.3, 1);
134
+ const power2Out = Easing.bezier(0.4, 0, 0.2, 1);
135
+
136
+ // gsap.fromTo equivalent
137
+ const tween = (
138
+ f: number,
139
+ startFrame: number,
140
+ durFrames: number,
141
+ from: number,
142
+ to: number,
143
+ easing: ReturnType<typeof Easing.bezier> = expoOut,
144
+ ) =>
145
+ interpolate(f, [startFrame, startFrame + durFrames], [from, to], {
146
+ extrapolateLeft: "clamp",
147
+ extrapolateRight: "clamp",
148
+ easing,
149
+ });
150
+
151
+ // Letter cascade
152
+ const letterStagger = (
153
+ chars: string,
154
+ frame: number,
155
+ fps: number,
156
+ startSec: number,
157
+ perCharSec = 0.03,
158
+ ) =>
159
+ chars.split("").map((ch, i) => {
160
+ const s = sp(frame, fps, startSec + i * perCharSec, "snappy");
161
+ return {
162
+ ch,
163
+ opacity: interpolate(s, [0, 0.45], [0, 1], {
164
+ extrapolateLeft: "clamp",
165
+ extrapolateRight: "clamp",
166
+ }),
167
+ y: interpolate(s, [0, 1], [44, 0]),
168
+ scale: interpolate(s, [0, 0.6, 1], [0.86, 1.06, 1]),
169
+ };
170
+ });
171
+
172
+ // ═══════════════════════════════════════════════════════════════════════════
173
+ // BACKGROUND ATMOSPHERICS
174
+ // ═══════════════════════════════════════════════════════════════════════════
175
+
176
+ const NoiseGrain: React.FC<{ opacity?: number }> = ({ opacity = 0.06 }) => (
177
+ <AbsoluteFill
178
+ style={{
179
+ pointerEvents: "none",
180
+ opacity,
181
+ mixBlendMode: "overlay",
182
+ backgroundImage: `url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='200' height='200'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.9'/></filter><rect width='200' height='200' filter='url(%23n)' opacity='0.7'/></svg>")`,
183
+ }}
184
+ />
185
+ );
186
+
187
+ const Vignette: React.FC<{ frame: number; tone?: "dark" | "warm" }> = ({
188
+ frame,
189
+ tone = "dark",
190
+ }) => {
191
+ const pulse = 0.42 + Math.sin(frame * 0.012) * 0.05;
192
+ const col =
193
+ tone === "warm" ? `rgba(70,40,5,${pulse})` : `rgba(0,0,0,${pulse})`;
194
+ return (
195
+ <AbsoluteFill
196
+ style={{
197
+ background: `radial-gradient(ellipse at 50% 50%, transparent 38%, ${col} 100%)`,
198
+ }}
199
+ />
200
+ );
201
+ };
202
+
203
+ const DarkBg: React.FC<{
204
+ frame: number;
205
+ tint?: "amber" | "emerald" | "red" | "neutral";
206
+ }> = ({ frame, tint = "amber" }) => {
207
+ const drift = (Math.sin(frame * 0.0028) + 1) * 0.5;
208
+ const drift2 = (Math.cos(frame * 0.0021) + 1) * 0.5;
209
+ const tintRgb =
210
+ tint === "amber"
211
+ ? "249,156,0"
212
+ : tint === "emerald"
213
+ ? "0,210,148"
214
+ : tint === "red"
215
+ ? "239,68,68"
216
+ : "120,120,135";
217
+ return (
218
+ <AbsoluteFill>
219
+ <div style={{ width: "100%", height: "100%", background: C.bg }} />
220
+ <div
221
+ style={{
222
+ position: "absolute",
223
+ inset: 0,
224
+ background: `radial-gradient(ellipse 800px 1000px at ${20 + drift * 60}% ${15 + drift2 * 40}%, rgba(${tintRgb},0.12) 0%, transparent 60%)`,
225
+ }}
226
+ />
227
+ <div
228
+ style={{
229
+ position: "absolute",
230
+ inset: 0,
231
+ background: `radial-gradient(ellipse 700px 900px at ${85 - drift * 55}% ${78 + drift2 * 15}%, rgba(${tintRgb},0.06) 0%, transparent 55%)`,
232
+ }}
233
+ />
234
+ {/* fine grid */}
235
+ <div
236
+ style={{
237
+ position: "absolute",
238
+ inset: 0,
239
+ backgroundImage: `linear-gradient(rgba(255,255,255,0.035) 1px, transparent 1px), linear-gradient(90deg, rgba(255,255,255,0.035) 1px, transparent 1px)`,
240
+ backgroundSize: "72px 72px",
241
+ maskImage:
242
+ "radial-gradient(ellipse at 50% 50%, black 30%, transparent 85%)",
243
+ WebkitMaskImage:
244
+ "radial-gradient(ellipse at 50% 50%, black 30%, transparent 85%)",
245
+ }}
246
+ />
247
+ <NoiseGrain />
248
+ <Vignette frame={frame} tone={tint === "amber" ? "warm" : "dark"} />
249
+ </AbsoluteFill>
250
+ );
251
+ };
252
+
253
+ // ═══════════════════════════════════════════════════════════════════════════
254
+ // REUSABLE PRIMITIVES
255
+ // ═══════════════════════════════════════════════════════════════════════════
256
+
257
+ const Pill: React.FC<{
258
+ children: React.ReactNode;
259
+ bg: string;
260
+ fg: string;
261
+ border?: string;
262
+ dot?: string;
263
+ mono?: boolean;
264
+ size?: number;
265
+ }> = ({ children, bg, fg, border, dot, mono = true, size = 22 }) => (
266
+ <div
267
+ style={{
268
+ display: "inline-flex",
269
+ alignItems: "center",
270
+ gap: 12,
271
+ padding: "12px 22px",
272
+ borderRadius: 9999,
273
+ background: bg,
274
+ border: border ? `1px solid ${border}` : undefined,
275
+ color: fg,
276
+ fontFamily: mono ? MONO : FONT,
277
+ fontSize: size,
278
+ fontWeight: 600,
279
+ letterSpacing: "0.08em",
280
+ }}
281
+ >
282
+ {dot ? (
283
+ <span
284
+ style={{
285
+ width: 8,
286
+ height: 8,
287
+ borderRadius: 999,
288
+ background: dot,
289
+ boxShadow: `0 0 12px ${dot}`,
290
+ }}
291
+ />
292
+ ) : null}
293
+ {children}
294
+ </div>
295
+ );
296
+
297
+ // Animated character row — used for headline slams
298
+ const StaggerRow: React.FC<{
299
+ text: string;
300
+ startSec: number;
301
+ perCharSec?: number;
302
+ fontSize: number;
303
+ weight?: number;
304
+ letterSpacing?: string;
305
+ color?: string;
306
+ font?: string;
307
+ }> = ({
308
+ text,
309
+ startSec,
310
+ perCharSec = 0.03,
311
+ fontSize,
312
+ weight = 800,
313
+ letterSpacing = "-0.04em",
314
+ color = C.fg,
315
+ font = FONT,
316
+ }) => {
317
+ const frame = useCurrentFrame();
318
+ const { fps } = useVideoConfig();
319
+ const items = letterStagger(text, frame, fps, startSec, perCharSec);
320
+ return (
321
+ <div
322
+ style={{
323
+ display: "flex",
324
+ flexWrap: "wrap",
325
+ gap: 0,
326
+ fontFamily: font,
327
+ fontWeight: weight,
328
+ fontSize,
329
+ letterSpacing,
330
+ lineHeight: 1,
331
+ color,
332
+ }}
333
+ >
334
+ {items.map((it, i) => (
335
+ <span
336
+ key={i}
337
+ style={{
338
+ display: "inline-block",
339
+ opacity: it.opacity,
340
+ transform: `translateY(${it.y}px) scale(${it.scale})`,
341
+ whiteSpace: it.ch === " " ? "pre" : undefined,
342
+ }}
343
+ >
344
+ {it.ch}
345
+ </span>
346
+ ))}
347
+ </div>
348
+ );
349
+ };
350
+
351
+ // ═══════════════════════════════════════════════════════════════════════════
352
+ // SCENE 1 — HOOK · "Never hit Claude Design limits again"
353
+ // Visual: rate-limit wall slamming up; struck-through limits; open-source escape
354
+ // ═══════════════════════════════════════════════════════════════════════════
355
+
356
+ const Scene1: React.FC = () => {
357
+ const frame = useCurrentFrame();
358
+ const { fps } = useVideoConfig();
359
+
360
+ // top warning pill
361
+ const pillSpring = sp(frame, fps, 0, "snappy");
362
+ const pillY = interpolate(pillSpring, [0, 1], [-30, 0]);
363
+ const pillOp = interpolate(pillSpring, [0, 0.5], [0, 1], {
364
+ extrapolateLeft: "clamp",
365
+ extrapolateRight: "clamp",
366
+ });
367
+
368
+ // alert dialog slides in
369
+ const dialogSpring = sp(frame, fps, 0.2, "snappy");
370
+ const dialogY = interpolate(dialogSpring, [0, 1], [60, 0]);
371
+ const dialogOp = interpolate(dialogSpring, [0, 0.5], [0, 1], {
372
+ extrapolateLeft: "clamp",
373
+ extrapolateRight: "clamp",
374
+ });
375
+
376
+ // strike-through animation — sweeps left to right at frame 50
377
+ const strikeProgress = tween(frame, 50, 22, 0, 100);
378
+
379
+ // payoff line — "open source repo" slams in
380
+ const payoffSpring = sp(frame, fps, 2.6, "bouncy");
381
+ const payoffOp = interpolate(payoffSpring, [0, 0.4], [0, 1], {
382
+ extrapolateLeft: "clamp",
383
+ extrapolateRight: "clamp",
384
+ });
385
+ const payoffY = interpolate(payoffSpring, [0, 1], [40, 0]);
386
+ const payoffScale = interpolate(payoffSpring, [0, 0.6, 1], [0.85, 1.08, 1]);
387
+
388
+ // breathing red dot
389
+ const dotPulse = 0.6 + Math.sin(frame * 0.18) * 0.4;
390
+
391
+ const exit = fadeOut(frame, SCENES.S1.dur - 14, 12);
392
+
393
+ return (
394
+ <AbsoluteFill style={{ fontFamily: FONT, opacity: exit }}>
395
+ <DarkBg frame={frame} tint="red" />
396
+
397
+ {/* TOP — rate-limit context tag */}
398
+ <div
399
+ style={{
400
+ position: "absolute",
401
+ top: SAFE_TOP,
402
+ left: 0,
403
+ right: 0,
404
+ display: "flex",
405
+ justifyContent: "center",
406
+ opacity: pillOp,
407
+ transform: `translateY(${pillY}px)`,
408
+ }}
409
+ >
410
+ <Pill
411
+ bg="rgba(239,68,68,0.10)"
412
+ fg={C.redSoft}
413
+ border="rgba(239,68,68,0.35)"
414
+ dot={C.red}
415
+ >
416
+ RATE LIMITED · QUOTA HIT
417
+ </Pill>
418
+ </div>
419
+
420
+ {/* CENTER — fake Claude Design quota dialog */}
421
+ <div
422
+ style={{
423
+ position: "absolute",
424
+ left: SAFE_L,
425
+ right: SAFE_R,
426
+ top: 430,
427
+ opacity: dialogOp,
428
+ transform: `translateY(${dialogY}px)`,
429
+ }}
430
+ >
431
+ <div
432
+ style={{
433
+ background: C.surface,
434
+ border: `1px solid ${C.borderLoud}`,
435
+ borderRadius: 26,
436
+ overflow: "hidden",
437
+ boxShadow:
438
+ "0 30px 80px -20px rgba(0,0,0,0.7), inset 0 1px 0 rgba(255,255,255,0.08)",
439
+ }}
440
+ >
441
+ {/* title bar */}
442
+ <div
443
+ style={{
444
+ display: "flex",
445
+ alignItems: "center",
446
+ gap: 10,
447
+ padding: "16px 22px",
448
+ background: "rgba(255,255,255,0.03)",
449
+ borderBottom: `1px solid ${C.border}`,
450
+ }}
451
+ >
452
+ <div
453
+ style={{
454
+ width: 12,
455
+ height: 12,
456
+ borderRadius: 999,
457
+ background: "#ff5f57",
458
+ opacity: 0.5 + dotPulse * 0.5,
459
+ }}
460
+ />
461
+ <div
462
+ style={{
463
+ width: 12,
464
+ height: 12,
465
+ borderRadius: 999,
466
+ background: "#febc2e",
467
+ }}
468
+ />
469
+ <div
470
+ style={{
471
+ width: 12,
472
+ height: 12,
473
+ borderRadius: 999,
474
+ background: "#28c840",
475
+ }}
476
+ />
477
+ <div
478
+ style={{
479
+ marginLeft: 18,
480
+ fontFamily: MONO,
481
+ fontSize: 18,
482
+ color: C.fgMuted,
483
+ letterSpacing: "0.05em",
484
+ }}
485
+ >
486
+ claude.ai/design
487
+ </div>
488
+ </div>
489
+
490
+ {/* body */}
491
+ <div
492
+ style={{
493
+ padding: "44px 36px",
494
+ fontFamily: FONT,
495
+ color: C.fgSoft,
496
+ }}
497
+ >
498
+ <div
499
+ style={{
500
+ display: "flex",
501
+ alignItems: "center",
502
+ gap: 14,
503
+ marginBottom: 20,
504
+ }}
505
+ >
506
+ <div
507
+ style={{
508
+ width: 14,
509
+ height: 14,
510
+ borderRadius: 999,
511
+ background: C.red,
512
+ boxShadow: `0 0 ${10 + dotPulse * 14}px ${C.red}`,
513
+ }}
514
+ />
515
+ <div
516
+ style={{
517
+ fontFamily: MONO,
518
+ fontSize: 18,
519
+ letterSpacing: "0.12em",
520
+ color: C.redSoft,
521
+ fontWeight: 600,
522
+ }}
523
+ >
524
+ WEEKLY LIMIT REACHED
525
+ </div>
526
+ </div>
527
+
528
+ <div
529
+ style={{
530
+ position: "relative",
531
+ fontSize: 38,
532
+ fontWeight: 600,
533
+ color: C.fg,
534
+ lineHeight: 1.18,
535
+ letterSpacing: "-0.015em",
536
+ marginBottom: 18,
537
+ }}
538
+ >
539
+ You&apos;ve hit your Claude Design quota.
540
+ {/* strike-through bar */}
541
+ <div
542
+ style={{
543
+ position: "absolute",
544
+ top: "55%",
545
+ left: 0,
546
+ height: 4,
547
+ width: `${strikeProgress}%`,
548
+ background: C.red,
549
+ boxShadow: `0 0 12px ${C.red}`,
550
+ }}
551
+ />
552
+ </div>
553
+
554
+ <div
555
+ style={{
556
+ fontFamily: MONO,
557
+ fontSize: 20,
558
+ color: C.fgDim,
559
+ letterSpacing: "0.04em",
560
+ }}
561
+ >
562
+ Resets in 6 days · 14 hrs · 22 min
563
+ </div>
564
+ </div>
565
+ </div>
566
+ </div>
567
+
568
+ {/* PAYOFF — "fix it with one open source repo" */}
569
+ <div
570
+ style={{
571
+ position: "absolute",
572
+ left: SAFE_L,
573
+ right: SAFE_R,
574
+ top: 1080,
575
+ opacity: payoffOp,
576
+ transform: `translateY(${payoffY}px) scale(${payoffScale})`,
577
+ textAlign: "left",
578
+ }}
579
+ >
580
+ <div
581
+ style={{
582
+ fontFamily: MONO,
583
+ fontSize: 18,
584
+ letterSpacing: "0.18em",
585
+ color: C.amber,
586
+ fontWeight: 600,
587
+ marginBottom: 18,
588
+ }}
589
+ >
590
+ THE FIX ↓
591
+ </div>
592
+ <div
593
+ style={{
594
+ fontFamily: FONT,
595
+ fontSize: 88,
596
+ fontWeight: 800,
597
+ letterSpacing: "-0.045em",
598
+ lineHeight: 0.96,
599
+ color: C.fg,
600
+ }}
601
+ >
602
+ One{" "}
603
+ <span
604
+ style={{
605
+ color: C.amber,
606
+ textShadow: `0 0 36px ${C.amberGlow}`,
607
+ }}
608
+ >
609
+ open-source
610
+ </span>
611
+ <br />
612
+ repo.
613
+ </div>
614
+ </div>
615
+ </AbsoluteFill>
616
+ );
617
+ };
618
+
619
+ // ═══════════════════════════════════════════════════════════════════════════
620
+ // SCENE 2 — NAME REVEAL · "It's called Huashu Design. Just dropped on GitHub."
621
+ // ═══════════════════════════════════════════════════════════════════════════
622
+
623
+ const Scene2: React.FC = () => {
624
+ const frame = useCurrentFrame();
625
+ const { fps } = useVideoConfig();
626
+
627
+ const kickerOp = fadeIn(frame, 0, 14);
628
+ const kickerY = tween(frame, 0, 18, 18, 0);
629
+
630
+ const huashuStart = 0.35;
631
+ const designStart = 0.78;
632
+
633
+ // GitHub card slides up
634
+ const repoSpring = sp(frame, fps, 2.4, "snappy");
635
+ const repoY = interpolate(repoSpring, [0, 1], [80, 0]);
636
+ const repoOp = interpolate(repoSpring, [0, 0.5], [0, 1], {
637
+ extrapolateLeft: "clamp",
638
+ extrapolateRight: "clamp",
639
+ });
640
+
641
+ // "JUST DROPPED" pulsing pill
642
+ const dropOp = fadeIn(frame, 100, 14);
643
+ const pulse = 0.5 + Math.sin(frame * 0.18) * 0.4;
644
+
645
+ const exit = fadeOut(frame, SCENES.S2.dur - 14, 12);
646
+
647
+ return (
648
+ <AbsoluteFill style={{ fontFamily: FONT, opacity: exit }}>
649
+ <DarkBg frame={frame} tint="amber" />
650
+
651
+ {/* TOP kicker */}
652
+ <div
653
+ style={{
654
+ position: "absolute",
655
+ top: SAFE_TOP,
656
+ left: SAFE_L,
657
+ right: SAFE_R,
658
+ display: "flex",
659
+ justifyContent: "flex-start",
660
+ opacity: kickerOp,
661
+ transform: `translateY(${kickerY}px)`,
662
+ }}
663
+ >
664
+ <Pill
665
+ bg="rgba(249,156,0,0.10)"
666
+ fg={C.amberSoft}
667
+ border="rgba(249,156,0,0.35)"
668
+ dot={C.amber}
669
+ >
670
+ NEW · OPEN SOURCE
671
+ </Pill>
672
+ </div>
673
+
674
+ {/* CENTER — name slam */}
675
+ <div
676
+ style={{
677
+ position: "absolute",
678
+ left: SAFE_L,
679
+ right: SAFE_R,
680
+ top: 460,
681
+ }}
682
+ >
683
+ <div style={{ marginBottom: 16 }}>
684
+ <StaggerRow
685
+ text="HUASHU"
686
+ startSec={huashuStart}
687
+ fontSize={184}
688
+ weight={900}
689
+ letterSpacing="-0.05em"
690
+ color={C.fg}
691
+ />
692
+ </div>
693
+ <div>
694
+ <StaggerRow
695
+ text="DESIGN."
696
+ startSec={designStart}
697
+ fontSize={184}
698
+ weight={900}
699
+ letterSpacing="-0.05em"
700
+ color={C.amber}
701
+ />
702
+ </div>
703
+
704
+ {/* mandarin sub */}
705
+ <div
706
+ style={{
707
+ marginTop: 24,
708
+ fontFamily: MONO,
709
+ fontSize: 22,
710
+ letterSpacing: "0.18em",
711
+ color: C.fgDim,
712
+ fontWeight: 500,
713
+ opacity: fadeIn(frame, 60, 18),
714
+ }}
715
+ >
716
+ 画术设计 · 「打字。回车。一份能交付的设计。」
717
+ </div>
718
+ </div>
719
+
720
+ {/* GITHUB REPO CARD */}
721
+ <div
722
+ style={{
723
+ position: "absolute",
724
+ left: SAFE_L,
725
+ right: SAFE_R,
726
+ top: 1140,
727
+ opacity: repoOp,
728
+ transform: `translateY(${repoY}px)`,
729
+ }}
730
+ >
731
+ <div
732
+ style={{
733
+ background: C.surface,
734
+ border: `1px solid ${C.borderLoud}`,
735
+ borderRadius: 22,
736
+ padding: "26px 30px",
737
+ boxShadow:
738
+ "0 24px 60px -18px rgba(0,0,0,0.7), inset 0 1px 0 rgba(255,255,255,0.08)",
739
+ }}
740
+ >
741
+ <div
742
+ style={{
743
+ display: "flex",
744
+ alignItems: "center",
745
+ gap: 18,
746
+ marginBottom: 16,
747
+ }}
748
+ >
749
+ {/* GitHub mark */}
750
+ <svg width="44" height="44" viewBox="0 0 24 24" fill={C.fg}>
751
+ <path d="M12 0C5.37 0 0 5.37 0 12c0 5.3 3.44 9.79 8.21 11.39.6.11.82-.26.82-.58v-2.04c-3.34.73-4.04-1.61-4.04-1.61-.55-1.39-1.34-1.76-1.34-1.76-1.09-.74.08-.73.08-.73 1.21.09 1.85 1.24 1.85 1.24 1.07 1.84 2.81 1.31 3.5 1 .11-.78.42-1.31.76-1.61-2.66-.3-5.46-1.33-5.46-5.93 0-1.31.47-2.38 1.24-3.22-.13-.31-.54-1.53.11-3.18 0 0 1.01-.32 3.31 1.23a11.5 11.5 0 0 1 6.02 0c2.3-1.55 3.31-1.23 3.31-1.23.65 1.65.24 2.87.12 3.18.77.84 1.24 1.91 1.24 3.22 0 4.61-2.81 5.62-5.48 5.92.43.37.81 1.1.81 2.22v3.29c0 .32.22.7.83.58A12 12 0 0 0 24 12c0-6.63-5.37-12-12-12z" />
752
+ </svg>
753
+ <div style={{ display: "flex", flexDirection: "column" }}>
754
+ <div
755
+ style={{
756
+ fontFamily: MONO,
757
+ fontSize: 16,
758
+ letterSpacing: "0.1em",
759
+ color: C.fgDim,
760
+ fontWeight: 500,
761
+ }}
762
+ >
763
+ github.com
764
+ </div>
765
+ <div
766
+ style={{
767
+ fontFamily: MONO,
768
+ fontSize: 30,
769
+ fontWeight: 600,
770
+ color: C.fg,
771
+ letterSpacing: "-0.01em",
772
+ }}
773
+ >
774
+ alchaincyf/huashu-design
775
+ </div>
776
+ </div>
777
+ </div>
778
+
779
+ <div
780
+ style={{
781
+ display: "flex",
782
+ alignItems: "center",
783
+ gap: 12,
784
+ opacity: dropOp,
785
+ }}
786
+ >
787
+ <div
788
+ style={{
789
+ display: "inline-flex",
790
+ alignItems: "center",
791
+ gap: 10,
792
+ padding: "8px 16px",
793
+ borderRadius: 9999,
794
+ background: "rgba(0,210,148,0.12)",
795
+ border: `1px solid rgba(0,210,148,0.35)`,
796
+ color: C.emeraldSoft,
797
+ fontFamily: MONO,
798
+ fontSize: 16,
799
+ fontWeight: 600,
800
+ letterSpacing: "0.1em",
801
+ }}
802
+ >
803
+ <span
804
+ style={{
805
+ width: 6,
806
+ height: 6,
807
+ borderRadius: 999,
808
+ background: C.emerald,
809
+ opacity: pulse,
810
+ boxShadow: `0 0 ${6 + pulse * 10}px ${C.emerald}`,
811
+ }}
812
+ />
813
+ JUST DROPPED
814
+ </div>
815
+ <div
816
+ style={{
817
+ fontFamily: MONO,
818
+ fontSize: 16,
819
+ color: C.fgDim,
820
+ letterSpacing: "0.08em",
821
+ }}
822
+ >
823
+ · Personal-use license
824
+ </div>
825
+ </div>
826
+ </div>
827
+ </div>
828
+ </AbsoluteFill>
829
+ );
830
+ };
831
+
832
+ // ═══════════════════════════════════════════════════════════════════════════
833
+ // SCENE 3 — DROPS INTO ANY AGENT · Claude Code · Cursor · Codex · OpenClaw · Hermes
834
+ // ═══════════════════════════════════════════════════════════════════════════
835
+
836
+ type AgentChip = {
837
+ name: string;
838
+ mark: React.ReactNode;
839
+ bg: string;
840
+ border: string;
841
+ };
842
+
843
+ const ClaudeMark: React.FC = () => (
844
+ <svg width="34" height="34" viewBox="0 0 24 24" fill="#d4663a">
845
+ <path d="M5 4l3 6h2L7 4H5zm6 0l3 6h2l-3-6h-2zm-6 9l-3 7h2l3-7H5zm6 0l-3 7h2l3-7h-2zm6-9l3 6h-2l-3-6h2zm0 9l3 7h-2l-3-7h2z" />
846
+ </svg>
847
+ );
848
+
849
+ const CursorMark: React.FC = () => (
850
+ <svg width="34" height="34" viewBox="0 0 24 24">
851
+ <path d="M3.4 2.5l9.1 18.9 2.7-7.7 7.7-2.7L3.4 2.5z" fill="#fff" />
852
+ </svg>
853
+ );
854
+
855
+ const OpenAIMark: React.FC = () => (
856
+ <svg width="34" height="34" viewBox="0 0 24 24" fill="#10a37f">
857
+ <path d="M22.28 9.82a5.95 5.95 0 0 0-.51-4.91 6.04 6.04 0 0 0-6.51-2.9A6.07 6.07 0 0 0 4.99 4.16 5.98 5.98 0 0 0 1 7.06a6.04 6.04 0 0 0 .74 7.08 5.95 5.95 0 0 0 .51 4.91 6.06 6.06 0 0 0 6.51 2.9A5.99 5.99 0 0 0 13.26 24a6.06 6.06 0 0 0 5.77-4.21 5.99 5.99 0 0 0 4-2.9 6.06 6.06 0 0 0-.75-7.07zM13.26 22.43a4.48 4.48 0 0 1-2.88-1.04l.14-.08 4.78-2.76a.79.79 0 0 0 .39-.68v-6.74l2.02 1.17.02.02v5.58a4.5 4.5 0 0 1-4.47 4.53zM3.6 18.32a4.5 4.5 0 0 1-.53-3.02l.14.08 4.78 2.76a.78.78 0 0 0 .79 0l5.84-3.37v2.33a.07.07 0 0 1-.03.06l-4.83 2.79a4.5 4.5 0 0 1-6.16-1.63zm-1.18-9.6a4.5 4.5 0 0 1 2.36-1.99l-.01.16v5.5a.78.78 0 0 0 .39.67l5.84 3.37-2.02 1.17a.08.08 0 0 1-.07.01L4.07 14.8a4.5 4.5 0 0 1-1.65-6.07v-.01zm16.59 3.85l-5.84-3.39 2.01-1.16a.08.08 0 0 1 .07-.01l4.83 2.79a4.5 4.5 0 0 1-.68 8.13v-5.69a.78.78 0 0 0-.39-.67zm2.01-3.02l-.14-.09-4.77-2.78a.78.78 0 0 0-.79 0L9.48 10.05V7.72a.07.07 0 0 1 .03-.06l4.83-2.78a4.5 4.5 0 0 1 6.68 4.66v.01zm-12.65 4.16l-2.02-1.17a.08.08 0 0 1-.04-.06v-5.55a4.5 4.5 0 0 1 7.38-3.46l-.14.08-4.78 2.76a.79.79 0 0 0-.39.68l-.01 6.72zm1.1-2.36L12.07 9.8l2.6 1.5v3l-2.6 1.5-2.6-1.5v-3z" />
858
+ </svg>
859
+ );
860
+
861
+ const OpenClawMark: React.FC = () => (
862
+ <svg width="34" height="34" viewBox="0 0 24 24" fill="none" stroke="#f99c00" strokeWidth="2">
863
+ <circle cx="12" cy="12" r="9" />
864
+ <path d="M8 9c1 2 2 3 4 3s3-1 4-3M9 15c1 1 2 2 3 2s2-1 3-2" />
865
+ </svg>
866
+ );
867
+
868
+ const HermesMark: React.FC = () => (
869
+ <svg width="34" height="34" viewBox="0 0 24 24" fill="none" stroke="#a4b3ff" strokeWidth="2">
870
+ <path d="M4 12 L20 12 M12 4 L12 20 M6 6 L18 18 M18 6 L6 18" strokeLinecap="round" />
871
+ </svg>
872
+ );
873
+
874
+ const Scene3: React.FC = () => {
875
+ const frame = useCurrentFrame();
876
+ const { fps } = useVideoConfig();
877
+
878
+ const titleOp = fadeIn(frame, 0, 14);
879
+ const titleY = tween(frame, 0, 18, 22, 0);
880
+
881
+ const lineOp = fadeIn(frame, 14, 14);
882
+ const lineY = tween(frame, 14, 18, 26, 0);
883
+
884
+ // central skill node breathes
885
+ const breathe = 0.92 + Math.sin(frame * 0.045) * 0.06;
886
+
887
+ const skillSpring = sp(frame, fps, 0.55, "bouncy");
888
+ const skillOp = interpolate(skillSpring, [0, 0.5], [0, 1], {
889
+ extrapolateLeft: "clamp",
890
+ extrapolateRight: "clamp",
891
+ });
892
+ const skillScale = interpolate(skillSpring, [0, 0.6, 1], [0.6, 1.1, 1]);
893
+
894
+ const agents: AgentChip[] = [
895
+ {
896
+ name: "Claude Code",
897
+ mark: <ClaudeMark />,
898
+ bg: "rgba(212,102,58,0.10)",
899
+ border: "rgba(212,102,58,0.35)",
900
+ },
901
+ {
902
+ name: "Cursor",
903
+ mark: <CursorMark />,
904
+ bg: "rgba(255,255,255,0.06)",
905
+ border: "rgba(255,255,255,0.18)",
906
+ },
907
+ {
908
+ name: "Codex",
909
+ mark: <OpenAIMark />,
910
+ bg: "rgba(16,163,127,0.10)",
911
+ border: "rgba(16,163,127,0.32)",
912
+ },
913
+ {
914
+ name: "OpenClaw",
915
+ mark: <OpenClawMark />,
916
+ bg: "rgba(249,156,0,0.08)",
917
+ border: "rgba(249,156,0,0.32)",
918
+ },
919
+ {
920
+ name: "Hermes",
921
+ mark: <HermesMark />,
922
+ bg: "rgba(164,179,255,0.08)",
923
+ border: "rgba(164,179,255,0.32)",
924
+ },
925
+ ];
926
+
927
+ const exit = fadeOut(frame, SCENES.S3.dur - 14, 12);
928
+
929
+ return (
930
+ <AbsoluteFill style={{ fontFamily: FONT, opacity: exit }}>
931
+ <DarkBg frame={frame} tint="neutral" />
932
+
933
+ {/* TOP — kicker */}
934
+ <div
935
+ style={{
936
+ position: "absolute",
937
+ top: SAFE_TOP,
938
+ left: SAFE_L,
939
+ right: SAFE_R,
940
+ opacity: titleOp,
941
+ transform: `translateY(${titleY}px)`,
942
+ }}
943
+ >
944
+ <Pill
945
+ bg="rgba(255,255,255,0.05)"
946
+ fg={C.fgSoft}
947
+ border="rgba(255,255,255,0.14)"
948
+ >
949
+ AGENT-AGNOSTIC
950
+ </Pill>
951
+ </div>
952
+
953
+ {/* HEADLINE — asymmetric, left-aligned */}
954
+ <div
955
+ style={{
956
+ position: "absolute",
957
+ left: SAFE_L,
958
+ right: SAFE_R,
959
+ top: 380,
960
+ opacity: lineOp,
961
+ transform: `translateY(${lineY}px)`,
962
+ }}
963
+ >
964
+ <div
965
+ style={{
966
+ fontFamily: FONT,
967
+ fontSize: 88,
968
+ fontWeight: 800,
969
+ letterSpacing: "-0.045em",
970
+ lineHeight: 0.95,
971
+ color: C.fg,
972
+ }}
973
+ >
974
+ Drop it into{" "}
975
+ <span style={{ color: C.amber }}>any</span>
976
+ <br />
977
+ coding agent.
978
+ </div>
979
+ </div>
980
+
981
+ {/* CENTER skill node */}
982
+ <div
983
+ style={{
984
+ position: "absolute",
985
+ left: 0,
986
+ right: 0,
987
+ top: 700,
988
+ display: "flex",
989
+ justifyContent: "center",
990
+ opacity: skillOp,
991
+ }}
992
+ >
993
+ <div
994
+ style={{
995
+ transform: `scale(${skillScale * breathe})`,
996
+ display: "flex",
997
+ alignItems: "center",
998
+ gap: 14,
999
+ padding: "20px 32px",
1000
+ background: C.surfaceWarm,
1001
+ border: `1px solid ${C.amberDeep}`,
1002
+ borderRadius: 9999,
1003
+ boxShadow: `0 0 ${30 + (breathe - 0.92) * 600}px ${C.amberGlow}, inset 0 1px 0 rgba(255,255,255,0.10)`,
1004
+ }}
1005
+ >
1006
+ <div
1007
+ style={{
1008
+ width: 42,
1009
+ height: 42,
1010
+ borderRadius: 999,
1011
+ background: `radial-gradient(circle at 30% 30%, ${C.amberSoft} 0%, ${C.amber} 60%, ${C.amberDeep} 100%)`,
1012
+ boxShadow: `0 0 30px ${C.amber}`,
1013
+ }}
1014
+ />
1015
+ <div
1016
+ style={{
1017
+ fontFamily: MONO,
1018
+ fontSize: 28,
1019
+ fontWeight: 600,
1020
+ color: C.fg,
1021
+ letterSpacing: "0.02em",
1022
+ }}
1023
+ >
1024
+ huashu-design
1025
+ </div>
1026
+ <div
1027
+ style={{
1028
+ fontFamily: MONO,
1029
+ fontSize: 16,
1030
+ color: C.amberSoft,
1031
+ padding: "4px 10px",
1032
+ borderRadius: 6,
1033
+ background: "rgba(249,156,0,0.14)",
1034
+ letterSpacing: "0.12em",
1035
+ fontWeight: 600,
1036
+ }}
1037
+ >
1038
+ SKILL
1039
+ </div>
1040
+ </div>
1041
+ </div>
1042
+
1043
+ {/* AGENTS — radial cascade */}
1044
+ <div
1045
+ style={{
1046
+ position: "absolute",
1047
+ left: SAFE_L,
1048
+ right: SAFE_R,
1049
+ top: 880,
1050
+ display: "grid",
1051
+ gridTemplateColumns: "1fr 1fr",
1052
+ gap: 16,
1053
+ }}
1054
+ >
1055
+ {agents.slice(0, 4).map((a, i) => {
1056
+ const s = sp(frame, fps, 1.2 + i * 0.18, "bouncy");
1057
+ const op = interpolate(s, [0, 0.5], [0, 1], {
1058
+ extrapolateLeft: "clamp",
1059
+ extrapolateRight: "clamp",
1060
+ });
1061
+ const x = interpolate(s, [0, 1], [i % 2 === 0 ? -50 : 50, 0]);
1062
+ const sc = interpolate(s, [0, 0.6, 1], [0.7, 1.06, 1]);
1063
+ return (
1064
+ <div
1065
+ key={a.name}
1066
+ style={{
1067
+ opacity: op,
1068
+ transform: `translateX(${x}px) scale(${sc})`,
1069
+ background: a.bg,
1070
+ border: `1px solid ${a.border}`,
1071
+ borderRadius: 22,
1072
+ padding: "26px 28px",
1073
+ display: "flex",
1074
+ alignItems: "center",
1075
+ gap: 16,
1076
+ backdropFilter: "blur(20px)",
1077
+ boxShadow: "inset 0 1px 0 rgba(255,255,255,0.06)",
1078
+ }}
1079
+ >
1080
+ {a.mark}
1081
+ <div
1082
+ style={{
1083
+ fontFamily: FONT,
1084
+ fontSize: 30,
1085
+ fontWeight: 600,
1086
+ color: C.fg,
1087
+ letterSpacing: "-0.01em",
1088
+ }}
1089
+ >
1090
+ {a.name}
1091
+ </div>
1092
+ </div>
1093
+ );
1094
+ })}
1095
+ </div>
1096
+
1097
+ {/* "+ more" tag */}
1098
+ <div
1099
+ style={{
1100
+ position: "absolute",
1101
+ left: 0,
1102
+ right: 0,
1103
+ top: 1340,
1104
+ display: "flex",
1105
+ justifyContent: "center",
1106
+ opacity: fadeIn(frame, 200, 18),
1107
+ }}
1108
+ >
1109
+ <div
1110
+ style={{
1111
+ fontFamily: MONO,
1112
+ fontSize: 22,
1113
+ color: C.fgDim,
1114
+ letterSpacing: "0.18em",
1115
+ display: "flex",
1116
+ alignItems: "center",
1117
+ gap: 14,
1118
+ }}
1119
+ >
1120
+ <span style={{ width: 30, height: 1, background: C.fgDim }} />
1121
+ + OPENCLAW · HERMES · ANY SKILLS-COMPATIBLE AGENT
1122
+ <span style={{ width: 30, height: 1, background: C.fgDim }} />
1123
+ </div>
1124
+ </div>
1125
+ </AbsoluteFill>
1126
+ );
1127
+ };
1128
+
1129
+ // ═══════════════════════════════════════════════════════════════════════════
1130
+ // SCENE 4 — INSTALL · "Install is one line — npx skills add"
1131
+ // ═══════════════════════════════════════════════════════════════════════════
1132
+
1133
+ const Scene4: React.FC = () => {
1134
+ const frame = useCurrentFrame();
1135
+ const { fps } = useVideoConfig();
1136
+
1137
+ const winSpring = sp(frame, fps, 0, "snappy");
1138
+ const winY = interpolate(winSpring, [0, 1], [70, 0]);
1139
+ const winOp = interpolate(winSpring, [0, 0.5], [0, 1], {
1140
+ extrapolateLeft: "clamp",
1141
+ extrapolateRight: "clamp",
1142
+ });
1143
+
1144
+ // typewriter
1145
+ const typed = "npx skills add alchaincyf/huashu-design";
1146
+ const startTypeF = 18;
1147
+ const charPerFrame = 1.4;
1148
+ const charsShown = Math.max(
1149
+ 0,
1150
+ Math.min(typed.length, Math.floor((frame - startTypeF) * charPerFrame)),
1151
+ );
1152
+
1153
+ const typeDoneAt = startTypeF + Math.ceil(typed.length / charPerFrame);
1154
+ const installSuccessAt = typeDoneAt + 18;
1155
+
1156
+ // success row
1157
+ const successOp = fadeIn(frame, installSuccessAt, 14);
1158
+ const successY = tween(frame, installSuccessAt, 18, 14, 0);
1159
+
1160
+ // bottom one-line tag
1161
+ const oneLineOp = fadeIn(frame, installSuccessAt + 18, 14);
1162
+ const oneLineY = tween(frame, installSuccessAt + 18, 18, 24, 0);
1163
+
1164
+ const exit = fadeOut(frame, SCENES.S4.dur - 14, 12);
1165
+
1166
+ return (
1167
+ <AbsoluteFill style={{ fontFamily: FONT, opacity: exit }}>
1168
+ <DarkBg frame={frame} tint="amber" />
1169
+
1170
+ {/* TOP kicker */}
1171
+ <div
1172
+ style={{
1173
+ position: "absolute",
1174
+ top: SAFE_TOP,
1175
+ left: SAFE_L,
1176
+ right: SAFE_R,
1177
+ opacity: fadeIn(frame, 0, 14),
1178
+ }}
1179
+ >
1180
+ <Pill
1181
+ bg="rgba(249,156,0,0.10)"
1182
+ fg={C.amberSoft}
1183
+ border="rgba(249,156,0,0.32)"
1184
+ >
1185
+ ONE-LINE INSTALL
1186
+ </Pill>
1187
+ </div>
1188
+
1189
+ {/* TERMINAL window */}
1190
+ <div
1191
+ style={{
1192
+ position: "absolute",
1193
+ left: SAFE_L,
1194
+ right: SAFE_R,
1195
+ top: 480,
1196
+ opacity: winOp,
1197
+ transform: `translateY(${winY}px)`,
1198
+ }}
1199
+ >
1200
+ <div
1201
+ style={{
1202
+ background: C.surface,
1203
+ border: `1px solid ${C.borderLoud}`,
1204
+ borderRadius: 22,
1205
+ overflow: "hidden",
1206
+ boxShadow:
1207
+ "0 30px 80px -20px rgba(0,0,0,0.7), inset 0 1px 0 rgba(255,255,255,0.10)",
1208
+ }}
1209
+ >
1210
+ {/* title bar */}
1211
+ <div
1212
+ style={{
1213
+ display: "flex",
1214
+ alignItems: "center",
1215
+ gap: 10,
1216
+ padding: "16px 22px",
1217
+ background: "rgba(255,255,255,0.03)",
1218
+ borderBottom: `1px solid ${C.border}`,
1219
+ }}
1220
+ >
1221
+ <div
1222
+ style={{
1223
+ width: 12,
1224
+ height: 12,
1225
+ borderRadius: 999,
1226
+ background: "#ff5f57",
1227
+ }}
1228
+ />
1229
+ <div
1230
+ style={{
1231
+ width: 12,
1232
+ height: 12,
1233
+ borderRadius: 999,
1234
+ background: "#febc2e",
1235
+ }}
1236
+ />
1237
+ <div
1238
+ style={{
1239
+ width: 12,
1240
+ height: 12,
1241
+ borderRadius: 999,
1242
+ background: "#28c840",
1243
+ }}
1244
+ />
1245
+ <div
1246
+ style={{
1247
+ marginLeft: 18,
1248
+ fontFamily: MONO,
1249
+ fontSize: 18,
1250
+ color: C.fgMuted,
1251
+ letterSpacing: "0.05em",
1252
+ }}
1253
+ >
1254
+ ~/projects · zsh
1255
+ </div>
1256
+ </div>
1257
+
1258
+ {/* body */}
1259
+ <div
1260
+ style={{
1261
+ padding: "36px 30px 50px",
1262
+ fontFamily: MONO,
1263
+ fontSize: 26,
1264
+ lineHeight: 1.5,
1265
+ color: C.fgSoft,
1266
+ minHeight: 380,
1267
+ }}
1268
+ >
1269
+ <div style={{ color: C.fgDim, marginBottom: 14 }}>
1270
+ # one line. all 20 skills.
1271
+ </div>
1272
+ <div>
1273
+ <span style={{ color: C.amber }}>$</span>{" "}
1274
+ {typed.slice(0, charsShown)}
1275
+ {frame < typeDoneAt + 8 ? (
1276
+ <span
1277
+ style={{
1278
+ display: "inline-block",
1279
+ width: 14,
1280
+ height: 28,
1281
+ marginLeft: 4,
1282
+ background: C.fg,
1283
+ verticalAlign: "-4px",
1284
+ opacity:
1285
+ 0.35 +
1286
+ 0.65 *
1287
+ Math.pow(Math.cos((frame * Math.PI) / 16), 2),
1288
+ }}
1289
+ />
1290
+ ) : null}
1291
+ </div>
1292
+
1293
+ {/* progress bar */}
1294
+ {frame > typeDoneAt + 4 ? (
1295
+ <div style={{ marginTop: 26 }}>
1296
+ <div
1297
+ style={{
1298
+ fontSize: 18,
1299
+ color: C.fgDim,
1300
+ marginBottom: 8,
1301
+ letterSpacing: "0.02em",
1302
+ }}
1303
+ >
1304
+ resolving alchaincyf/huashu-design...
1305
+ </div>
1306
+ <div
1307
+ style={{
1308
+ width: "100%",
1309
+ height: 8,
1310
+ background: "rgba(255,255,255,0.06)",
1311
+ borderRadius: 999,
1312
+ overflow: "hidden",
1313
+ }}
1314
+ >
1315
+ <div
1316
+ style={{
1317
+ width: `${tween(frame, typeDoneAt + 4, 14, 0, 100)}%`,
1318
+ height: "100%",
1319
+ background: `linear-gradient(90deg, ${C.amber}, ${C.amberSoft})`,
1320
+ boxShadow: `0 0 12px ${C.amberGlow}`,
1321
+ }}
1322
+ />
1323
+ </div>
1324
+ </div>
1325
+ ) : null}
1326
+
1327
+ {/* success line */}
1328
+ <div
1329
+ style={{
1330
+ marginTop: 20,
1331
+ opacity: successOp,
1332
+ transform: `translateY(${successY}px)`,
1333
+ display: "flex",
1334
+ alignItems: "center",
1335
+ gap: 12,
1336
+ color: C.emeraldSoft,
1337
+ fontWeight: 600,
1338
+ }}
1339
+ >
1340
+ <span
1341
+ style={{
1342
+ display: "inline-flex",
1343
+ alignItems: "center",
1344
+ justifyContent: "center",
1345
+ width: 28,
1346
+ height: 28,
1347
+ borderRadius: 999,
1348
+ background: "rgba(0,210,148,0.15)",
1349
+ border: `1px solid rgba(0,210,148,0.45)`,
1350
+ }}
1351
+ >
1352
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke={C.emerald} strokeWidth="3" strokeLinecap="round" strokeLinejoin="round">
1353
+ <path d="M5 12l5 5L20 7" />
1354
+ </svg>
1355
+ </span>
1356
+ installed · 20 skills · 0 errors
1357
+ </div>
1358
+ </div>
1359
+ </div>
1360
+ </div>
1361
+
1362
+ {/* BOTTOM tag */}
1363
+ <div
1364
+ style={{
1365
+ position: "absolute",
1366
+ left: SAFE_L,
1367
+ right: SAFE_R,
1368
+ top: 1320,
1369
+ opacity: oneLineOp,
1370
+ transform: `translateY(${oneLineY}px)`,
1371
+ }}
1372
+ >
1373
+ <div
1374
+ style={{
1375
+ fontFamily: FONT,
1376
+ fontSize: 64,
1377
+ fontWeight: 800,
1378
+ letterSpacing: "-0.04em",
1379
+ lineHeight: 0.98,
1380
+ color: C.fg,
1381
+ }}
1382
+ >
1383
+ Then talk to your agent.
1384
+ <span style={{ color: C.amber }}>{" "}No buttons. No panels.</span>
1385
+ </div>
1386
+ </div>
1387
+ </AbsoluteFill>
1388
+ );
1389
+ };
1390
+
1391
+ // ═══════════════════════════════════════════════════════════════════════════
1392
+ // SCENE 5 — CAPABILITIES BENTO · prototypes / decks / motion / infographics
1393
+ // ═══════════════════════════════════════════════════════════════════════════
1394
+
1395
+ type Capability = {
1396
+ kicker: string;
1397
+ name: string;
1398
+ body: string;
1399
+ glyph: React.ReactNode;
1400
+ tint: string;
1401
+ delay: number;
1402
+ };
1403
+
1404
+ const Scene5: React.FC = () => {
1405
+ const frame = useCurrentFrame();
1406
+ const { fps } = useVideoConfig();
1407
+
1408
+ const titleOp = fadeIn(frame, 0, 14);
1409
+ const titleY = tween(frame, 0, 18, 22, 0);
1410
+
1411
+ const caps: Capability[] = [
1412
+ {
1413
+ kicker: "01 · INTERACTIVE",
1414
+ name: "Prototypes",
1415
+ body: "iPhone bezels, Playwright-tested clicks.",
1416
+ tint: C.amber,
1417
+ delay: 0.35,
1418
+ glyph: (
1419
+ <svg width="44" height="44" viewBox="0 0 24 24" fill="none" stroke={C.amber} strokeWidth="1.6">
1420
+ <rect x="6" y="2" width="12" height="20" rx="3" />
1421
+ <path d="M10 18h4" />
1422
+ </svg>
1423
+ ),
1424
+ },
1425
+ {
1426
+ kicker: "02 · HTML→PPTX",
1427
+ name: "Slide Decks",
1428
+ body: "Editable text boxes, real PowerPoint output.",
1429
+ tint: C.emerald,
1430
+ delay: 0.55,
1431
+ glyph: (
1432
+ <svg width="44" height="44" viewBox="0 0 24 24" fill="none" stroke={C.emerald} strokeWidth="1.6">
1433
+ <rect x="3" y="4" width="18" height="14" rx="2" />
1434
+ <path d="M8 21h8M12 18v3" />
1435
+ </svg>
1436
+ ),
1437
+ },
1438
+ {
1439
+ kicker: "03 · TIMELINE",
1440
+ name: "Motion",
1441
+ body: "MP4 + GIF + 60fps interpolation, BGM ready.",
1442
+ tint: "#a4b3ff",
1443
+ delay: 0.75,
1444
+ glyph: (
1445
+ <svg width="44" height="44" viewBox="0 0 24 24" fill="none" stroke="#a4b3ff" strokeWidth="1.6">
1446
+ <polygon points="6,4 20,12 6,20" fill="#a4b3ff" fillOpacity="0.18" />
1447
+ </svg>
1448
+ ),
1449
+ },
1450
+ {
1451
+ kicker: "04 · PRINT-GRADE",
1452
+ name: "Infographics",
1453
+ body: "CSS Grid typography, PDF/PNG/SVG export.",
1454
+ tint: C.amberSoft,
1455
+ delay: 0.95,
1456
+ glyph: (
1457
+ <svg width="44" height="44" viewBox="0 0 24 24" fill="none" stroke={C.amberSoft} strokeWidth="1.6">
1458
+ <rect x="3" y="13" width="4" height="8" />
1459
+ <rect x="10" y="8" width="4" height="13" />
1460
+ <rect x="17" y="3" width="4" height="18" />
1461
+ </svg>
1462
+ ),
1463
+ },
1464
+ ];
1465
+
1466
+ const exit = fadeOut(frame, SCENES.S5.dur - 14, 12);
1467
+
1468
+ return (
1469
+ <AbsoluteFill style={{ fontFamily: FONT, opacity: exit }}>
1470
+ <DarkBg frame={frame} tint="amber" />
1471
+
1472
+ {/* TOP — kicker */}
1473
+ <div
1474
+ style={{
1475
+ position: "absolute",
1476
+ top: SAFE_TOP,
1477
+ left: SAFE_L,
1478
+ right: SAFE_R,
1479
+ opacity: titleOp,
1480
+ transform: `translateY(${titleY}px)`,
1481
+ }}
1482
+ >
1483
+ <Pill
1484
+ bg="rgba(255,255,255,0.05)"
1485
+ fg={C.fgSoft}
1486
+ border="rgba(255,255,255,0.14)"
1487
+ >
1488
+ ONE SKILL · FOUR DELIVERABLES
1489
+ </Pill>
1490
+ </div>
1491
+
1492
+ {/* HEADLINE */}
1493
+ <div
1494
+ style={{
1495
+ position: "absolute",
1496
+ left: SAFE_L,
1497
+ right: SAFE_R,
1498
+ top: 360,
1499
+ opacity: fadeIn(frame, 6, 14),
1500
+ transform: `translateY(${tween(frame, 6, 18, 26, 0)}px)`,
1501
+ }}
1502
+ >
1503
+ <div
1504
+ style={{
1505
+ fontFamily: FONT,
1506
+ fontSize: 84,
1507
+ fontWeight: 800,
1508
+ letterSpacing: "-0.045em",
1509
+ lineHeight: 0.95,
1510
+ color: C.fg,
1511
+ }}
1512
+ >
1513
+ Type once. Ship{" "}
1514
+ <span style={{ color: C.amber }}>anything.</span>
1515
+ </div>
1516
+ </div>
1517
+
1518
+ {/* BENTO 2x2 — staggered cascade */}
1519
+ <div
1520
+ style={{
1521
+ position: "absolute",
1522
+ left: SAFE_L,
1523
+ right: SAFE_R,
1524
+ top: 600,
1525
+ display: "grid",
1526
+ gridTemplateColumns: "1fr 1fr",
1527
+ gridTemplateRows: "1fr 1fr",
1528
+ gap: 16,
1529
+ height: 880,
1530
+ }}
1531
+ >
1532
+ {caps.map((c, i) => {
1533
+ const s = sp(frame, fps, c.delay, "bouncy");
1534
+ const op = interpolate(s, [0, 0.5], [0, 1], {
1535
+ extrapolateLeft: "clamp",
1536
+ extrapolateRight: "clamp",
1537
+ });
1538
+ const y = interpolate(s, [0, 1], [60, 0]);
1539
+ const sc = interpolate(s, [0, 0.6, 1], [0.84, 1.04, 1]);
1540
+
1541
+ // perpetual gentle float
1542
+ const floatY = Math.sin(frame * 0.03 + i * 1.3) * 4;
1543
+
1544
+ return (
1545
+ <div
1546
+ key={c.name}
1547
+ style={{
1548
+ opacity: op,
1549
+ transform: `translateY(${y + floatY}px) scale(${sc})`,
1550
+ background: C.surface,
1551
+ border: `1px solid ${C.borderLoud}`,
1552
+ borderRadius: 28,
1553
+ padding: "30px 28px",
1554
+ display: "flex",
1555
+ flexDirection: "column",
1556
+ justifyContent: "space-between",
1557
+ position: "relative",
1558
+ overflow: "hidden",
1559
+ boxShadow: `0 24px 60px -20px rgba(0,0,0,0.6), inset 0 1px 0 rgba(255,255,255,0.08)`,
1560
+ }}
1561
+ >
1562
+ {/* corner glow */}
1563
+ <div
1564
+ style={{
1565
+ position: "absolute",
1566
+ top: -120,
1567
+ right: -120,
1568
+ width: 280,
1569
+ height: 280,
1570
+ borderRadius: 9999,
1571
+ background: c.tint,
1572
+ filter: "blur(80px)",
1573
+ opacity: 0.16,
1574
+ }}
1575
+ />
1576
+ <div
1577
+ style={{ position: "relative", zIndex: 1 }}
1578
+ >
1579
+ {c.glyph}
1580
+ <div
1581
+ style={{
1582
+ marginTop: 12,
1583
+ fontFamily: MONO,
1584
+ fontSize: 14,
1585
+ letterSpacing: "0.16em",
1586
+ color: c.tint,
1587
+ fontWeight: 600,
1588
+ }}
1589
+ >
1590
+ {c.kicker}
1591
+ </div>
1592
+ </div>
1593
+ <div style={{ position: "relative", zIndex: 1 }}>
1594
+ <div
1595
+ style={{
1596
+ fontFamily: FONT,
1597
+ fontSize: 48,
1598
+ fontWeight: 700,
1599
+ letterSpacing: "-0.025em",
1600
+ color: C.fg,
1601
+ marginBottom: 8,
1602
+ lineHeight: 1,
1603
+ }}
1604
+ >
1605
+ {c.name}
1606
+ </div>
1607
+ <div
1608
+ style={{
1609
+ fontFamily: FONT,
1610
+ fontSize: 19,
1611
+ color: C.fgMuted,
1612
+ lineHeight: 1.4,
1613
+ fontWeight: 400,
1614
+ }}
1615
+ >
1616
+ {c.body}
1617
+ </div>
1618
+ </div>
1619
+ </div>
1620
+ );
1621
+ })}
1622
+ </div>
1623
+ </AbsoluteFill>
1624
+ );
1625
+ };
1626
+
1627
+ // ═══════════════════════════════════════════════════════════════════════════
1628
+ // SCENE 6 — UNDER THE HOOD · claude-design.mov + 20 philosophies + toolchain
1629
+ // ═══════════════════════════════════════════════════════════════════════════
1630
+
1631
+ const Scene6: React.FC = () => {
1632
+ const frame = useCurrentFrame();
1633
+ const { fps } = useVideoConfig();
1634
+
1635
+ const titleOp = fadeIn(frame, 0, 14);
1636
+ const titleY = tween(frame, 0, 18, 22, 0);
1637
+
1638
+ // big counter — 20 philosophies
1639
+ const counterValue = Math.round(tween(frame, 24, 36, 0, 20));
1640
+
1641
+ // toolchain pills — staggered
1642
+ const tools = [
1643
+ { label: "HTML → MP4", color: C.amber, delay: 5.0 },
1644
+ { label: "PLAYWRIGHT", color: C.emerald, delay: 5.4 },
1645
+ { label: "PPTX EXPORT", color: "#a4b3ff", delay: 5.8 },
1646
+ { label: "60fps INTERP", color: C.amberSoft, delay: 6.2 },
1647
+ ];
1648
+
1649
+ const videoSpring = sp(frame, fps, 1.4, "snappy");
1650
+ const videoY = interpolate(videoSpring, [0, 1], [50, 0]);
1651
+ const videoOp = interpolate(videoSpring, [0, 0.5], [0, 1], {
1652
+ extrapolateLeft: "clamp",
1653
+ extrapolateRight: "clamp",
1654
+ });
1655
+
1656
+ // REC dot pulse
1657
+ const recPulse = 0.5 + Math.sin(frame * 0.22) * 0.4;
1658
+
1659
+ const exit = fadeOut(frame, SCENES.S6.dur - 14, 12);
1660
+
1661
+ return (
1662
+ <AbsoluteFill style={{ fontFamily: FONT, opacity: exit }}>
1663
+ <DarkBg frame={frame} tint="amber" />
1664
+
1665
+ {/* TOP kicker */}
1666
+ <div
1667
+ style={{
1668
+ position: "absolute",
1669
+ top: SAFE_TOP,
1670
+ left: SAFE_L,
1671
+ right: SAFE_R,
1672
+ opacity: titleOp,
1673
+ transform: `translateY(${titleY}px)`,
1674
+ }}
1675
+ >
1676
+ <Pill
1677
+ bg="rgba(249,156,0,0.10)"
1678
+ fg={C.amberSoft}
1679
+ border="rgba(249,156,0,0.32)"
1680
+ >
1681
+ UNDER THE HOOD
1682
+ </Pill>
1683
+ </div>
1684
+
1685
+ {/* HUGE COUNTER */}
1686
+ <div
1687
+ style={{
1688
+ position: "absolute",
1689
+ left: SAFE_L,
1690
+ right: SAFE_R,
1691
+ top: 360,
1692
+ display: "flex",
1693
+ alignItems: "flex-end",
1694
+ gap: 22,
1695
+ opacity: fadeIn(frame, 12, 14),
1696
+ }}
1697
+ >
1698
+ <div
1699
+ style={{
1700
+ fontFamily: FONT,
1701
+ fontSize: 240,
1702
+ fontWeight: 900,
1703
+ letterSpacing: "-0.06em",
1704
+ lineHeight: 0.85,
1705
+ color: C.amber,
1706
+ textShadow: `0 0 60px ${C.amberGlow}`,
1707
+ }}
1708
+ >
1709
+ {counterValue}
1710
+ </div>
1711
+ <div
1712
+ style={{
1713
+ paddingBottom: 24,
1714
+ fontFamily: FONT,
1715
+ fontSize: 38,
1716
+ fontWeight: 600,
1717
+ letterSpacing: "-0.02em",
1718
+ color: C.fg,
1719
+ lineHeight: 1.05,
1720
+ maxWidth: 480,
1721
+ }}
1722
+ >
1723
+ design philosophies,
1724
+ <br />
1725
+ <span style={{ color: C.fgMuted }}>baked in.</span>
1726
+ </div>
1727
+ </div>
1728
+
1729
+ {/* SCREEN-REC FRAME with claude-design.mov */}
1730
+ <div
1731
+ style={{
1732
+ position: "absolute",
1733
+ left: SAFE_L,
1734
+ right: SAFE_R,
1735
+ top: 700,
1736
+ opacity: videoOp,
1737
+ transform: `translateY(${videoY}px)`,
1738
+ }}
1739
+ >
1740
+ <div
1741
+ style={{
1742
+ background: C.bg,
1743
+ border: `1px solid ${C.borderLoud}`,
1744
+ borderRadius: 22,
1745
+ overflow: "hidden",
1746
+ boxShadow:
1747
+ "0 24px 60px -18px rgba(0,0,0,0.8), inset 0 1px 0 rgba(255,255,255,0.10)",
1748
+ position: "relative",
1749
+ aspectRatio: "1896 / 1716",
1750
+ }}
1751
+ >
1752
+ {/* title bar */}
1753
+ <div
1754
+ style={{
1755
+ position: "absolute",
1756
+ top: 0,
1757
+ left: 0,
1758
+ right: 0,
1759
+ display: "flex",
1760
+ alignItems: "center",
1761
+ gap: 8,
1762
+ padding: "12px 18px",
1763
+ background: "rgba(0,0,0,0.45)",
1764
+ borderBottom: `1px solid ${C.border}`,
1765
+ backdropFilter: "blur(10px)",
1766
+ zIndex: 2,
1767
+ }}
1768
+ >
1769
+ <div
1770
+ style={{
1771
+ width: 10,
1772
+ height: 10,
1773
+ borderRadius: 999,
1774
+ background: "#ff5f57",
1775
+ }}
1776
+ />
1777
+ <div
1778
+ style={{
1779
+ width: 10,
1780
+ height: 10,
1781
+ borderRadius: 999,
1782
+ background: "#febc2e",
1783
+ }}
1784
+ />
1785
+ <div
1786
+ style={{
1787
+ width: 10,
1788
+ height: 10,
1789
+ borderRadius: 999,
1790
+ background: "#28c840",
1791
+ }}
1792
+ />
1793
+ <div
1794
+ style={{
1795
+ marginLeft: 14,
1796
+ fontFamily: MONO,
1797
+ fontSize: 14,
1798
+ color: C.fgMuted,
1799
+ letterSpacing: "0.06em",
1800
+ }}
1801
+ >
1802
+ huashu-design · live preview
1803
+ </div>
1804
+ <div
1805
+ style={{
1806
+ marginLeft: "auto",
1807
+ display: "flex",
1808
+ alignItems: "center",
1809
+ gap: 6,
1810
+ fontFamily: MONO,
1811
+ fontSize: 12,
1812
+ color: C.red,
1813
+ letterSpacing: "0.14em",
1814
+ fontWeight: 600,
1815
+ }}
1816
+ >
1817
+ <span
1818
+ style={{
1819
+ width: 6,
1820
+ height: 6,
1821
+ borderRadius: 999,
1822
+ background: C.red,
1823
+ opacity: recPulse,
1824
+ boxShadow: `0 0 ${recPulse * 14}px ${C.red}`,
1825
+ }}
1826
+ />
1827
+ REC
1828
+ </div>
1829
+ </div>
1830
+
1831
+ {/* the clip — wrapped so it starts after dialog has settled */}
1832
+ <Sequence from={42} durationInFrames={SCENES.S6.dur - 42}>
1833
+ {/* REFERENCE-STRIP: <OffthreadVideo> removed — bring your own clip */}
1834
+ </Sequence>
1835
+
1836
+ {/* subtle scanline overlay */}
1837
+ <div
1838
+ style={{
1839
+ position: "absolute",
1840
+ inset: 0,
1841
+ backgroundImage: `repeating-linear-gradient(0deg, rgba(255,255,255,0.02) 0px, rgba(255,255,255,0.02) 1px, transparent 1px, transparent 4px)`,
1842
+ pointerEvents: "none",
1843
+ mixBlendMode: "overlay",
1844
+ }}
1845
+ />
1846
+ </div>
1847
+ </div>
1848
+
1849
+ {/* TOOLCHAIN — kinetic pill row */}
1850
+ <div
1851
+ style={{
1852
+ position: "absolute",
1853
+ bottom: 1920 - SAFE_BOT_Y + 30,
1854
+ left: SAFE_L,
1855
+ right: SAFE_R,
1856
+ display: "flex",
1857
+ flexWrap: "wrap",
1858
+ gap: 12,
1859
+ justifyContent: "flex-start",
1860
+ }}
1861
+ >
1862
+ {tools.map((t) => {
1863
+ const s = sp(frame, fps, t.delay, "snappy");
1864
+ const op = interpolate(s, [0, 0.5], [0, 1], {
1865
+ extrapolateLeft: "clamp",
1866
+ extrapolateRight: "clamp",
1867
+ });
1868
+ const y = interpolate(s, [0, 1], [16, 0]);
1869
+ return (
1870
+ <div
1871
+ key={t.label}
1872
+ style={{
1873
+ opacity: op,
1874
+ transform: `translateY(${y}px)`,
1875
+ padding: "10px 18px",
1876
+ borderRadius: 9999,
1877
+ background: "rgba(255,255,255,0.04)",
1878
+ border: `1px solid ${t.color}55`,
1879
+ color: t.color,
1880
+ fontFamily: MONO,
1881
+ fontSize: 18,
1882
+ fontWeight: 600,
1883
+ letterSpacing: "0.1em",
1884
+ display: "inline-flex",
1885
+ alignItems: "center",
1886
+ gap: 8,
1887
+ }}
1888
+ >
1889
+ <span
1890
+ style={{
1891
+ width: 6,
1892
+ height: 6,
1893
+ borderRadius: 999,
1894
+ background: t.color,
1895
+ boxShadow: `0 0 8px ${t.color}`,
1896
+ }}
1897
+ />
1898
+ {t.label}
1899
+ </div>
1900
+ );
1901
+ })}
1902
+ </div>
1903
+ </AbsoluteFill>
1904
+ );
1905
+ };
1906
+
1907
+ // ═══════════════════════════════════════════════════════════════════════════
1908
+ // SCENE 7 — VARIANTS + TWEAKS PANEL · "three side-by-side options"
1909
+ // ═══════════════════════════════════════════════════════════════════════════
1910
+
1911
+ const Scene7: React.FC = () => {
1912
+ const frame = useCurrentFrame();
1913
+ const { fps } = useVideoConfig();
1914
+
1915
+ const titleOp = fadeIn(frame, 0, 14);
1916
+ const titleY = tween(frame, 0, 18, 22, 0);
1917
+
1918
+ const variants = [
1919
+ {
1920
+ label: "Editorial",
1921
+ tone: "Serif",
1922
+ bg: "#f4ede0",
1923
+ ink: "#1a1410",
1924
+ accent: "#9a5e00",
1925
+ tag: C.amber,
1926
+ delay: 0.4,
1927
+ },
1928
+ {
1929
+ label: "Brutalist",
1930
+ tone: "Mono",
1931
+ bg: "#0d0d0e",
1932
+ ink: "#fafafa",
1933
+ accent: "#00d294",
1934
+ tag: C.emerald,
1935
+ delay: 0.6,
1936
+ },
1937
+ {
1938
+ label: "Soft",
1939
+ tone: "Geist",
1940
+ bg: "#ece8e3",
1941
+ ink: "#2a2722",
1942
+ accent: "#d4663a",
1943
+ tag: C.claudeWarm,
1944
+ delay: 0.8,
1945
+ },
1946
+ ];
1947
+
1948
+ // Tweaks panel
1949
+ const panelSpring = sp(frame, fps, 4.0, "snappy");
1950
+ const panelOp = interpolate(panelSpring, [0, 0.5], [0, 1], {
1951
+ extrapolateLeft: "clamp",
1952
+ extrapolateRight: "clamp",
1953
+ });
1954
+ const panelY = interpolate(panelSpring, [0, 1], [40, 0]);
1955
+
1956
+ // animated slider value
1957
+ const sliderHue = 50 + (Math.sin(frame * 0.04) + 1) * 80;
1958
+ const sliderDensity = 30 + (Math.cos(frame * 0.05) + 1) * 30;
1959
+
1960
+ const exit = fadeOut(frame, SCENES.S7.dur - 14, 12);
1961
+
1962
+ return (
1963
+ <AbsoluteFill style={{ fontFamily: FONT, opacity: exit }}>
1964
+ <DarkBg frame={frame} tint="neutral" />
1965
+
1966
+ {/* TOP kicker */}
1967
+ <div
1968
+ style={{
1969
+ position: "absolute",
1970
+ top: SAFE_TOP,
1971
+ left: SAFE_L,
1972
+ right: SAFE_R,
1973
+ opacity: titleOp,
1974
+ transform: `translateY(${titleY}px)`,
1975
+ }}
1976
+ >
1977
+ <Pill
1978
+ bg="rgba(255,255,255,0.05)"
1979
+ fg={C.fgSoft}
1980
+ border="rgba(255,255,255,0.14)"
1981
+ >
1982
+ THREE VARIANTS · TWEAKS PANEL
1983
+ </Pill>
1984
+ </div>
1985
+
1986
+ {/* HEADLINE */}
1987
+ <div
1988
+ style={{
1989
+ position: "absolute",
1990
+ left: SAFE_L,
1991
+ right: SAFE_R,
1992
+ top: 360,
1993
+ opacity: fadeIn(frame, 6, 14),
1994
+ transform: `translateY(${tween(frame, 6, 18, 26, 0)}px)`,
1995
+ }}
1996
+ >
1997
+ <div
1998
+ style={{
1999
+ fontFamily: FONT,
2000
+ fontSize: 78,
2001
+ fontWeight: 800,
2002
+ letterSpacing: "-0.045em",
2003
+ lineHeight: 0.96,
2004
+ color: C.fg,
2005
+ }}
2006
+ >
2007
+ Three options.
2008
+ <br />
2009
+ <span style={{ color: C.amber }}>Pick one. Or tweak.</span>
2010
+ </div>
2011
+ </div>
2012
+
2013
+ {/* VARIANT CARDS — three side-by-side */}
2014
+ <div
2015
+ style={{
2016
+ position: "absolute",
2017
+ left: SAFE_L,
2018
+ right: SAFE_R,
2019
+ top: 660,
2020
+ display: "grid",
2021
+ gridTemplateColumns: "1fr 1fr 1fr",
2022
+ gap: 12,
2023
+ height: 460,
2024
+ }}
2025
+ >
2026
+ {variants.map((v, i) => {
2027
+ const s = sp(frame, fps, v.delay, "bouncy");
2028
+ const op = interpolate(s, [0, 0.5], [0, 1], {
2029
+ extrapolateLeft: "clamp",
2030
+ extrapolateRight: "clamp",
2031
+ });
2032
+ const y = interpolate(s, [0, 1], [80, 0]);
2033
+ const sc = interpolate(s, [0, 0.6, 1], [0.82, 1.04, 1]);
2034
+ const tilt = i === 0 ? -2 : i === 2 ? 2 : 0;
2035
+
2036
+ // perpetual breathe per card
2037
+ const breath = Math.sin(frame * 0.025 + i * 1.7) * 0.5;
2038
+
2039
+ return (
2040
+ <div
2041
+ key={v.label}
2042
+ style={{
2043
+ opacity: op,
2044
+ transform: `translateY(${y + breath}px) scale(${sc}) rotate(${tilt}deg)`,
2045
+ background: v.bg,
2046
+ borderRadius: 22,
2047
+ padding: "26px 22px",
2048
+ position: "relative",
2049
+ overflow: "hidden",
2050
+ display: "flex",
2051
+ flexDirection: "column",
2052
+ justifyContent: "space-between",
2053
+ boxShadow:
2054
+ i === 1
2055
+ ? `0 20px 50px -12px rgba(0,0,0,0.6), inset 0 1px 0 rgba(255,255,255,0.15)`
2056
+ : `0 16px 40px -12px rgba(0,0,0,0.5), inset 0 1px 0 rgba(255,255,255,0.12)`,
2057
+ }}
2058
+ >
2059
+ {/* mini "design preview" */}
2060
+ <div>
2061
+ <div
2062
+ style={{
2063
+ fontFamily: MONO,
2064
+ fontSize: 12,
2065
+ letterSpacing: "0.18em",
2066
+ color: v.ink,
2067
+ opacity: 0.45,
2068
+ fontWeight: 600,
2069
+ marginBottom: 10,
2070
+ }}
2071
+ >
2072
+ VARIANT 0{i + 1}
2073
+ </div>
2074
+ <div
2075
+ style={{
2076
+ fontFamily:
2077
+ v.tone === "Serif"
2078
+ ? "ui-serif, 'Iowan Old Style', 'Charter', 'Source Serif Pro', Georgia, serif"
2079
+ : v.tone === "Mono"
2080
+ ? MONO
2081
+ : FONT,
2082
+ fontSize: 38,
2083
+ fontWeight: 700,
2084
+ color: v.ink,
2085
+ letterSpacing:
2086
+ v.tone === "Serif" ? "-0.02em" : "-0.03em",
2087
+ lineHeight: 0.95,
2088
+ marginBottom: 10,
2089
+ }}
2090
+ >
2091
+ {v.label}
2092
+ </div>
2093
+ <div
2094
+ style={{
2095
+ width: "85%",
2096
+ height: 3,
2097
+ background: v.accent,
2098
+ borderRadius: 999,
2099
+ marginBottom: 8,
2100
+ }}
2101
+ />
2102
+ <div
2103
+ style={{
2104
+ width: "55%",
2105
+ height: 3,
2106
+ background: v.ink,
2107
+ opacity: 0.16,
2108
+ borderRadius: 999,
2109
+ }}
2110
+ />
2111
+ </div>
2112
+
2113
+ {/* tag */}
2114
+ <div
2115
+ style={{
2116
+ display: "inline-flex",
2117
+ alignSelf: "flex-start",
2118
+ alignItems: "center",
2119
+ gap: 6,
2120
+ padding: "6px 12px",
2121
+ borderRadius: 9999,
2122
+ background: `${v.accent}26`,
2123
+ color: v.accent,
2124
+ fontFamily: MONO,
2125
+ fontSize: 13,
2126
+ fontWeight: 600,
2127
+ letterSpacing: "0.1em",
2128
+ }}
2129
+ >
2130
+ <span
2131
+ style={{
2132
+ width: 5,
2133
+ height: 5,
2134
+ borderRadius: 999,
2135
+ background: v.accent,
2136
+ }}
2137
+ />
2138
+ {v.tone}
2139
+ </div>
2140
+ </div>
2141
+ );
2142
+ })}
2143
+ </div>
2144
+
2145
+ {/* TWEAKS PANEL */}
2146
+ <div
2147
+ style={{
2148
+ position: "absolute",
2149
+ left: SAFE_L,
2150
+ right: SAFE_R,
2151
+ top: 1180,
2152
+ opacity: panelOp,
2153
+ transform: `translateY(${panelY}px)`,
2154
+ background: C.surface,
2155
+ border: `1px solid ${C.borderLoud}`,
2156
+ borderRadius: 22,
2157
+ padding: "22px 26px",
2158
+ boxShadow:
2159
+ "0 20px 50px -16px rgba(0,0,0,0.6), inset 0 1px 0 rgba(255,255,255,0.06)",
2160
+ }}
2161
+ >
2162
+ <div
2163
+ style={{
2164
+ display: "flex",
2165
+ justifyContent: "space-between",
2166
+ alignItems: "center",
2167
+ marginBottom: 18,
2168
+ }}
2169
+ >
2170
+ <div
2171
+ style={{
2172
+ fontFamily: MONO,
2173
+ fontSize: 16,
2174
+ letterSpacing: "0.18em",
2175
+ color: C.fgDim,
2176
+ fontWeight: 600,
2177
+ }}
2178
+ >
2179
+ ./TWEAKS
2180
+ </div>
2181
+ <div
2182
+ style={{
2183
+ fontFamily: MONO,
2184
+ fontSize: 14,
2185
+ color: C.amber,
2186
+ letterSpacing: "0.1em",
2187
+ fontWeight: 600,
2188
+ }}
2189
+ >
2190
+ LIVE
2191
+ </div>
2192
+ </div>
2193
+
2194
+ {[
2195
+ { label: "Hue", val: sliderHue, color: C.amber },
2196
+ { label: "Density", val: sliderDensity, color: C.emerald },
2197
+ { label: "Type Scale", val: 50 + Math.cos(frame * 0.04) * 25, color: "#a4b3ff" },
2198
+ ].map((row) => (
2199
+ <div
2200
+ key={row.label}
2201
+ style={{
2202
+ display: "flex",
2203
+ alignItems: "center",
2204
+ gap: 14,
2205
+ marginBottom: 14,
2206
+ }}
2207
+ >
2208
+ <div
2209
+ style={{
2210
+ width: 130,
2211
+ fontFamily: MONO,
2212
+ fontSize: 16,
2213
+ color: C.fgSoft,
2214
+ letterSpacing: "0.04em",
2215
+ }}
2216
+ >
2217
+ {row.label}
2218
+ </div>
2219
+ <div
2220
+ style={{
2221
+ flex: 1,
2222
+ height: 6,
2223
+ background: "rgba(255,255,255,0.06)",
2224
+ borderRadius: 999,
2225
+ position: "relative",
2226
+ overflow: "hidden",
2227
+ }}
2228
+ >
2229
+ <div
2230
+ style={{
2231
+ position: "absolute",
2232
+ inset: 0,
2233
+ width: `${row.val}%`,
2234
+ background: `linear-gradient(90deg, ${row.color}aa, ${row.color})`,
2235
+ borderRadius: 999,
2236
+ }}
2237
+ />
2238
+ <div
2239
+ style={{
2240
+ position: "absolute",
2241
+ top: -4,
2242
+ left: `calc(${row.val}% - 7px)`,
2243
+ width: 14,
2244
+ height: 14,
2245
+ borderRadius: 999,
2246
+ background: row.color,
2247
+ boxShadow: `0 0 12px ${row.color}`,
2248
+ }}
2249
+ />
2250
+ </div>
2251
+ <div
2252
+ style={{
2253
+ width: 60,
2254
+ textAlign: "right",
2255
+ fontFamily: MONO,
2256
+ fontSize: 16,
2257
+ color: row.color,
2258
+ fontWeight: 600,
2259
+ }}
2260
+ >
2261
+ {Math.round(row.val)}
2262
+ </div>
2263
+ </div>
2264
+ ))}
2265
+ </div>
2266
+ </AbsoluteFill>
2267
+ );
2268
+ };
2269
+
2270
+ // ═══════════════════════════════════════════════════════════════════════════
2271
+ // SCENE 8 — THE PAYOFF · "No separate weekly cap. No design quota."
2272
+ // ═══════════════════════════════════════════════════════════════════════════
2273
+
2274
+ const Scene8: React.FC = () => {
2275
+ const frame = useCurrentFrame();
2276
+ const { fps } = useVideoConfig();
2277
+
2278
+ const titleOp = fadeIn(frame, 0, 14);
2279
+ const titleY = tween(frame, 0, 18, 22, 0);
2280
+
2281
+ // line 1 "NO" stagger
2282
+ // line 2 "NO" stagger
2283
+
2284
+ const line1Spring = sp(frame, fps, 0.6, "bouncy");
2285
+ const line1Op = interpolate(line1Spring, [0, 0.5], [0, 1], {
2286
+ extrapolateLeft: "clamp",
2287
+ extrapolateRight: "clamp",
2288
+ });
2289
+ const line1Scale = interpolate(line1Spring, [0, 0.6, 1], [0.8, 1.08, 1]);
2290
+
2291
+ const line2Spring = sp(frame, fps, 1.6, "bouncy");
2292
+ const line2Op = interpolate(line2Spring, [0, 0.5], [0, 1], {
2293
+ extrapolateLeft: "clamp",
2294
+ extrapolateRight: "clamp",
2295
+ });
2296
+ const line2Scale = interpolate(line2Spring, [0, 0.6, 1], [0.8, 1.08, 1]);
2297
+
2298
+ // infinity counter
2299
+ const infOp = fadeIn(frame, 90, 18);
2300
+ const infY = tween(frame, 90, 22, 30, 0);
2301
+
2302
+ // bottom tag
2303
+ const bottomOp = fadeIn(frame, 200, 14);
2304
+ const bottomY = tween(frame, 200, 22, 26, 0);
2305
+
2306
+ // ticker — designs shipped (animated)
2307
+ const designCount =
2308
+ 14207 + Math.floor((frame - 90) * 5.7);
2309
+ const safeDesignCount = frame > 90 ? designCount : 14207;
2310
+
2311
+ const exit = fadeOut(frame, SCENES.S8.dur - 14, 12);
2312
+
2313
+ return (
2314
+ <AbsoluteFill style={{ fontFamily: FONT, opacity: exit }}>
2315
+ <DarkBg frame={frame} tint="emerald" />
2316
+
2317
+ {/* TOP kicker */}
2318
+ <div
2319
+ style={{
2320
+ position: "absolute",
2321
+ top: SAFE_TOP,
2322
+ left: SAFE_L,
2323
+ right: SAFE_R,
2324
+ opacity: titleOp,
2325
+ transform: `translateY(${titleY}px)`,
2326
+ }}
2327
+ >
2328
+ <Pill
2329
+ bg="rgba(0,210,148,0.10)"
2330
+ fg={C.emeraldSoft}
2331
+ border="rgba(0,210,148,0.32)"
2332
+ dot={C.emerald}
2333
+ >
2334
+ THE PART THAT MATTERS
2335
+ </Pill>
2336
+ </div>
2337
+
2338
+ {/* THE NO STACK */}
2339
+ <div
2340
+ style={{
2341
+ position: "absolute",
2342
+ left: SAFE_L,
2343
+ right: SAFE_R,
2344
+ top: 460,
2345
+ }}
2346
+ >
2347
+ <div
2348
+ style={{
2349
+ opacity: line1Op,
2350
+ transform: `scale(${line1Scale})`,
2351
+ transformOrigin: "left center",
2352
+ fontFamily: FONT,
2353
+ fontWeight: 900,
2354
+ fontSize: 156,
2355
+ letterSpacing: "-0.06em",
2356
+ lineHeight: 0.86,
2357
+ color: C.fg,
2358
+ marginBottom: 8,
2359
+ }}
2360
+ >
2361
+ <span style={{ color: C.red }}>NO</span> separate
2362
+ <br />
2363
+ weekly cap.
2364
+ </div>
2365
+
2366
+ <div
2367
+ style={{
2368
+ opacity: line2Op,
2369
+ transform: `scale(${line2Scale})`,
2370
+ transformOrigin: "left center",
2371
+ fontFamily: FONT,
2372
+ fontWeight: 900,
2373
+ fontSize: 156,
2374
+ letterSpacing: "-0.06em",
2375
+ lineHeight: 0.86,
2376
+ color: C.fg,
2377
+ marginTop: 36,
2378
+ }}
2379
+ >
2380
+ <span style={{ color: C.red }}>NO</span> design
2381
+ <br />
2382
+ <span style={{ color: C.emerald }}>quota.</span>
2383
+ </div>
2384
+ </div>
2385
+
2386
+ {/* infinity counter */}
2387
+ <div
2388
+ style={{
2389
+ position: "absolute",
2390
+ left: SAFE_L,
2391
+ right: SAFE_R,
2392
+ top: 1230,
2393
+ opacity: infOp,
2394
+ transform: `translateY(${infY}px)`,
2395
+ }}
2396
+ >
2397
+ <div
2398
+ style={{
2399
+ background: "rgba(0,210,148,0.06)",
2400
+ border: `1px solid rgba(0,210,148,0.30)`,
2401
+ borderRadius: 22,
2402
+ padding: "22px 26px",
2403
+ display: "flex",
2404
+ justifyContent: "space-between",
2405
+ alignItems: "center",
2406
+ backdropFilter: "blur(20px)",
2407
+ boxShadow: "inset 0 1px 0 rgba(255,255,255,0.06)",
2408
+ }}
2409
+ >
2410
+ <div>
2411
+ <div
2412
+ style={{
2413
+ fontFamily: MONO,
2414
+ fontSize: 16,
2415
+ letterSpacing: "0.16em",
2416
+ color: C.emeraldSoft,
2417
+ fontWeight: 600,
2418
+ marginBottom: 6,
2419
+ }}
2420
+ >
2421
+ RUNS ON YOUR
2422
+ </div>
2423
+ <div
2424
+ style={{
2425
+ fontFamily: FONT,
2426
+ fontSize: 36,
2427
+ fontWeight: 700,
2428
+ color: C.fg,
2429
+ letterSpacing: "-0.02em",
2430
+ lineHeight: 1,
2431
+ }}
2432
+ >
2433
+ Claude Code usage.
2434
+ </div>
2435
+ </div>
2436
+ <div
2437
+ style={{
2438
+ textAlign: "right",
2439
+ }}
2440
+ >
2441
+ <div
2442
+ style={{
2443
+ fontFamily: MONO,
2444
+ fontSize: 13,
2445
+ letterSpacing: "0.18em",
2446
+ color: C.fgDim,
2447
+ fontWeight: 600,
2448
+ marginBottom: 4,
2449
+ }}
2450
+ >
2451
+ DESIGNS SHIPPED
2452
+ </div>
2453
+ <div
2454
+ style={{
2455
+ fontFamily: MONO,
2456
+ fontSize: 38,
2457
+ fontWeight: 600,
2458
+ color: C.emerald,
2459
+ letterSpacing: "-0.02em",
2460
+ }}
2461
+ >
2462
+ {safeDesignCount.toLocaleString()}
2463
+ </div>
2464
+ </div>
2465
+ </div>
2466
+ </div>
2467
+
2468
+ {/* BOTTOM TAG */}
2469
+ <div
2470
+ style={{
2471
+ position: "absolute",
2472
+ left: SAFE_L,
2473
+ right: SAFE_R,
2474
+ top: 1400,
2475
+ opacity: bottomOp,
2476
+ transform: `translateY(${bottomY}px)`,
2477
+ }}
2478
+ >
2479
+ <div
2480
+ style={{
2481
+ fontFamily: MONO,
2482
+ fontSize: 22,
2483
+ letterSpacing: "0.16em",
2484
+ color: C.fgDim,
2485
+ fontWeight: 500,
2486
+ }}
2487
+ >
2488
+ → THAT&apos;S THE WHOLE PITCH.
2489
+ </div>
2490
+ </div>
2491
+ </AbsoluteFill>
2492
+ );
2493
+ };
2494
+
2495
+ // ═══════════════════════════════════════════════════════════════════════════
2496
+ // SCENE 9 — HONEST TAKE · GUI vs Terminal split
2497
+ // ═══════════════════════════════════════════════════════════════════════════
2498
+
2499
+ const Scene9: React.FC = () => {
2500
+ const frame = useCurrentFrame();
2501
+ const { fps } = useVideoConfig();
2502
+
2503
+ const titleOp = fadeIn(frame, 0, 14);
2504
+ const titleY = tween(frame, 0, 18, 22, 0);
2505
+
2506
+ // GUI side slides from left
2507
+ const guiSpring = sp(frame, fps, 0.55, "snappy");
2508
+ const guiX = interpolate(guiSpring, [0, 1], [-80, 0]);
2509
+ const guiOp = interpolate(guiSpring, [0, 0.5], [0, 1], {
2510
+ extrapolateLeft: "clamp",
2511
+ extrapolateRight: "clamp",
2512
+ });
2513
+
2514
+ // Terminal side slides from right
2515
+ const termSpring = sp(frame, fps, 0.85, "snappy");
2516
+ const termX = interpolate(termSpring, [0, 1], [80, 0]);
2517
+ const termOp = interpolate(termSpring, [0, 0.5], [0, 1], {
2518
+ extrapolateLeft: "clamp",
2519
+ extrapolateRight: "clamp",
2520
+ });
2521
+
2522
+ // VS divider
2523
+ const vsSpring = sp(frame, fps, 1.4, "bouncy");
2524
+ const vsScale = interpolate(vsSpring, [0, 0.6, 1], [0.4, 1.15, 1]);
2525
+ const vsOp = interpolate(vsSpring, [0, 0.5], [0, 1], {
2526
+ extrapolateLeft: "clamp",
2527
+ extrapolateRight: "clamp",
2528
+ });
2529
+
2530
+ const verdictOp = fadeIn(frame, 200, 14);
2531
+ const verdictY = tween(frame, 200, 22, 28, 0);
2532
+
2533
+ const exit = fadeOut(frame, SCENES.S9.dur - 14, 12);
2534
+
2535
+ // typewriter for terminal mock
2536
+ const cmdTyped = "> design a landing page";
2537
+ const cmdStart = 80;
2538
+ const cmdShown = Math.max(
2539
+ 0,
2540
+ Math.min(cmdTyped.length, Math.floor((frame - cmdStart) * 1.2)),
2541
+ );
2542
+
2543
+ return (
2544
+ <AbsoluteFill style={{ fontFamily: FONT, opacity: exit }}>
2545
+ <DarkBg frame={frame} tint="neutral" />
2546
+
2547
+ {/* TOP kicker */}
2548
+ <div
2549
+ style={{
2550
+ position: "absolute",
2551
+ top: SAFE_TOP,
2552
+ left: SAFE_L,
2553
+ right: SAFE_R,
2554
+ opacity: titleOp,
2555
+ transform: `translateY(${titleY}px)`,
2556
+ }}
2557
+ >
2558
+ <Pill
2559
+ bg="rgba(255,255,255,0.05)"
2560
+ fg={C.fgSoft}
2561
+ border="rgba(255,255,255,0.14)"
2562
+ >
2563
+ THE HONEST TAKE
2564
+ </Pill>
2565
+ </div>
2566
+
2567
+ {/* HEADLINE */}
2568
+ <div
2569
+ style={{
2570
+ position: "absolute",
2571
+ left: SAFE_L,
2572
+ right: SAFE_R,
2573
+ top: 360,
2574
+ opacity: fadeIn(frame, 6, 14),
2575
+ transform: `translateY(${tween(frame, 6, 18, 24, 0)}px)`,
2576
+ }}
2577
+ >
2578
+ <div
2579
+ style={{
2580
+ fontFamily: FONT,
2581
+ fontSize: 78,
2582
+ fontWeight: 800,
2583
+ letterSpacing: "-0.045em",
2584
+ lineHeight: 0.95,
2585
+ color: C.fg,
2586
+ }}
2587
+ >
2588
+ Replace it fully?{" "}
2589
+ <span style={{ color: C.fgMuted }}>Probably not.</span>
2590
+ </div>
2591
+ </div>
2592
+
2593
+ {/* SPLIT — vertical 50/50 of two cards */}
2594
+ <div
2595
+ style={{
2596
+ position: "absolute",
2597
+ left: SAFE_L,
2598
+ right: SAFE_R,
2599
+ top: 660,
2600
+ height: 540,
2601
+ display: "grid",
2602
+ gridTemplateColumns: "1fr 1fr",
2603
+ gap: 16,
2604
+ position: "absolute",
2605
+ }}
2606
+ >
2607
+ {/* GUI side */}
2608
+ <div
2609
+ style={{
2610
+ opacity: guiOp,
2611
+ transform: `translateX(${guiX}px)`,
2612
+ background: "#fafaf7",
2613
+ borderRadius: 24,
2614
+ padding: "24px 22px",
2615
+ border: "1px solid rgba(0,0,0,0.08)",
2616
+ display: "flex",
2617
+ flexDirection: "column",
2618
+ justifyContent: "space-between",
2619
+ boxShadow: "0 16px 40px -12px rgba(0,0,0,0.4)",
2620
+ }}
2621
+ >
2622
+ <div>
2623
+ <div
2624
+ style={{
2625
+ fontFamily: MONO,
2626
+ fontSize: 13,
2627
+ letterSpacing: "0.18em",
2628
+ color: "#9a8b6f",
2629
+ fontWeight: 600,
2630
+ marginBottom: 14,
2631
+ }}
2632
+ >
2633
+ CLAUDE DESIGN · GUI
2634
+ </div>
2635
+
2636
+ {/* fake toolbar */}
2637
+ <div
2638
+ style={{
2639
+ display: "flex",
2640
+ gap: 8,
2641
+ marginBottom: 14,
2642
+ }}
2643
+ >
2644
+ {["click", "draw", "comment"].map((b) => (
2645
+ <div
2646
+ key={b}
2647
+ style={{
2648
+ padding: "6px 12px",
2649
+ borderRadius: 9,
2650
+ background: "rgba(0,0,0,0.05)",
2651
+ fontFamily: MONO,
2652
+ fontSize: 12,
2653
+ color: "#3a2f24",
2654
+ fontWeight: 600,
2655
+ letterSpacing: "0.05em",
2656
+ }}
2657
+ >
2658
+ {b}
2659
+ </div>
2660
+ ))}
2661
+ </div>
2662
+
2663
+ {/* fake canvas with shapes */}
2664
+ <div
2665
+ style={{
2666
+ background: "#fff",
2667
+ borderRadius: 12,
2668
+ border: "1px solid rgba(0,0,0,0.08)",
2669
+ padding: 16,
2670
+ position: "relative",
2671
+ height: 240,
2672
+ }}
2673
+ >
2674
+ <div
2675
+ style={{
2676
+ position: "absolute",
2677
+ top: 24,
2678
+ left: 24,
2679
+ width: 80,
2680
+ height: 80,
2681
+ background: C.amber,
2682
+ borderRadius: 8,
2683
+ opacity: 0.85,
2684
+ }}
2685
+ />
2686
+ <div
2687
+ style={{
2688
+ position: "absolute",
2689
+ top: 60,
2690
+ left: 100,
2691
+ width: 100,
2692
+ height: 60,
2693
+ background: "#1a1410",
2694
+ borderRadius: 8,
2695
+ opacity: 0.85,
2696
+ }}
2697
+ />
2698
+ <div
2699
+ style={{
2700
+ position: "absolute",
2701
+ top: 130,
2702
+ left: 40,
2703
+ width: 110,
2704
+ height: 6,
2705
+ background: "#3a2f24",
2706
+ borderRadius: 999,
2707
+ opacity: 0.5,
2708
+ }}
2709
+ />
2710
+ <div
2711
+ style={{
2712
+ position: "absolute",
2713
+ top: 145,
2714
+ left: 40,
2715
+ width: 70,
2716
+ height: 4,
2717
+ background: "#3a2f24",
2718
+ borderRadius: 999,
2719
+ opacity: 0.3,
2720
+ }}
2721
+ />
2722
+
2723
+ {/* cursor */}
2724
+ <div
2725
+ style={{
2726
+ position: "absolute",
2727
+ top: 110 + Math.sin(frame * 0.06) * 16,
2728
+ left: 130 + Math.cos(frame * 0.05) * 22,
2729
+ fontSize: 16,
2730
+ fontFamily: MONO,
2731
+ color: "#1a1410",
2732
+ }}
2733
+ >
2734
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="#1a1410">
2735
+ <path d="M3 2l8 18 2-7 7-2L3 2z" />
2736
+ </svg>
2737
+ </div>
2738
+ </div>
2739
+ </div>
2740
+ <div
2741
+ style={{
2742
+ fontFamily: FONT,
2743
+ fontSize: 18,
2744
+ color: "#3a2f24",
2745
+ fontWeight: 600,
2746
+ letterSpacing: "-0.01em",
2747
+ }}
2748
+ >
2749
+ wins for click · draw · comment
2750
+ </div>
2751
+ </div>
2752
+
2753
+ {/* TERMINAL side */}
2754
+ <div
2755
+ style={{
2756
+ opacity: termOp,
2757
+ transform: `translateX(${termX}px)`,
2758
+ background: C.surface,
2759
+ borderRadius: 24,
2760
+ padding: "24px 22px",
2761
+ border: `1px solid ${C.borderLoud}`,
2762
+ display: "flex",
2763
+ flexDirection: "column",
2764
+ justifyContent: "space-between",
2765
+ boxShadow:
2766
+ "0 16px 40px -12px rgba(0,0,0,0.6), inset 0 1px 0 rgba(255,255,255,0.06)",
2767
+ }}
2768
+ >
2769
+ <div>
2770
+ <div
2771
+ style={{
2772
+ fontFamily: MONO,
2773
+ fontSize: 13,
2774
+ letterSpacing: "0.18em",
2775
+ color: C.amberSoft,
2776
+ fontWeight: 600,
2777
+ marginBottom: 14,
2778
+ }}
2779
+ >
2780
+ HUASHU · TERMINAL
2781
+ </div>
2782
+
2783
+ {/* fake terminal */}
2784
+ <div
2785
+ style={{
2786
+ background: "#0a0a0c",
2787
+ borderRadius: 12,
2788
+ border: `1px solid ${C.border}`,
2789
+ padding: 18,
2790
+ position: "relative",
2791
+ height: 296,
2792
+ fontFamily: MONO,
2793
+ fontSize: 17,
2794
+ color: C.fgSoft,
2795
+ lineHeight: 1.5,
2796
+ }}
2797
+ >
2798
+ <div style={{ color: C.fgDim, marginBottom: 8 }}>
2799
+ ~/projects · zsh
2800
+ </div>
2801
+ <div style={{ color: C.amber, marginBottom: 8 }}>
2802
+ {cmdTyped.slice(0, cmdShown)}
2803
+ {frame < cmdStart + cmdTyped.length / 1.2 + 8 ? (
2804
+ <span
2805
+ style={{
2806
+ display: "inline-block",
2807
+ width: 9,
2808
+ height: 19,
2809
+ marginLeft: 3,
2810
+ background: C.amber,
2811
+ verticalAlign: "-3px",
2812
+ opacity:
2813
+ 0.4 +
2814
+ 0.6 *
2815
+ Math.pow(Math.cos((frame * Math.PI) / 16), 2),
2816
+ }}
2817
+ />
2818
+ ) : null}
2819
+ </div>
2820
+ {frame > cmdStart + 30 ? (
2821
+ <>
2822
+ <div style={{ color: C.fgDim }}>
2823
+ [planning] picking variant...
2824
+ </div>
2825
+ <div style={{ color: C.fgDim }}>
2826
+ [generating] 3 directions in parallel
2827
+ </div>
2828
+ <div
2829
+ style={{
2830
+ color: C.emerald,
2831
+ fontWeight: 600,
2832
+ marginTop: 6,
2833
+ }}
2834
+ >
2835
+ ✓ delivered in 4m 18s
2836
+ </div>
2837
+ </>
2838
+ ) : null}
2839
+ </div>
2840
+ </div>
2841
+ <div
2842
+ style={{
2843
+ fontFamily: FONT,
2844
+ fontSize: 18,
2845
+ color: C.amberSoft,
2846
+ fontWeight: 600,
2847
+ letterSpacing: "-0.01em",
2848
+ }}
2849
+ >
2850
+ closes the gap fast.
2851
+ </div>
2852
+ </div>
2853
+
2854
+ {/* center "VS" badge */}
2855
+ <div
2856
+ style={{
2857
+ position: "absolute",
2858
+ top: 230,
2859
+ left: "50%",
2860
+ transform: `translateX(-50%) scale(${vsScale})`,
2861
+ opacity: vsOp,
2862
+ zIndex: 5,
2863
+ }}
2864
+ >
2865
+ <div
2866
+ style={{
2867
+ width: 88,
2868
+ height: 88,
2869
+ borderRadius: 999,
2870
+ background: C.bg,
2871
+ border: `2px solid ${C.amber}`,
2872
+ display: "flex",
2873
+ alignItems: "center",
2874
+ justifyContent: "center",
2875
+ fontFamily: MONO,
2876
+ fontSize: 26,
2877
+ fontWeight: 700,
2878
+ color: C.amber,
2879
+ letterSpacing: "0.05em",
2880
+ boxShadow: `0 0 30px ${C.amberGlow}`,
2881
+ }}
2882
+ >
2883
+ VS
2884
+ </div>
2885
+ </div>
2886
+ </div>
2887
+
2888
+ {/* VERDICT */}
2889
+ <div
2890
+ style={{
2891
+ position: "absolute",
2892
+ left: SAFE_L,
2893
+ right: SAFE_R,
2894
+ top: 1300,
2895
+ opacity: verdictOp,
2896
+ transform: `translateY(${verdictY}px)`,
2897
+ }}
2898
+ >
2899
+ <div
2900
+ style={{
2901
+ fontFamily: FONT,
2902
+ fontSize: 50,
2903
+ fontWeight: 700,
2904
+ letterSpacing: "-0.025em",
2905
+ lineHeight: 1.1,
2906
+ color: C.fg,
2907
+ }}
2908
+ >
2909
+ But if you live in your terminal —
2910
+ <br />
2911
+ <span style={{ color: C.amber }}>this thing closes the gap fast.</span>
2912
+ </div>
2913
+ </div>
2914
+ </AbsoluteFill>
2915
+ );
2916
+ };
2917
+
2918
+ // ═══════════════════════════════════════════════════════════════════════════
2919
+ // SCENE 10 — LICENSE WARNING · "personal use only"
2920
+ // ═══════════════════════════════════════════════════════════════════════════
2921
+
2922
+ const Scene10: React.FC = () => {
2923
+ const frame = useCurrentFrame();
2924
+ const { fps } = useVideoConfig();
2925
+
2926
+ const tagOp = fadeIn(frame, 0, 14);
2927
+ const tagY = tween(frame, 0, 18, 22, 0);
2928
+
2929
+ const cardSpring = sp(frame, fps, 0.4, "snappy");
2930
+ const cardOp = interpolate(cardSpring, [0, 0.5], [0, 1], {
2931
+ extrapolateLeft: "clamp",
2932
+ extrapolateRight: "clamp",
2933
+ });
2934
+ const cardY = interpolate(cardSpring, [0, 1], [50, 0]);
2935
+
2936
+ // checklist staggered
2937
+ const checks = [
2938
+ { ok: true, text: "Personal projects", delay: 1.4 },
2939
+ { ok: true, text: "Your own portfolio, internal tools", delay: 1.7 },
2940
+ { ok: false, text: "Client work / commercial gigs", delay: 2.0 },
2941
+ { ok: false, text: "Reselling generated assets", delay: 2.3 },
2942
+ ];
2943
+
2944
+ const readmeSpring = sp(frame, fps, 5.4, "bouncy");
2945
+ const readmeOp = interpolate(readmeSpring, [0, 0.5], [0, 1], {
2946
+ extrapolateLeft: "clamp",
2947
+ extrapolateRight: "clamp",
2948
+ });
2949
+ const readmeScale = interpolate(readmeSpring, [0, 0.6, 1], [0.85, 1.06, 1]);
2950
+
2951
+ const exit = fadeOut(frame, SCENES.S10.dur - 14, 12);
2952
+
2953
+ return (
2954
+ <AbsoluteFill style={{ fontFamily: FONT, opacity: exit }}>
2955
+ <DarkBg frame={frame} tint="amber" />
2956
+
2957
+ {/* TOP kicker */}
2958
+ <div
2959
+ style={{
2960
+ position: "absolute",
2961
+ top: SAFE_TOP,
2962
+ left: SAFE_L,
2963
+ right: SAFE_R,
2964
+ opacity: tagOp,
2965
+ transform: `translateY(${tagY}px)`,
2966
+ }}
2967
+ >
2968
+ <Pill
2969
+ bg="rgba(252,187,0,0.10)"
2970
+ fg={C.amberSoft}
2971
+ border="rgba(252,187,0,0.32)"
2972
+ dot={C.amberSoft}
2973
+ >
2974
+ QUICK HEADS-UP · LICENSE
2975
+ </Pill>
2976
+ </div>
2977
+
2978
+ {/* HEADLINE */}
2979
+ <div
2980
+ style={{
2981
+ position: "absolute",
2982
+ left: SAFE_L,
2983
+ right: SAFE_R,
2984
+ top: 380,
2985
+ opacity: fadeIn(frame, 8, 14),
2986
+ transform: `translateY(${tween(frame, 8, 18, 26, 0)}px)`,
2987
+ }}
2988
+ >
2989
+ <div
2990
+ style={{
2991
+ fontFamily: FONT,
2992
+ fontSize: 96,
2993
+ fontWeight: 800,
2994
+ letterSpacing: "-0.05em",
2995
+ lineHeight: 0.92,
2996
+ color: C.fg,
2997
+ }}
2998
+ >
2999
+ Personal
3000
+ <br />
3001
+ use{" "}
3002
+ <span style={{ color: C.amber }}>only.</span>
3003
+ </div>
3004
+ </div>
3005
+
3006
+ {/* CHECKLIST CARD */}
3007
+ <div
3008
+ style={{
3009
+ position: "absolute",
3010
+ left: SAFE_L,
3011
+ right: SAFE_R,
3012
+ top: 760,
3013
+ opacity: cardOp,
3014
+ transform: `translateY(${cardY}px)`,
3015
+ background: C.surface,
3016
+ border: `1px solid ${C.borderLoud}`,
3017
+ borderRadius: 22,
3018
+ padding: "30px 28px",
3019
+ boxShadow:
3020
+ "0 24px 60px -18px rgba(0,0,0,0.7), inset 0 1px 0 rgba(255,255,255,0.08)",
3021
+ }}
3022
+ >
3023
+ <div
3024
+ style={{
3025
+ fontFamily: MONO,
3026
+ fontSize: 14,
3027
+ letterSpacing: "0.18em",
3028
+ color: C.fgDim,
3029
+ fontWeight: 600,
3030
+ marginBottom: 22,
3031
+ }}
3032
+ >
3033
+ ./LICENSE.md
3034
+ </div>
3035
+
3036
+ {checks.map((c) => {
3037
+ const s = sp(frame, fps, c.delay, "snappy");
3038
+ const op = interpolate(s, [0, 0.5], [0, 1], {
3039
+ extrapolateLeft: "clamp",
3040
+ extrapolateRight: "clamp",
3041
+ });
3042
+ const x = interpolate(s, [0, 1], [-30, 0]);
3043
+ return (
3044
+ <div
3045
+ key={c.text}
3046
+ style={{
3047
+ opacity: op,
3048
+ transform: `translateX(${x}px)`,
3049
+ display: "flex",
3050
+ alignItems: "center",
3051
+ gap: 16,
3052
+ padding: "14px 0",
3053
+ borderBottom: `1px solid ${C.border}`,
3054
+ }}
3055
+ >
3056
+ <div
3057
+ style={{
3058
+ width: 32,
3059
+ height: 32,
3060
+ borderRadius: 999,
3061
+ background: c.ok
3062
+ ? "rgba(0,210,148,0.14)"
3063
+ : "rgba(239,68,68,0.14)",
3064
+ border: c.ok
3065
+ ? `1px solid rgba(0,210,148,0.45)`
3066
+ : `1px solid rgba(239,68,68,0.45)`,
3067
+ display: "flex",
3068
+ alignItems: "center",
3069
+ justifyContent: "center",
3070
+ }}
3071
+ >
3072
+ {c.ok ? (
3073
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke={C.emerald} strokeWidth="3" strokeLinecap="round" strokeLinejoin="round">
3074
+ <path d="M5 12l5 5L20 7" />
3075
+ </svg>
3076
+ ) : (
3077
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke={C.red} strokeWidth="3" strokeLinecap="round">
3078
+ <path d="M6 6l12 12M18 6L6 18" />
3079
+ </svg>
3080
+ )}
3081
+ </div>
3082
+ <div
3083
+ style={{
3084
+ fontFamily: FONT,
3085
+ fontSize: 28,
3086
+ fontWeight: 500,
3087
+ color: c.ok ? C.fg : C.fgMuted,
3088
+ textDecoration: c.ok ? "none" : "line-through",
3089
+ textDecorationColor: C.red,
3090
+ letterSpacing: "-0.01em",
3091
+ }}
3092
+ >
3093
+ {c.text}
3094
+ </div>
3095
+ </div>
3096
+ );
3097
+ })}
3098
+ </div>
3099
+
3100
+ {/* README pointer */}
3101
+ <div
3102
+ style={{
3103
+ position: "absolute",
3104
+ left: SAFE_L,
3105
+ right: SAFE_R,
3106
+ top: 1300,
3107
+ opacity: readmeOp,
3108
+ transform: `scale(${readmeScale})`,
3109
+ transformOrigin: "left center",
3110
+ }}
3111
+ >
3112
+ <div
3113
+ style={{
3114
+ fontFamily: FONT,
3115
+ fontSize: 34,
3116
+ fontWeight: 700,
3117
+ color: C.fg,
3118
+ letterSpacing: "-0.02em",
3119
+ lineHeight: 1.15,
3120
+ }}
3121
+ >
3122
+ Read the{" "}
3123
+ <span
3124
+ style={{
3125
+ fontFamily: MONO,
3126
+ padding: "4px 10px",
3127
+ borderRadius: 8,
3128
+ background: "rgba(249,156,0,0.14)",
3129
+ color: C.amber,
3130
+ border: `1px solid rgba(249,156,0,0.35)`,
3131
+ }}
3132
+ >
3133
+ README
3134
+ </span>{" "}
3135
+ before you ship client work with it.
3136
+ </div>
3137
+ </div>
3138
+ </AbsoluteFill>
3139
+ );
3140
+ };
3141
+
3142
+ // ═══════════════════════════════════════════════════════════════════════════
3143
+ // SCENE 11 — CTA · "Comment 'AI'"
3144
+ // ═══════════════════════════════════════════════════════════════════════════
3145
+
3146
+ const Scene11: React.FC = () => {
3147
+ const frame = useCurrentFrame();
3148
+ const { fps } = useVideoConfig();
3149
+
3150
+ const kickerOp = fadeIn(frame, 0, 14);
3151
+ const kickerY = tween(frame, 0, 18, 22, 0);
3152
+
3153
+ // "COMMENT" letter cascade
3154
+ // "AI" big bouncy pop
3155
+
3156
+ const aiSpring = sp(frame, fps, 0.65, "bouncy");
3157
+ const aiOp = interpolate(aiSpring, [0, 0.5], [0, 1], {
3158
+ extrapolateLeft: "clamp",
3159
+ extrapolateRight: "clamp",
3160
+ });
3161
+ const aiScale = interpolate(aiSpring, [0, 0.55, 1], [0.4, 1.18, 1]);
3162
+ const aiRot = interpolate(aiSpring, [0, 0.6, 1], [-12, 4, 0]);
3163
+
3164
+ // perpetual breath on AI
3165
+ const aiBreath = 1 + Math.sin(frame * 0.08) * 0.02;
3166
+
3167
+ const subOp = fadeIn(frame, 60, 14);
3168
+ const subY = tween(frame, 60, 22, 24, 0);
3169
+
3170
+ const repoSpring = sp(frame, fps, 3.1, "snappy");
3171
+ const repoOp = interpolate(repoSpring, [0, 0.5], [0, 1], {
3172
+ extrapolateLeft: "clamp",
3173
+ extrapolateRight: "clamp",
3174
+ });
3175
+ const repoY = interpolate(repoSpring, [0, 1], [40, 0]);
3176
+
3177
+ // jail bars sweep out
3178
+ const finalOp = fadeIn(frame, 150, 14);
3179
+ const finalY = tween(frame, 150, 22, 28, 0);
3180
+
3181
+ return (
3182
+ <AbsoluteFill style={{ fontFamily: FONT }}>
3183
+ <DarkBg frame={frame} tint="amber" />
3184
+
3185
+ {/* TOP — speech bubble pill */}
3186
+ <div
3187
+ style={{
3188
+ position: "absolute",
3189
+ top: SAFE_TOP,
3190
+ left: SAFE_L,
3191
+ right: SAFE_R,
3192
+ opacity: kickerOp,
3193
+ transform: `translateY(${kickerY}px)`,
3194
+ }}
3195
+ >
3196
+ <Pill
3197
+ bg="rgba(249,156,0,0.10)"
3198
+ fg={C.amberSoft}
3199
+ border="rgba(249,156,0,0.32)"
3200
+ dot={C.amber}
3201
+ >
3202
+ DROP A COMMENT
3203
+ </Pill>
3204
+ </div>
3205
+
3206
+ {/* MASSIVE "AI" with COMMENT prefix */}
3207
+ <div
3208
+ style={{
3209
+ position: "absolute",
3210
+ left: SAFE_L,
3211
+ right: SAFE_R,
3212
+ top: 420,
3213
+ }}
3214
+ >
3215
+ <div
3216
+ style={{
3217
+ fontFamily: MONO,
3218
+ fontSize: 30,
3219
+ color: C.fgMuted,
3220
+ letterSpacing: "0.18em",
3221
+ fontWeight: 600,
3222
+ marginBottom: 8,
3223
+ opacity: fadeIn(frame, 10, 12),
3224
+ }}
3225
+ >
3226
+ Comment
3227
+ </div>
3228
+
3229
+ <div
3230
+ style={{
3231
+ fontFamily: FONT,
3232
+ fontSize: 420,
3233
+ fontWeight: 900,
3234
+ letterSpacing: "-0.07em",
3235
+ lineHeight: 0.84,
3236
+ color: C.amber,
3237
+ opacity: aiOp,
3238
+ transform: `scale(${aiScale * aiBreath}) rotate(${aiRot}deg)`,
3239
+ transformOrigin: "left center",
3240
+ textShadow: `0 0 80px ${C.amberGlow}`,
3241
+ }}
3242
+ >
3243
+ AI
3244
+ </div>
3245
+
3246
+ <div
3247
+ style={{
3248
+ marginTop: 6,
3249
+ opacity: subOp,
3250
+ transform: `translateY(${subY}px)`,
3251
+ fontFamily: FONT,
3252
+ fontSize: 36,
3253
+ fontWeight: 600,
3254
+ color: C.fg,
3255
+ letterSpacing: "-0.02em",
3256
+ lineHeight: 1.18,
3257
+ maxWidth: 920,
3258
+ }}
3259
+ >
3260
+ → I&apos;ll send the repo + a starter prompt.
3261
+ </div>
3262
+ </div>
3263
+
3264
+ {/* repo card */}
3265
+ <div
3266
+ style={{
3267
+ position: "absolute",
3268
+ left: SAFE_L,
3269
+ right: SAFE_R,
3270
+ top: 1140,
3271
+ opacity: repoOp,
3272
+ transform: `translateY(${repoY}px)`,
3273
+ }}
3274
+ >
3275
+ <div
3276
+ style={{
3277
+ background: C.surface,
3278
+ border: `1px solid ${C.borderLoud}`,
3279
+ borderRadius: 18,
3280
+ padding: "18px 22px",
3281
+ display: "flex",
3282
+ alignItems: "center",
3283
+ gap: 14,
3284
+ boxShadow:
3285
+ "0 18px 40px -14px rgba(0,0,0,0.6), inset 0 1px 0 rgba(255,255,255,0.06)",
3286
+ }}
3287
+ >
3288
+ <svg width="32" height="32" viewBox="0 0 24 24" fill={C.fg}>
3289
+ <path d="M12 0C5.37 0 0 5.37 0 12c0 5.3 3.44 9.79 8.21 11.39.6.11.82-.26.82-.58v-2.04c-3.34.73-4.04-1.61-4.04-1.61-.55-1.39-1.34-1.76-1.34-1.76-1.09-.74.08-.73.08-.73 1.21.09 1.85 1.24 1.85 1.24 1.07 1.84 2.81 1.31 3.5 1 .11-.78.42-1.31.76-1.61-2.66-.3-5.46-1.33-5.46-5.93 0-1.31.47-2.38 1.24-3.22-.13-.31-.54-1.53.11-3.18 0 0 1.01-.32 3.31 1.23a11.5 11.5 0 0 1 6.02 0c2.3-1.55 3.31-1.23 3.31-1.23.65 1.65.24 2.87.12 3.18.77.84 1.24 1.91 1.24 3.22 0 4.61-2.81 5.62-5.48 5.92.43.37.81 1.1.81 2.22v3.29c0 .32.22.7.83.58A12 12 0 0 0 24 12c0-6.63-5.37-12-12-12z" />
3290
+ </svg>
3291
+ <div
3292
+ style={{
3293
+ fontFamily: MONO,
3294
+ fontSize: 22,
3295
+ fontWeight: 600,
3296
+ color: C.fg,
3297
+ }}
3298
+ >
3299
+ github.com/alchaincyf/huashu-design
3300
+ </div>
3301
+ </div>
3302
+ </div>
3303
+
3304
+ {/* FINAL line — way out */}
3305
+ <div
3306
+ style={{
3307
+ position: "absolute",
3308
+ left: SAFE_L,
3309
+ right: SAFE_R,
3310
+ top: 1280,
3311
+ opacity: finalOp,
3312
+ transform: `translateY(${finalY}px)`,
3313
+ }}
3314
+ >
3315
+ <div
3316
+ style={{
3317
+ fontFamily: FONT,
3318
+ fontSize: 38,
3319
+ fontWeight: 700,
3320
+ letterSpacing: "-0.025em",
3321
+ lineHeight: 1.1,
3322
+ color: C.fg,
3323
+ }}
3324
+ >
3325
+ Stuck in Claude Design{" "}
3326
+ <span
3327
+ style={{
3328
+ position: "relative",
3329
+ padding: "0 6px",
3330
+ color: C.red,
3331
+ fontWeight: 800,
3332
+ }}
3333
+ >
3334
+ usage jail
3335
+ <span
3336
+ style={{
3337
+ position: "absolute",
3338
+ left: 0,
3339
+ right: 0,
3340
+ bottom: 6,
3341
+ height: 4,
3342
+ background: C.red,
3343
+ opacity: 0.35,
3344
+ }}
3345
+ />
3346
+ </span>
3347
+ ?
3348
+ <br />
3349
+ <span style={{ color: C.amber }}>This is your way out.</span>
3350
+ </div>
3351
+ </div>
3352
+ </AbsoluteFill>
3353
+ );
3354
+ };
3355
+
3356
+ // ═══════════════════════════════════════════════════════════════════════════
3357
+ // MAIN COMPOSITION
3358
+ // ═══════════════════════════════════════════════════════════════════════════
3359
+
3360
+ export const HuashuReel: React.FC = () => {
3361
+ return (
3362
+ <AbsoluteFill
3363
+ style={{
3364
+ background: C.bg,
3365
+ fontFamily: FONT,
3366
+ color: C.fg,
3367
+ }}
3368
+ >
3369
+ {/* VOICEOVER — runs the entire composition */}
3370
+ {/* REFERENCE-STRIP: <Audio> tag removed — bring your own voiceover */}
3371
+
3372
+ <Sequence from={SCENES.S1.from} durationInFrames={SCENES.S1.dur}>
3373
+ <Scene1 />
3374
+ </Sequence>
3375
+ <Sequence from={SCENES.S2.from} durationInFrames={SCENES.S2.dur}>
3376
+ <Scene2 />
3377
+ </Sequence>
3378
+ <Sequence from={SCENES.S3.from} durationInFrames={SCENES.S3.dur}>
3379
+ <Scene3 />
3380
+ </Sequence>
3381
+ <Sequence from={SCENES.S4.from} durationInFrames={SCENES.S4.dur}>
3382
+ <Scene4 />
3383
+ </Sequence>
3384
+ <Sequence from={SCENES.S5.from} durationInFrames={SCENES.S5.dur}>
3385
+ <Scene5 />
3386
+ </Sequence>
3387
+ <Sequence from={SCENES.S6.from} durationInFrames={SCENES.S6.dur}>
3388
+ <Scene6 />
3389
+ </Sequence>
3390
+ <Sequence from={SCENES.S7.from} durationInFrames={SCENES.S7.dur}>
3391
+ <Scene7 />
3392
+ </Sequence>
3393
+ <Sequence from={SCENES.S8.from} durationInFrames={SCENES.S8.dur}>
3394
+ <Scene8 />
3395
+ </Sequence>
3396
+ <Sequence from={SCENES.S9.from} durationInFrames={SCENES.S9.dur}>
3397
+ <Scene9 />
3398
+ </Sequence>
3399
+ <Sequence from={SCENES.S10.from} durationInFrames={SCENES.S10.dur}>
3400
+ <Scene10 />
3401
+ </Sequence>
3402
+ <Sequence from={SCENES.S11.from} durationInFrames={SCENES.S11.dur}>
3403
+ <Scene11 />
3404
+ </Sequence>
3405
+
3406
+ {/* SUBTLE GRAIN — global */}
3407
+ <NoiseGrain opacity={0.04} />
3408
+ </AbsoluteFill>
3409
+ );
3410
+ };
3411
+
3412
+ // keep power2Out referenced to avoid unused-import lint (export reusable curve)
3413
+ export const huashuEasings = { expoOut, power2Out };