@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,2909 @@
1
+ /**
2
+ * REFERENCE — MemPalaceReel (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/MemPalaceReel.tsx
15
+ * Bundled at: 2026-05-08T18:50:39.538Z
16
+ */
17
+ import React from "react";
18
+ import {
19
+ AbsoluteFill,
20
+ Audio,
21
+ Easing,
22
+ Img,
23
+ OffthreadVideo,
24
+ Sequence,
25
+ interpolate,
26
+ spring,
27
+ staticFile,
28
+ useCurrentFrame,
29
+ useVideoConfig,
30
+ } from "remotion";
31
+ import { ds } from "./designSystem";
32
+
33
+ // ═══════════════════════════════════════════════════════════════════════════
34
+ // CONSTANTS — 9:16 reel, 30 fps, ~84 s
35
+ // ═══════════════════════════════════════════════════════════════════════════
36
+
37
+ const H = 1920;
38
+
39
+ // IG safe zones per project memory: top ~280, bottom ~410
40
+ const SAFE_TOP = 280;
41
+ const SAFE_BOT_Y = 1510;
42
+
43
+ export const MEMPALACE_TOTAL = 2520;
44
+
45
+ const SCENES = {
46
+ S1: { from: 0, dur: 225 }, // 7.5s — cold-open hook
47
+ S2: { from: 225, dur: 330 }, // 11s — pain (Gone.)
48
+ S3: { from: 555, dur: 225 }, // 7.5s — Mem0 / "AI decides" wall
49
+ S4: { from: 780, dur: 135 }, // 4.5s — MemPalace flip
50
+ S5: { from: 915, dur: 480 }, // 16s — Architecture (Aristotle/Cicero, wings/rooms/drawers)
51
+ S6: { from: 1395, dur: 420 }, // 14s — Hook magic + screen recording
52
+ S7: { from: 1815, dur: 390 }, // 13s — Social proof
53
+ S8: { from: 2205, dur: 315 }, // 10.5s— CTA
54
+ } as const;
55
+
56
+ // Palette — dark zinc base + warm parchment palace + emerald success.
57
+ // One accent rule (palace gold) keeps the LLM-generic look out.
58
+ const C = {
59
+ bg: "#09090b",
60
+ bgLift: "#111114",
61
+ surface: "#15151c",
62
+ border: "rgba(255,255,255,0.08)",
63
+ borderLoud: "rgba(255,255,255,0.16)",
64
+ fg: "#fafafa",
65
+ fgSoft: "#d4d4d8",
66
+ fgMuted: "#9f9fa9",
67
+ fgDim: "#6b6b75",
68
+
69
+ // Palace warm tones
70
+ paper: "#efe7d6",
71
+ paperWarm: "#e7dcc4",
72
+ paperDeep: "#d8c8a0",
73
+ ink: "#1a1410",
74
+ inkSoft: "#3a2f24",
75
+ gold: "#b8893d",
76
+ goldDeep: "#8a6429",
77
+ goldGlow: "rgba(184,137,61,0.22)",
78
+
79
+ // Tech accents
80
+ emerald: "#00d294",
81
+ emeraldDeep: "#00875a",
82
+ indigo: "#625fff",
83
+ red: "#ef4444",
84
+ redDeep: "#7f1d1d",
85
+ } as const;
86
+
87
+ const FONT = ds.font.sans;
88
+ const MONO = ds.font.mono;
89
+ const SERIF = "ui-serif, 'Iowan Old Style', 'Charter', 'Source Serif Pro', Georgia, serif";
90
+
91
+ // ═══════════════════════════════════════════════════════════════════════════
92
+ // MOTION HELPERS — GSAP-style timeline ergonomics on top of Remotion springs
93
+ // ═══════════════════════════════════════════════════════════════════════════
94
+
95
+ type SpringKind = "smooth" | "snappy" | "gentle" | "bouncy" | "glass";
96
+
97
+ const sp = (
98
+ frame: number,
99
+ fps: number,
100
+ delaySec: number,
101
+ kind: SpringKind = "snappy",
102
+ ) =>
103
+ spring({ frame, fps, delay: Math.round(delaySec * fps), config: ds.spring[kind] });
104
+
105
+ const fadeIn = (f: number, at: number, len = 12) =>
106
+ interpolate(f, [at, at + len], [0, 1], {
107
+ extrapolateLeft: "clamp",
108
+ extrapolateRight: "clamp",
109
+ });
110
+
111
+ const fadeOut = (f: number, at: number, len = 12) =>
112
+ interpolate(f, [at, at + len], [1, 0], {
113
+ extrapolateLeft: "clamp",
114
+ extrapolateRight: "clamp",
115
+ });
116
+
117
+ // GSAP power3.out feel
118
+ const expoOut = Easing.bezier(0.16, 1, 0.3, 1);
119
+
120
+ // "tween" — like gsap.to({duration, ease}) — clamp by default
121
+ const tween = (
122
+ f: number,
123
+ startFrame: number,
124
+ durFrames: number,
125
+ from: number,
126
+ to: number,
127
+ easing: ReturnType<typeof Easing.bezier> = expoOut,
128
+ ) =>
129
+ interpolate(f, [startFrame, startFrame + durFrames], [from, to], {
130
+ extrapolateLeft: "clamp",
131
+ extrapolateRight: "clamp",
132
+ easing,
133
+ });
134
+
135
+ // Letter-by-letter stagger helper — returns per-char {opacity, y}
136
+ const letterStagger = (
137
+ chars: string,
138
+ frame: number,
139
+ fps: number,
140
+ startSec: number,
141
+ perCharSec = 0.04,
142
+ ) =>
143
+ chars.split("").map((ch, i) => {
144
+ const s = sp(frame, fps, startSec + i * perCharSec, "snappy");
145
+ return {
146
+ ch,
147
+ opacity: interpolate(s, [0, 0.4], [0, 1], {
148
+ extrapolateLeft: "clamp",
149
+ extrapolateRight: "clamp",
150
+ }),
151
+ y: interpolate(s, [0, 1], [40, 0]),
152
+ scale: interpolate(s, [0, 0.6, 1], [0.85, 1.05, 1]),
153
+ };
154
+ });
155
+
156
+ // ═══════════════════════════════════════════════════════════════════════════
157
+ // BACKGROUNDS
158
+ // ═══════════════════════════════════════════════════════════════════════════
159
+
160
+ const NoiseGrain: React.FC<{ opacity?: number }> = ({ opacity = 0.06 }) => (
161
+ <AbsoluteFill
162
+ style={{
163
+ pointerEvents: "none",
164
+ opacity,
165
+ mixBlendMode: "overlay",
166
+ 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>")`,
167
+ }}
168
+ />
169
+ );
170
+
171
+ const Vignette: React.FC<{ frame: number; tone?: "dark" | "warm" }> = ({
172
+ frame,
173
+ tone = "dark",
174
+ }) => {
175
+ const pulse = 0.42 + Math.sin(frame * 0.012) * 0.05;
176
+ const col =
177
+ tone === "warm"
178
+ ? `rgba(80,55,15,${pulse})`
179
+ : `rgba(0,0,0,${pulse})`;
180
+ return (
181
+ <AbsoluteFill
182
+ style={{
183
+ background: `radial-gradient(ellipse at 50% 50%, transparent 38%, ${col} 100%)`,
184
+ }}
185
+ />
186
+ );
187
+ };
188
+
189
+ const DarkBg: React.FC<{ frame: number; tint?: "indigo" | "emerald" | "red" }> = ({
190
+ frame,
191
+ tint = "indigo",
192
+ }) => {
193
+ const drift = (Math.sin(frame * 0.0028) + 1) * 0.5;
194
+ const drift2 = (Math.cos(frame * 0.0021) + 1) * 0.5;
195
+ const tintRgb =
196
+ tint === "indigo"
197
+ ? "98,95,255"
198
+ : tint === "emerald"
199
+ ? "0,210,148"
200
+ : "239,68,68";
201
+ return (
202
+ <AbsoluteFill>
203
+ <div style={{ width: "100%", height: "100%", background: C.bg }} />
204
+ <div
205
+ style={{
206
+ position: "absolute",
207
+ inset: 0,
208
+ background: `radial-gradient(ellipse 800px 1000px at ${20 + drift * 60}% ${15 + drift2 * 40}%, rgba(${tintRgb},0.10) 0%, transparent 60%)`,
209
+ }}
210
+ />
211
+ <div
212
+ style={{
213
+ position: "absolute",
214
+ inset: 0,
215
+ background: `radial-gradient(ellipse 700px 900px at ${85 - drift * 55}% ${78 + drift2 * 15}%, rgba(${tintRgb},0.05) 0%, transparent 55%)`,
216
+ }}
217
+ />
218
+ {/* fine grid */}
219
+ <div
220
+ style={{
221
+ position: "absolute",
222
+ inset: 0,
223
+ backgroundImage: `linear-gradient(rgba(255,255,255,0.035) 1px, transparent 1px), linear-gradient(90deg, rgba(255,255,255,0.035) 1px, transparent 1px)`,
224
+ backgroundSize: "72px 72px",
225
+ maskImage: "radial-gradient(ellipse at 50% 50%, black 30%, transparent 85%)",
226
+ WebkitMaskImage: "radial-gradient(ellipse at 50% 50%, black 30%, transparent 85%)",
227
+ }}
228
+ />
229
+ <NoiseGrain />
230
+ <Vignette frame={frame} tone="dark" />
231
+ </AbsoluteFill>
232
+ );
233
+ };
234
+
235
+ const PaperBg: React.FC<{ frame: number }> = ({ frame }) => {
236
+ const drift = (Math.sin(frame * 0.0025) + 1) * 0.5;
237
+ return (
238
+ <AbsoluteFill>
239
+ <div style={{ width: "100%", height: "100%", background: C.paper }} />
240
+ <div
241
+ style={{
242
+ position: "absolute",
243
+ inset: 0,
244
+ background: `radial-gradient(ellipse 900px 1200px at ${30 + drift * 40}% 20%, rgba(255,245,220,0.7) 0%, transparent 60%)`,
245
+ }}
246
+ />
247
+ <div
248
+ style={{
249
+ position: "absolute",
250
+ inset: 0,
251
+ background: `radial-gradient(ellipse 700px 700px at 80% 90%, ${C.paperDeep} 0%, transparent 55%)`,
252
+ opacity: 0.55,
253
+ }}
254
+ />
255
+ {/* horizontal blueprint lines */}
256
+ <div
257
+ style={{
258
+ position: "absolute",
259
+ inset: 0,
260
+ backgroundImage: `linear-gradient(rgba(58,47,36,0.06) 1px, transparent 1px)`,
261
+ backgroundSize: "100% 56px",
262
+ maskImage: "linear-gradient(to bottom, transparent 0%, black 12%, black 88%, transparent 100%)",
263
+ WebkitMaskImage: "linear-gradient(to bottom, transparent 0%, black 12%, black 88%, transparent 100%)",
264
+ }}
265
+ />
266
+ {/* paper grain */}
267
+ <div
268
+ style={{
269
+ position: "absolute",
270
+ inset: 0,
271
+ opacity: 0.18,
272
+ mixBlendMode: "multiply",
273
+ backgroundImage: `url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='240' height='240'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.72'/></filter><rect width='240' height='240' filter='url(%23n)' opacity='0.55'/></svg>")`,
274
+ }}
275
+ />
276
+ <Vignette frame={frame} tone="warm" />
277
+ </AbsoluteFill>
278
+ );
279
+ };
280
+
281
+ // ═══════════════════════════════════════════════════════════════════════════
282
+ // REUSABLE PRIMITIVES
283
+ // ═══════════════════════════════════════════════════════════════════════════
284
+
285
+ const Pill: React.FC<{
286
+ children: React.ReactNode;
287
+ bg: string;
288
+ fg: string;
289
+ border?: string;
290
+ dot?: string;
291
+ mono?: boolean;
292
+ size?: number;
293
+ }> = ({ children, bg, fg, border, dot, mono = true, size = 22 }) => (
294
+ <div
295
+ style={{
296
+ display: "inline-flex",
297
+ alignItems: "center",
298
+ gap: 12,
299
+ padding: "12px 22px",
300
+ borderRadius: 9999,
301
+ background: bg,
302
+ border: border ? `1px solid ${border}` : undefined,
303
+ color: fg,
304
+ fontFamily: mono ? MONO : FONT,
305
+ fontSize: size,
306
+ fontWeight: 600,
307
+ letterSpacing: "0.08em",
308
+ }}
309
+ >
310
+ {dot ? (
311
+ <span
312
+ style={{
313
+ width: 8,
314
+ height: 8,
315
+ borderRadius: 999,
316
+ background: dot,
317
+ boxShadow: `0 0 12px ${dot}`,
318
+ }}
319
+ />
320
+ ) : null}
321
+ {children}
322
+ </div>
323
+ );
324
+
325
+ // ═══════════════════════════════════════════════════════════════════════════
326
+ // SCENE 1 — COLD OPEN HOOK
327
+ // "Your Claude Code forgets your entire project every session.
328
+ // This 1-line install gives it permanent memory — and it's free."
329
+ // ═══════════════════════════════════════════════════════════════════════════
330
+
331
+ const Scene1: React.FC = () => {
332
+ const frame = useCurrentFrame();
333
+ const { fps } = useVideoConfig();
334
+
335
+ // Terminal window slides up
336
+ const winSpring = sp(frame, fps, 0.05, "snappy");
337
+ const winY = interpolate(winSpring, [0, 1], [60, 0]);
338
+ const winOp = interpolate(winSpring, [0, 0.5], [0, 1], {
339
+ extrapolateLeft: "clamp",
340
+ extrapolateRight: "clamp",
341
+ });
342
+
343
+ // Typewriter — "How do we handle auth tokens?"
344
+ const typed = "> How do we handle auth tokens?";
345
+ const startTypeF = 12;
346
+ const charPerFrame = 1.4;
347
+ const charsShown = Math.max(
348
+ 0,
349
+ Math.min(typed.length, Math.floor((frame - startTypeF) * charPerFrame)),
350
+ );
351
+
352
+ // After ~2.6s — single deterministic glitch beat instead of per-frame strobe
353
+ const expireAt = 78;
354
+ // Two short dropouts at expireAt+2 and expireAt+10, each lasting 2 frames.
355
+ // Looks like a CRT signal hiccup, not a flickering bulb.
356
+ const inDropout =
357
+ (frame >= expireAt + 2 && frame < expireAt + 4) ||
358
+ (frame >= expireAt + 10 && frame < expireAt + 12);
359
+ const flicker = inDropout ? 0.55 : 1;
360
+ const expiredOp = fadeIn(frame, expireAt + 18, 8);
361
+ const expiredY = interpolate(frame, [expireAt + 18, expireAt + 30], [10, 0], {
362
+ extrapolateLeft: "clamp",
363
+ extrapolateRight: "clamp",
364
+ });
365
+
366
+ // Headline slam — at 4.0s (frame 120)
367
+ const slamAt = 120;
368
+ const slamSpring = sp(frame, fps, slamAt / fps, "bouncy");
369
+ const slamScale = interpolate(slamSpring, [0, 0.6, 1], [0.7, 1.06, 1]);
370
+
371
+ // Two lines of headline letter-stagger
372
+ const line1 = "FORGETS EVERY";
373
+
374
+ // Line 2 — "SESSION." gradient slam, animated as a single unit
375
+ // (gradient + WebkitBackgroundClip do not propagate to per-letter child spans,
376
+ // so we don't split it letter-by-letter)
377
+ const line2Spring = sp(frame, fps, slamAt / fps + 0.22, "bouncy");
378
+ const line2Op = interpolate(line2Spring, [0, 0.4], [0, 1], {
379
+ extrapolateLeft: "clamp",
380
+ extrapolateRight: "clamp",
381
+ });
382
+ const line2Y = interpolate(line2Spring, [0, 1], [50, 0]);
383
+ const line2Scale = interpolate(line2Spring, [0, 0.55, 1], [0.7, 1.1, 1]);
384
+
385
+ // CTA strip — "1-line install · permanent memory · free"
386
+ const stripOp = fadeIn(frame, 165);
387
+ const stripY = interpolate(frame, [165, 185], [16, 0], {
388
+ extrapolateLeft: "clamp",
389
+ extrapolateRight: "clamp",
390
+ });
391
+
392
+ const exit = fadeOut(frame, SCENES.S1.dur - 16, 12);
393
+
394
+ return (
395
+ <AbsoluteFill style={{ fontFamily: FONT, opacity: exit }}>
396
+ <DarkBg frame={frame} tint="red" />
397
+
398
+ {/* TOP CONTEXT TAG */}
399
+ <div
400
+ style={{
401
+ position: "absolute",
402
+ top: SAFE_TOP,
403
+ left: 0,
404
+ right: 0,
405
+ display: "flex",
406
+ justifyContent: "center",
407
+ opacity: fadeIn(frame, 0, 10),
408
+ }}
409
+ >
410
+ <Pill
411
+ bg="rgba(239,68,68,0.10)"
412
+ fg="#fca5a5"
413
+ border="rgba(239,68,68,0.35)"
414
+ dot={C.red}
415
+ >
416
+ AMNESIA · CTX RESET
417
+ </Pill>
418
+ </div>
419
+
420
+ {/* TERMINAL WINDOW */}
421
+ <div
422
+ style={{
423
+ position: "absolute",
424
+ left: 60,
425
+ right: 60,
426
+ top: 460,
427
+ opacity: winOp * flicker,
428
+ transform: `translateY(${winY}px)`,
429
+ }}
430
+ >
431
+ <div
432
+ style={{
433
+ background: C.surface,
434
+ border: `1px solid ${C.borderLoud}`,
435
+ borderRadius: 22,
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 style={{ width: 12, height: 12, borderRadius: 999, background: "#ff5f57" }} />
453
+ <div style={{ width: 12, height: 12, borderRadius: 999, background: "#febc2e" }} />
454
+ <div style={{ width: 12, height: 12, borderRadius: 999, background: "#28c840" }} />
455
+ <div
456
+ style={{
457
+ marginLeft: 18,
458
+ fontFamily: MONO,
459
+ fontSize: 18,
460
+ color: C.fgMuted,
461
+ letterSpacing: "0.05em",
462
+ }}
463
+ >
464
+ claude-code · ~/project
465
+ </div>
466
+ </div>
467
+
468
+ {/* body */}
469
+ <div
470
+ style={{
471
+ padding: "32px 28px 60px",
472
+ fontFamily: MONO,
473
+ fontSize: 30,
474
+ lineHeight: 1.6,
475
+ color: C.fgSoft,
476
+ minHeight: 380,
477
+ }}
478
+ >
479
+ <div style={{ color: C.fgDim, marginBottom: 10 }}>
480
+ [session-7423] connected
481
+ </div>
482
+ <div>
483
+ {typed.slice(0, charsShown)}
484
+ {/* cursor — visible only while typing; hides once context is lost */}
485
+ {frame < expireAt ? (
486
+ <span
487
+ style={{
488
+ display: "inline-block",
489
+ width: 14,
490
+ height: 30,
491
+ marginLeft: 4,
492
+ background: C.fg,
493
+ verticalAlign: "-4px",
494
+ // Slower, eased blink: ~530ms cycle, smooth opacity not hard cut
495
+ opacity:
496
+ 0.35 +
497
+ 0.65 *
498
+ Math.pow(
499
+ Math.cos((frame * Math.PI) / 16),
500
+ 2,
501
+ ),
502
+ }}
503
+ />
504
+ ) : null}
505
+ </div>
506
+ {/* expired line */}
507
+ {frame >= expireAt + 6 ? (
508
+ <div
509
+ style={{
510
+ marginTop: 22,
511
+ opacity: expiredOp,
512
+ transform: `translateY(${expiredY}px)`,
513
+ color: C.red,
514
+ fontWeight: 600,
515
+ fontSize: 26,
516
+ letterSpacing: "0.04em",
517
+ }}
518
+ >
519
+ ✗ context lost · 0 memory carried over
520
+ </div>
521
+ ) : null}
522
+ </div>
523
+ </div>
524
+ </div>
525
+
526
+ {/* HEADLINE SLAM */}
527
+ {frame >= slamAt - 4 ? (
528
+ <div
529
+ style={{
530
+ position: "absolute",
531
+ top: 1010,
532
+ left: 0,
533
+ right: 0,
534
+ textAlign: "center",
535
+ padding: "0 50px",
536
+ transform: `scale(${slamScale})`,
537
+ }}
538
+ >
539
+ {/* line 1 */}
540
+ <div
541
+ style={{
542
+ display: "flex",
543
+ justifyContent: "center",
544
+ flexWrap: "nowrap",
545
+ gap: 0,
546
+ fontSize: 100,
547
+ fontWeight: 800,
548
+ color: C.fg,
549
+ letterSpacing: "-0.05em",
550
+ lineHeight: 0.95,
551
+ whiteSpace: "nowrap",
552
+ }}
553
+ >
554
+ {letterStagger(line1, frame, fps, slamAt / fps, 0.025).map((s, i) => (
555
+ <span
556
+ key={`l1-${i}`}
557
+ style={{
558
+ display: "inline-block",
559
+ opacity: s.opacity,
560
+ transform: `translateY(${s.y}px) scale(${s.scale})`,
561
+ width: s.ch === " " ? 24 : "auto",
562
+ }}
563
+ >
564
+ {s.ch === " " ? "\u00A0" : s.ch}
565
+ </span>
566
+ ))}
567
+ </div>
568
+ {/* line 2 — gradient slam (animated as single span) */}
569
+ <div
570
+ style={{
571
+ marginTop: 4,
572
+ opacity: line2Op,
573
+ transform: `translateY(${line2Y}px) scale(${line2Scale})`,
574
+ fontSize: 184,
575
+ fontWeight: 800,
576
+ letterSpacing: "-0.06em",
577
+ lineHeight: 0.95,
578
+ background: `linear-gradient(135deg, ${C.red} 0%, #fb7185 100%)`,
579
+ WebkitBackgroundClip: "text",
580
+ backgroundClip: "text",
581
+ WebkitTextFillColor: "transparent",
582
+ filter: "drop-shadow(0 0 32px rgba(239,68,68,0.45))",
583
+ }}
584
+ >
585
+ SESSION.
586
+ </div>
587
+ </div>
588
+ ) : null}
589
+
590
+ {/* INSTALL CTA STRIP */}
591
+ <div
592
+ style={{
593
+ position: "absolute",
594
+ left: 0,
595
+ right: 0,
596
+ bottom: H - SAFE_BOT_Y + 50,
597
+ display: "flex",
598
+ justifyContent: "center",
599
+ opacity: stripOp,
600
+ transform: `translateY(${stripY}px)`,
601
+ }}
602
+ >
603
+ <div
604
+ style={{
605
+ display: "flex",
606
+ alignItems: "center",
607
+ gap: 18,
608
+ padding: "20px 30px",
609
+ background: "rgba(255,255,255,0.05)",
610
+ border: `1px solid ${C.borderLoud}`,
611
+ borderRadius: 999,
612
+ backdropFilter: "blur(20px)",
613
+ fontFamily: MONO,
614
+ fontSize: 26,
615
+ fontWeight: 600,
616
+ color: C.fg,
617
+ letterSpacing: "0.04em",
618
+ }}
619
+ >
620
+ <span style={{ color: C.emerald }}>$</span>
621
+ <span>1 line · permanent memory · free</span>
622
+ </div>
623
+ </div>
624
+ </AbsoluteFill>
625
+ );
626
+ };
627
+
628
+ // ═══════════════════════════════════════════════════════════════════════════
629
+ // SCENE 2 — WHAT'S LOST
630
+ // "every decision, every back-and-forth, every reason behind your reasoning?
631
+ // Gone. The second you start a new session."
632
+ // ═══════════════════════════════════════════════════════════════════════════
633
+
634
+ const FloatingMemoryCard: React.FC<{
635
+ frame: number;
636
+ index: number;
637
+ startFrame: number;
638
+ shredAt: number;
639
+ label: string;
640
+ body: string;
641
+ x: number;
642
+ y: number;
643
+ rot: number;
644
+ tint: string;
645
+ }> = ({ frame, index, startFrame, shredAt, label, body, x, y, rot, tint }) => {
646
+ // Enter
647
+ const enterT = tween(frame, startFrame + index * 8, 24, 0, 1);
648
+ const op = enterT;
649
+ const fy = (Math.sin((frame + index * 14) * 0.04) - 0.5) * 8;
650
+
651
+ // Shred
652
+ const shredT = tween(frame, shredAt, 22, 0, 1);
653
+ const shredOp = 1 - shredT;
654
+ const shredX = shredT * (60 + index * 14) * (index % 2 === 0 ? 1 : -1);
655
+ const shredY = shredT * 90;
656
+ const shredScale = 1 - shredT * 0.18;
657
+ const shredBlur = shredT * 6;
658
+
659
+ return (
660
+ <div
661
+ style={{
662
+ position: "absolute",
663
+ left: x,
664
+ top: y + fy,
665
+ width: 540,
666
+ opacity: op * shredOp,
667
+ transform: `translate(${shredX}px, ${shredY}px) rotate(${rot + shredT * (index % 2 === 0 ? 6 : -6)}deg) scale(${shredScale})`,
668
+ filter: `blur(${shredBlur}px)`,
669
+ }}
670
+ >
671
+ <div
672
+ style={{
673
+ background: C.surface,
674
+ border: `1px solid ${C.border}`,
675
+ borderRadius: 18,
676
+ padding: "20px 22px",
677
+ fontFamily: FONT,
678
+ color: C.fgSoft,
679
+ boxShadow: "0 20px 60px -10px rgba(0,0,0,0.55), inset 0 1px 0 rgba(255,255,255,0.05)",
680
+ }}
681
+ >
682
+ <div
683
+ style={{
684
+ display: "flex",
685
+ alignItems: "center",
686
+ gap: 10,
687
+ marginBottom: 12,
688
+ }}
689
+ >
690
+ <div
691
+ style={{
692
+ width: 8,
693
+ height: 8,
694
+ borderRadius: 999,
695
+ background: tint,
696
+ boxShadow: `0 0 10px ${tint}`,
697
+ }}
698
+ />
699
+ <div
700
+ style={{
701
+ fontFamily: MONO,
702
+ fontSize: 17,
703
+ color: tint,
704
+ letterSpacing: "0.1em",
705
+ fontWeight: 600,
706
+ }}
707
+ >
708
+ {label}
709
+ </div>
710
+ </div>
711
+ <div style={{ fontSize: 27, fontWeight: 500, lineHeight: 1.35, color: C.fg }}>
712
+ {body}
713
+ </div>
714
+ </div>
715
+ </div>
716
+ );
717
+ };
718
+
719
+ const Scene2: React.FC = () => {
720
+ const frame = useCurrentFrame();
721
+ const { fps } = useVideoConfig();
722
+
723
+ const titleOp = fadeIn(frame, 0, 14);
724
+ const titleY = interpolate(frame, [0, 18], [22, 0], {
725
+ extrapolateLeft: "clamp",
726
+ extrapolateRight: "clamp",
727
+ });
728
+
729
+ // 4 cards stagger in across ~1s
730
+ // Shred at frame ~210 (when "Gone." hits)
731
+ const shredAt = 210;
732
+
733
+ // GONE slam
734
+ const goneAt = 220;
735
+ const goneSpring = sp(frame, fps, goneAt / fps, "bouncy");
736
+ const goneOp = interpolate(goneSpring, [0, 0.4], [0, 1], {
737
+ extrapolateLeft: "clamp",
738
+ extrapolateRight: "clamp",
739
+ });
740
+ const goneScale = interpolate(goneSpring, [0, 0.5, 1], [0.5, 1.15, 1]);
741
+ const goneRot = interpolate(goneSpring, [0, 1], [-6, -2]);
742
+ const goneShake = goneOp * Math.sin(frame * 0.6) * 1.5;
743
+
744
+ // sub line
745
+ const subOp = fadeIn(frame, 270);
746
+ const subY = interpolate(frame, [270, 295], [18, 0], {
747
+ extrapolateLeft: "clamp",
748
+ extrapolateRight: "clamp",
749
+ });
750
+
751
+ const exit = fadeOut(frame, SCENES.S2.dur - 16, 12);
752
+
753
+ return (
754
+ <AbsoluteFill style={{ fontFamily: FONT, opacity: exit }}>
755
+ <DarkBg frame={frame} tint="red" />
756
+
757
+ {/* Title */}
758
+ <div
759
+ style={{
760
+ position: "absolute",
761
+ top: SAFE_TOP - 10,
762
+ left: 0,
763
+ right: 0,
764
+ textAlign: "center",
765
+ opacity: titleOp,
766
+ transform: `translateY(${titleY}px)`,
767
+ padding: "0 60px",
768
+ }}
769
+ >
770
+ <Pill bg="rgba(255,255,255,0.05)" fg={C.fgSoft} border={C.border}>
771
+ WHAT'S LOST AT SESSION RESET
772
+ </Pill>
773
+ <div
774
+ style={{
775
+ marginTop: 28,
776
+ fontSize: 78,
777
+ fontWeight: 700,
778
+ color: C.fg,
779
+ letterSpacing: "-0.04em",
780
+ lineHeight: 1.02,
781
+ }}
782
+ >
783
+ Every decision.<br />
784
+ <span style={{ color: C.fgMuted, fontWeight: 600 }}>
785
+ Every back-and-forth.
786
+ </span><br />
787
+ <span style={{ color: C.fgDim, fontWeight: 600 }}>
788
+ Every reason behind it.
789
+ </span>
790
+ </div>
791
+ </div>
792
+
793
+ {/* FLOATING MEMORY CARDS */}
794
+ <FloatingMemoryCard
795
+ frame={frame}
796
+ index={0}
797
+ startFrame={50}
798
+ shredAt={shredAt}
799
+ label="DECISION · 14:32"
800
+ body={"\u201CWe chose Postgres over\nDynamo because of joins.\u201D"}
801
+ x={120}
802
+ y={830}
803
+ rot={-3}
804
+ tint={C.indigo}
805
+ />
806
+ <FloatingMemoryCard
807
+ frame={frame}
808
+ index={1}
809
+ startFrame={50}
810
+ shredAt={shredAt}
811
+ label="REASONING · 14:48"
812
+ body={"\u201CRetry on 502 only — 503\nmeans pool is exhausted.\u201D"}
813
+ x={420}
814
+ y={970}
815
+ rot={4}
816
+ tint={C.emerald}
817
+ />
818
+ <FloatingMemoryCard
819
+ frame={frame}
820
+ index={2}
821
+ startFrame={50}
822
+ shredAt={shredAt}
823
+ label="BACK-AND-FORTH · 15:11"
824
+ body={"\u201CDon\u2019t mock the DB in\ntests \u2014 burned us last Q.\u201D"}
825
+ x={130}
826
+ y={1110}
827
+ rot={2}
828
+ tint="#fcbb00"
829
+ />
830
+ <FloatingMemoryCard
831
+ frame={frame}
832
+ index={3}
833
+ startFrame={50}
834
+ shredAt={shredAt}
835
+ label="CONSTRAINT · 15:24"
836
+ body={"\u201CMobile freeze starts\nThu \u2014 critical merges only.\u201D"}
837
+ x={420}
838
+ y={1255}
839
+ rot={-2}
840
+ tint="#fb7185"
841
+ />
842
+
843
+ {/* GONE. — slam */}
844
+ {frame >= goneAt - 4 ? (
845
+ <div
846
+ style={{
847
+ position: "absolute",
848
+ left: 0,
849
+ right: 0,
850
+ top: 1340,
851
+ textAlign: "center",
852
+ opacity: goneOp,
853
+ }}
854
+ >
855
+ <div
856
+ style={{
857
+ display: "inline-block",
858
+ fontSize: 240,
859
+ fontWeight: 800,
860
+ letterSpacing: "-0.07em",
861
+ color: C.fg,
862
+ transform: `scale(${goneScale}) rotate(${goneRot}deg) translateX(${goneShake}px)`,
863
+ filter: "drop-shadow(0 0 60px rgba(239,68,68,0.45))",
864
+ }}
865
+ >
866
+ <span style={{ color: C.red }}>GONE</span>
867
+ <span style={{ color: C.fg }}>.</span>
868
+ </div>
869
+ </div>
870
+ ) : null}
871
+
872
+ {/* sub */}
873
+ <div
874
+ style={{
875
+ position: "absolute",
876
+ left: 0,
877
+ right: 0,
878
+ top: 1420,
879
+ textAlign: "center",
880
+ opacity: subOp,
881
+ transform: `translateY(${subY}px)`,
882
+ fontSize: 30,
883
+ fontWeight: 500,
884
+ color: C.fgMuted,
885
+ letterSpacing: "-0.01em",
886
+ }}
887
+ >
888
+ the second you start a new session
889
+ </div>
890
+ </AbsoluteFill>
891
+ );
892
+ };
893
+
894
+ // ═══════════════════════════════════════════════════════════════════════════
895
+ // SCENE 3 — MEM0 / "AI DECIDES" WALL
896
+ // "Mem zero charges nineteen a month. But they all hit the same wall —
897
+ // they let AI decide what's worth keeping."
898
+ // ═══════════════════════════════════════════════════════════════════════════
899
+
900
+ const Scene3: React.FC = () => {
901
+ const frame = useCurrentFrame();
902
+ const { fps } = useVideoConfig();
903
+
904
+ // Pricing card flip in
905
+ const cardSpring = sp(frame, fps, 0.1, "snappy");
906
+ const cardScale = interpolate(cardSpring, [0, 1], [0.88, 1]);
907
+ const cardOp = interpolate(cardSpring, [0, 0.5], [0, 1], {
908
+ extrapolateLeft: "clamp",
909
+ extrapolateRight: "clamp",
910
+ });
911
+ const cardY = interpolate(cardSpring, [0, 1], [40, 0]);
912
+
913
+ // Price counter — counts up from 0 to 19
914
+ const priceCount = Math.round(tween(frame, 12, 30, 0, 19));
915
+
916
+ // Title
917
+ const titleOp = fadeIn(frame, 0, 12);
918
+
919
+ // VS divider
920
+ const dividerOp = fadeIn(frame, 70);
921
+
922
+ // Right card — "AI decides" with strikethrough
923
+ const rightSpring = sp(frame, fps, 2.4, "snappy");
924
+ const rightOp = interpolate(rightSpring, [0, 0.4], [0, 1], {
925
+ extrapolateLeft: "clamp",
926
+ extrapolateRight: "clamp",
927
+ });
928
+ const rightY = interpolate(rightSpring, [0, 1], [40, 0]);
929
+
930
+ // Strikethrough draws on top of "AI DECIDES"
931
+ const strikeAt = 105;
932
+ const strikeT = tween(frame, strikeAt, 18, 0, 1);
933
+
934
+ // "SAME WALL" — bricks build up
935
+ const wallStartAt = 140;
936
+ const exit = fadeOut(frame, SCENES.S3.dur - 16, 12);
937
+
938
+ return (
939
+ <AbsoluteFill style={{ fontFamily: FONT, opacity: exit }}>
940
+ <DarkBg frame={frame} tint="red" />
941
+
942
+ {/* Title */}
943
+ <div
944
+ style={{
945
+ position: "absolute",
946
+ top: SAFE_TOP,
947
+ left: 0,
948
+ right: 0,
949
+ textAlign: "center",
950
+ opacity: titleOp,
951
+ padding: "0 60px",
952
+ }}
953
+ >
954
+ <Pill bg="rgba(239,68,68,0.10)" fg="#fca5a5" border="rgba(239,68,68,0.30)">
955
+ THE SAME CEILING
956
+ </Pill>
957
+ <div
958
+ style={{
959
+ marginTop: 24,
960
+ fontSize: 64,
961
+ fontWeight: 700,
962
+ color: C.fg,
963
+ letterSpacing: "-0.035em",
964
+ lineHeight: 1.05,
965
+ }}
966
+ >
967
+ They charge you,<br />
968
+ then <span style={{ color: C.red }}>throw most of it away.</span>
969
+ </div>
970
+ </div>
971
+
972
+ {/* PRICING CARD */}
973
+ <div
974
+ style={{
975
+ position: "absolute",
976
+ top: 720,
977
+ left: 80,
978
+ width: 420,
979
+ opacity: cardOp,
980
+ transform: `translateY(${cardY}px) scale(${cardScale})`,
981
+ }}
982
+ >
983
+ <div
984
+ style={{
985
+ background: C.surface,
986
+ border: `1px solid ${C.borderLoud}`,
987
+ borderRadius: 24,
988
+ padding: "32px 30px",
989
+ boxShadow:
990
+ "0 30px 60px -10px rgba(0,0,0,0.6), inset 0 1px 0 rgba(255,255,255,0.06)",
991
+ fontFamily: FONT,
992
+ }}
993
+ >
994
+ <div
995
+ style={{
996
+ fontFamily: MONO,
997
+ fontSize: 18,
998
+ color: C.fgMuted,
999
+ letterSpacing: "0.12em",
1000
+ fontWeight: 600,
1001
+ }}
1002
+ >
1003
+ MEM0 · BASIC
1004
+ </div>
1005
+ <div
1006
+ style={{
1007
+ marginTop: 18,
1008
+ display: "flex",
1009
+ alignItems: "baseline",
1010
+ gap: 6,
1011
+ }}
1012
+ >
1013
+ <span style={{ fontSize: 38, color: C.fgMuted, fontWeight: 600 }}>$</span>
1014
+ <span
1015
+ style={{
1016
+ fontSize: 124,
1017
+ fontWeight: 800,
1018
+ letterSpacing: "-0.06em",
1019
+ color: C.fg,
1020
+ lineHeight: 0.9,
1021
+ }}
1022
+ >
1023
+ {priceCount}
1024
+ </span>
1025
+ <span style={{ fontSize: 30, color: C.fgMuted, marginLeft: 6 }}>/mo</span>
1026
+ </div>
1027
+ <div
1028
+ style={{
1029
+ marginTop: 18,
1030
+ display: "flex",
1031
+ flexDirection: "column",
1032
+ gap: 12,
1033
+ }}
1034
+ >
1035
+ {["AI-curated retention", "What it deems important", "Vendor lock-in"].map(
1036
+ (txt, i) => {
1037
+ const lineT = tween(frame, 50 + i * 8, 18, 0, 1);
1038
+ return (
1039
+ <div
1040
+ key={txt}
1041
+ style={{
1042
+ opacity: lineT,
1043
+ display: "flex",
1044
+ alignItems: "center",
1045
+ gap: 12,
1046
+ fontSize: 22,
1047
+ color: C.fgSoft,
1048
+ fontFamily: MONO,
1049
+ }}
1050
+ >
1051
+ <Img
1052
+ src={staticFile("captures/your-asset.png" /* REFERENCE-STRIP */)}
1053
+ style={{ width: 22, height: 22, filter: "brightness(0) saturate(100%) invert(45%) sepia(85%) saturate(2900%) hue-rotate(335deg)" }}
1054
+ />
1055
+ {txt}
1056
+ </div>
1057
+ );
1058
+ },
1059
+ )}
1060
+ </div>
1061
+ </div>
1062
+ </div>
1063
+
1064
+ {/* VS DIVIDER */}
1065
+ <div
1066
+ style={{
1067
+ position: "absolute",
1068
+ top: 840,
1069
+ left: 510,
1070
+ width: 60,
1071
+ opacity: dividerOp,
1072
+ textAlign: "center",
1073
+ }}
1074
+ >
1075
+ <div
1076
+ style={{
1077
+ fontFamily: MONO,
1078
+ fontWeight: 800,
1079
+ fontSize: 30,
1080
+ color: C.fgMuted,
1081
+ letterSpacing: "0.1em",
1082
+ }}
1083
+ >
1084
+ ·
1085
+ </div>
1086
+ </div>
1087
+
1088
+ {/* RIGHT — "AI DECIDES" stamp */}
1089
+ <div
1090
+ style={{
1091
+ position: "absolute",
1092
+ top: 760,
1093
+ right: 80,
1094
+ width: 420,
1095
+ opacity: rightOp,
1096
+ transform: `translateY(${rightY}px)`,
1097
+ }}
1098
+ >
1099
+ <div
1100
+ style={{
1101
+ fontFamily: MONO,
1102
+ fontSize: 18,
1103
+ color: "#fca5a5",
1104
+ letterSpacing: "0.12em",
1105
+ fontWeight: 600,
1106
+ marginBottom: 18,
1107
+ }}
1108
+ >
1109
+ THE WALL
1110
+ </div>
1111
+ <div
1112
+ style={{
1113
+ position: "relative",
1114
+ display: "inline-block",
1115
+ fontSize: 96,
1116
+ fontWeight: 800,
1117
+ color: C.fg,
1118
+ letterSpacing: "-0.04em",
1119
+ lineHeight: 0.95,
1120
+ }}
1121
+ >
1122
+ AI DECIDES<br />
1123
+ <span style={{ color: C.fgDim }}>WHAT TO KEEP.</span>
1124
+ {/* Red strike line */}
1125
+ <svg
1126
+ width={400}
1127
+ height={220}
1128
+ style={{
1129
+ position: "absolute",
1130
+ top: 0,
1131
+ left: -10,
1132
+ pointerEvents: "none",
1133
+ overflow: "visible",
1134
+ }}
1135
+ >
1136
+ <line
1137
+ x1={20}
1138
+ y1={30}
1139
+ x2={20 + strikeT * 380}
1140
+ y2={30 + strikeT * 170}
1141
+ stroke={C.red}
1142
+ strokeWidth={10}
1143
+ strokeLinecap="round"
1144
+ style={{
1145
+ filter: `drop-shadow(0 0 10px rgba(239,68,68,0.6))`,
1146
+ }}
1147
+ />
1148
+ </svg>
1149
+ </div>
1150
+ </div>
1151
+
1152
+ {/* Wall bricks at bottom */}
1153
+ <div
1154
+ style={{
1155
+ position: "absolute",
1156
+ left: 60,
1157
+ right: 60,
1158
+ top: 1280,
1159
+ height: 230,
1160
+ display: "grid",
1161
+ gridTemplateColumns: "repeat(8, 1fr)",
1162
+ gridTemplateRows: "repeat(4, 1fr)",
1163
+ gap: 6,
1164
+ }}
1165
+ >
1166
+ {Array.from({ length: 32 }).map((_, i) => {
1167
+ const t = tween(frame, wallStartAt + i * 1.6, 14, 0, 1);
1168
+ const offsetRow = Math.floor(i / 8) % 2 === 0 ? 0 : 14;
1169
+ return (
1170
+ <div
1171
+ key={i}
1172
+ style={{
1173
+ background: `rgba(${48 + (i % 4) * 4},${28 + (i % 3) * 2},${28 + (i % 5) * 3},${0.85 * t})`,
1174
+ border: `1px solid rgba(239,68,68,${0.18 * t})`,
1175
+ borderRadius: 4,
1176
+ opacity: t,
1177
+ transform: `translate(${offsetRow}px, ${(1 - t) * -14}px)`,
1178
+ }}
1179
+ />
1180
+ );
1181
+ })}
1182
+ </div>
1183
+ </AbsoluteFill>
1184
+ );
1185
+ };
1186
+
1187
+ // ═══════════════════════════════════════════════════════════════════════════
1188
+ // SCENE 4 — MEMPALACE FLIP
1189
+ // "MemPalace flipped it. It saves everything. No AI deciding."
1190
+ // ═══════════════════════════════════════════════════════════════════════════
1191
+
1192
+ const Scene4: React.FC = () => {
1193
+ const frame = useCurrentFrame();
1194
+ const { fps } = useVideoConfig();
1195
+
1196
+ // Hard cut to PaperBg — feels like flipping a coin from dark to light
1197
+ // Logo emerges with bouncy spring
1198
+ const logoSpring = sp(frame, fps, 0.05, "bouncy");
1199
+ const logoScale = interpolate(logoSpring, [0, 0.55, 1], [0.3, 1.1, 1]);
1200
+ const logoOp = interpolate(logoSpring, [0, 0.45], [0, 1], {
1201
+ extrapolateLeft: "clamp",
1202
+ extrapolateRight: "clamp",
1203
+ });
1204
+ const logoRot = interpolate(logoSpring, [0, 1], [-180, 0]);
1205
+
1206
+ // Wordmark letter-by-letter
1207
+ const wordChars = "MEMPALACE";
1208
+
1209
+ // Underline
1210
+ const underlineT = tween(frame, 50, 20, 0, 1);
1211
+
1212
+ // Tagline: SAVES EVERYTHING
1213
+ const taglineOp = fadeIn(frame, 75);
1214
+ const taglineY = interpolate(frame, [75, 95], [16, 0], {
1215
+ extrapolateLeft: "clamp",
1216
+ extrapolateRight: "clamp",
1217
+ });
1218
+
1219
+ return (
1220
+ <AbsoluteFill style={{ fontFamily: FONT }}>
1221
+ <PaperBg frame={frame} />
1222
+
1223
+ {/* Crest / seal */}
1224
+ <div
1225
+ style={{
1226
+ position: "absolute",
1227
+ top: 540,
1228
+ left: 0,
1229
+ right: 0,
1230
+ display: "flex",
1231
+ justifyContent: "center",
1232
+ opacity: logoOp,
1233
+ transform: `scale(${logoScale}) rotate(${logoRot}deg)`,
1234
+ }}
1235
+ >
1236
+ <svg width={260} height={260} viewBox="0 0 200 200">
1237
+ {/* outer ring */}
1238
+ <circle cx={100} cy={100} r={92} fill="none" stroke={C.gold} strokeWidth={3} />
1239
+ <circle cx={100} cy={100} r={84} fill="none" stroke={C.goldDeep} strokeWidth={1.2} strokeDasharray="2 6" />
1240
+ {/* pediment */}
1241
+ <path
1242
+ d="M40 110 L100 50 L160 110 Z"
1243
+ fill="none"
1244
+ stroke={C.goldDeep}
1245
+ strokeWidth={3}
1246
+ />
1247
+ {/* columns */}
1248
+ <rect x={56} y={110} width={8} height={42} fill={C.goldDeep} />
1249
+ <rect x={84} y={110} width={8} height={42} fill={C.goldDeep} />
1250
+ <rect x={108} y={110} width={8} height={42} fill={C.goldDeep} />
1251
+ <rect x={136} y={110} width={8} height={42} fill={C.goldDeep} />
1252
+ {/* base */}
1253
+ <rect x={48} y={154} width={104} height={6} fill={C.goldDeep} />
1254
+ {/* steps */}
1255
+ <rect x={42} y={162} width={116} height={3} fill={C.gold} />
1256
+ {/* pediment dot */}
1257
+ <circle cx={100} cy={84} r={4} fill={C.gold} />
1258
+ </svg>
1259
+ </div>
1260
+
1261
+ {/* Wordmark */}
1262
+ <div
1263
+ style={{
1264
+ position: "absolute",
1265
+ top: 820,
1266
+ left: 0,
1267
+ right: 0,
1268
+ display: "flex",
1269
+ justifyContent: "center",
1270
+ fontFamily: SERIF,
1271
+ }}
1272
+ >
1273
+ <div
1274
+ style={{
1275
+ display: "flex",
1276
+ fontSize: 140,
1277
+ fontWeight: 700,
1278
+ color: C.ink,
1279
+ letterSpacing: "-0.04em",
1280
+ lineHeight: 1,
1281
+ }}
1282
+ >
1283
+ {letterStagger(wordChars, frame, fps, 0.4, 0.04).map((s, i) => (
1284
+ <span
1285
+ key={`w-${i}`}
1286
+ style={{
1287
+ display: "inline-block",
1288
+ opacity: s.opacity,
1289
+ transform: `translateY(${s.y}px) scale(${s.scale})`,
1290
+ }}
1291
+ >
1292
+ {s.ch}
1293
+ </span>
1294
+ ))}
1295
+ </div>
1296
+ </div>
1297
+
1298
+ {/* Underline */}
1299
+ <div
1300
+ style={{
1301
+ position: "absolute",
1302
+ top: 985,
1303
+ left: 0,
1304
+ right: 0,
1305
+ display: "flex",
1306
+ justifyContent: "center",
1307
+ }}
1308
+ >
1309
+ <div
1310
+ style={{
1311
+ width: underlineT * 540,
1312
+ height: 4,
1313
+ background: `linear-gradient(90deg, transparent, ${C.gold}, transparent)`,
1314
+ borderRadius: 999,
1315
+ }}
1316
+ />
1317
+ </div>
1318
+
1319
+ {/* Tagline */}
1320
+ <div
1321
+ style={{
1322
+ position: "absolute",
1323
+ top: 1080,
1324
+ left: 0,
1325
+ right: 0,
1326
+ textAlign: "center",
1327
+ opacity: taglineOp,
1328
+ transform: `translateY(${taglineY}px)`,
1329
+ padding: "0 80px",
1330
+ }}
1331
+ >
1332
+ <div
1333
+ style={{
1334
+ fontSize: 70,
1335
+ fontWeight: 700,
1336
+ color: C.ink,
1337
+ letterSpacing: "-0.03em",
1338
+ lineHeight: 1.05,
1339
+ }}
1340
+ >
1341
+ Saves <span style={{ color: C.goldDeep, fontStyle: "italic", fontFamily: SERIF }}>everything.</span>
1342
+ </div>
1343
+ <div
1344
+ style={{
1345
+ marginTop: 14,
1346
+ fontSize: 36,
1347
+ color: C.inkSoft,
1348
+ fontWeight: 500,
1349
+ }}
1350
+ >
1351
+ No AI deciding.
1352
+ </div>
1353
+ </div>
1354
+ </AbsoluteFill>
1355
+ );
1356
+ };
1357
+
1358
+ // ═══════════════════════════════════════════════════════════════════════════
1359
+ // SCENE 5 — ARCHITECTURE
1360
+ // "ancient Greek memory palace technique. Aristotle used it. Cicero used it.
1361
+ // Projects become wings. Topics become rooms. Every memory lives word-for-word
1362
+ // in a drawer Claude can open on demand."
1363
+ // ═══════════════════════════════════════════════════════════════════════════
1364
+
1365
+ const ArchitectureDiagram: React.FC<{ frame: number; fps: number }> = ({
1366
+ frame,
1367
+ }) => {
1368
+ // Build layered hierarchy: PALACE → WINGS → ROOMS → DRAWERS
1369
+ // Reveal layer by layer with stagger.
1370
+ const sFacade = tween(frame, 80, 30, 0, 1);
1371
+ const sWings = tween(frame, 130, 26, 0, 1);
1372
+ const sRooms = tween(frame, 180, 28, 0, 1);
1373
+ const sDrawers = tween(frame, 240, 32, 0, 1);
1374
+ const sLines = tween(frame, 200, 26, 0, 1);
1375
+
1376
+ // Subtle window-glow pulse — gives the buildings a "living" feel
1377
+ const winPulse = 0.78 + Math.sin(frame * 0.05) * 0.18;
1378
+ // Animated dash-flow on connectors (signal traveling down the hierarchy)
1379
+ const dashOffset = -(frame * 0.6) % 16;
1380
+
1381
+ // Wing data
1382
+ const wings = [
1383
+ { cx: 160, label: "claude-code" },
1384
+ { cx: 460, label: "my-saas" },
1385
+ { cx: 760, label: "agent-sdk" },
1386
+ ];
1387
+ // Room data
1388
+ const rooms = [
1389
+ { cx: 160, label: "auth" },
1390
+ { cx: 460, label: "billing" },
1391
+ { cx: 760, label: "ui-design" },
1392
+ ];
1393
+ // Drawers — each drawer carries a real memory snippet (echoes Scene 2)
1394
+ // 4 cards × 156 wide + 3 × 18 gap = 678 → centered in 920 (margin 121)
1395
+ const drawers = [
1396
+ { cx: 121, time: "14:32", body: "postgres > dynamo", tag: "joins" },
1397
+ { cx: 295, time: "14:48", body: "retry 502 only", tag: "503 = pool" },
1398
+ { cx: 469, time: "15:11", body: "no DB mocks", tag: "burned us" },
1399
+ { cx: 643, time: "15:24", body: "mobile freeze Thu", tag: "critical" },
1400
+ ];
1401
+
1402
+ return (
1403
+ <svg
1404
+ width={920}
1405
+ height={820}
1406
+ viewBox="0 0 920 820"
1407
+ style={{ position: "absolute", left: 80, top: 580 }}
1408
+ >
1409
+ <defs>
1410
+ {/* Pediment marble gradient */}
1411
+ <linearGradient id="marble" x1="0" y1="0" x2="0" y2="1">
1412
+ <stop offset="0%" stopColor={C.paper} />
1413
+ <stop offset="100%" stopColor={C.paperDeep} />
1414
+ </linearGradient>
1415
+ {/* Column shading: lit on left, shadow on right */}
1416
+ <linearGradient id="column" x1="0" y1="0" x2="1" y2="0">
1417
+ <stop offset="0%" stopColor={C.paper} />
1418
+ <stop offset="55%" stopColor={C.paperWarm} />
1419
+ <stop offset="100%" stopColor={C.paperDeep} />
1420
+ </linearGradient>
1421
+ {/* Building front shading */}
1422
+ <linearGradient id="wall" x1="0" y1="0" x2="1" y2="0">
1423
+ <stop offset="0%" stopColor={C.paper} />
1424
+ <stop offset="100%" stopColor={C.paperWarm} />
1425
+ </linearGradient>
1426
+ {/* Roof front face */}
1427
+ <linearGradient id="roof" x1="0" y1="0" x2="0" y2="1">
1428
+ <stop offset="0%" stopColor={C.gold} />
1429
+ <stop offset="100%" stopColor={C.goldDeep} />
1430
+ </linearGradient>
1431
+ {/* Lit window — gold light glowing from inside */}
1432
+ <radialGradient id="window-glow" cx="0.5" cy="0.5" r="0.7">
1433
+ <stop offset="0%" stopColor="#f9e1a8" />
1434
+ <stop offset="60%" stopColor={C.gold} />
1435
+ <stop offset="100%" stopColor={C.goldDeep} />
1436
+ </radialGradient>
1437
+ {/* Plaque shading */}
1438
+ <linearGradient id="plaque" x1="0" y1="0" x2="0" y2="1">
1439
+ <stop offset="0%" stopColor={C.paper} />
1440
+ <stop offset="100%" stopColor={C.paperDeep} />
1441
+ </linearGradient>
1442
+ {/* Drawer body */}
1443
+ <linearGradient id="drawer-body" x1="0" y1="0" x2="0" y2="1">
1444
+ <stop offset="0%" stopColor={C.paperWarm} />
1445
+ <stop offset="100%" stopColor={C.paperDeep} />
1446
+ </linearGradient>
1447
+ {/* Connector stroke */}
1448
+ <linearGradient id="connector" x1="0" y1="0" x2="0" y2="1">
1449
+ <stop offset="0%" stopColor={C.gold} stopOpacity="0.85" />
1450
+ <stop offset="100%" stopColor={C.goldDeep} stopOpacity="0.55" />
1451
+ </linearGradient>
1452
+
1453
+ {/* Soft cast-shadow filter */}
1454
+ <filter id="soft-shadow" x="-20%" y="-20%" width="140%" height="140%">
1455
+ <feGaussianBlur in="SourceAlpha" stdDeviation="2.4" />
1456
+ <feOffset dx="0" dy="3" result="offset" />
1457
+ <feComponentTransfer>
1458
+ <feFuncA type="linear" slope="0.35" />
1459
+ </feComponentTransfer>
1460
+ <feMerge>
1461
+ <feMergeNode />
1462
+ <feMergeNode in="SourceGraphic" />
1463
+ </feMerge>
1464
+ </filter>
1465
+ </defs>
1466
+
1467
+ {/* Corner ornaments — small classical filigree */}
1468
+ <g opacity={sFacade * 0.45} fill={C.goldDeep}>
1469
+ <circle cx={6} cy={6} r={2.5} />
1470
+ <circle cx={20} cy={6} r={1.2} />
1471
+ <circle cx={6} cy={20} r={1.2} />
1472
+ <circle cx={914} cy={6} r={2.5} />
1473
+ <circle cx={900} cy={6} r={1.2} />
1474
+ <circle cx={914} cy={20} r={1.2} />
1475
+ </g>
1476
+
1477
+ {/* ───────────────── PALACE FACADE ───────────────── */}
1478
+ <g
1479
+ opacity={sFacade}
1480
+ transform={`translate(${360}, ${10 + (1 - sFacade) * 20})`}
1481
+ >
1482
+ {/* Soft platform shadow */}
1483
+ <ellipse cx={100} cy={210} rx={130} ry={5} fill="rgba(26,20,16,0.18)" />
1484
+
1485
+ {/* Stepped base — three tiers, widening toward bottom */}
1486
+ <rect x={-10} y={195} width={220} height={11} fill={C.goldDeep} />
1487
+ <rect x={-4} y={186} width={208} height={9} fill={C.gold} />
1488
+ <rect x={2} y={178} width={196} height={8} fill={C.goldDeep} />
1489
+
1490
+ {/* 5 columns with subtle shading (lit left / shadow right) */}
1491
+ {[18, 56, 94, 132, 170].map((x) => (
1492
+ <g key={x}>
1493
+ {/* Capital (top flare) */}
1494
+ <rect x={x - 2} y={94} width={18} height={5} fill={C.goldDeep} />
1495
+ {/* Shaft */}
1496
+ <rect
1497
+ x={x}
1498
+ y={99}
1499
+ width={14}
1500
+ height={76}
1501
+ fill="url(#column)"
1502
+ stroke={C.goldDeep}
1503
+ strokeWidth={1}
1504
+ />
1505
+ {/* Vertical fluting line */}
1506
+ <line
1507
+ x1={x + 7}
1508
+ y1={102}
1509
+ x2={x + 7}
1510
+ y2={172}
1511
+ stroke={C.goldDeep}
1512
+ strokeOpacity="0.35"
1513
+ strokeWidth={0.8}
1514
+ />
1515
+ {/* Base */}
1516
+ <rect x={x - 2} y={175} width={18} height={3} fill={C.goldDeep} />
1517
+ </g>
1518
+ ))}
1519
+
1520
+ {/* Architrave (horizontal beam above columns) */}
1521
+ <rect x={2} y={84} width={196} height={11} fill={C.goldDeep} />
1522
+ <rect x={6} y={86} width={188} height={1.5} fill={C.gold} />
1523
+
1524
+ {/* Pediment (triangular gable) */}
1525
+ <path
1526
+ d="M2 84 L100 4 L198 84 Z"
1527
+ fill="url(#marble)"
1528
+ stroke={C.goldDeep}
1529
+ strokeWidth={2}
1530
+ />
1531
+ {/* Inner pediment line — echoes the silhouette */}
1532
+ <path
1533
+ d="M16 80 L100 12 L184 80"
1534
+ fill="none"
1535
+ stroke={C.gold}
1536
+ strokeOpacity="0.55"
1537
+ strokeWidth={1}
1538
+ />
1539
+ {/* Acroteria (small ornaments on pediment corners) */}
1540
+ <circle cx={2} cy={84} r={3} fill={C.goldDeep} />
1541
+ <circle cx={198} cy={84} r={3} fill={C.goldDeep} />
1542
+ <circle cx={100} cy={4} r={3.5} fill={C.goldDeep} />
1543
+
1544
+ {/* Central medallion / laurel */}
1545
+ <circle cx={100} cy={56} r={11} fill={C.paper} stroke={C.goldDeep} strokeWidth={1.4} />
1546
+ <circle cx={100} cy={56} r={6} fill={C.goldDeep} opacity={0.18} />
1547
+ <path
1548
+ d="M94 56 L106 56 M100 50 L100 62"
1549
+ stroke={C.goldDeep}
1550
+ strokeWidth={1.2}
1551
+ />
1552
+
1553
+ {/* Label below */}
1554
+ <text
1555
+ x={100}
1556
+ y={234}
1557
+ fill={C.inkSoft}
1558
+ fontFamily={MONO}
1559
+ fontSize={15}
1560
+ fontWeight={700}
1561
+ letterSpacing="0.22em"
1562
+ textAnchor="middle"
1563
+ >
1564
+ THE PALACE
1565
+ </text>
1566
+ </g>
1567
+
1568
+ {/* ───── Curved bezier connectors — palace → wings ───── */}
1569
+ <g
1570
+ fill="none"
1571
+ stroke="url(#connector)"
1572
+ strokeWidth={1.6}
1573
+ strokeDasharray="4 6"
1574
+ strokeDashoffset={dashOffset}
1575
+ opacity={sLines * 0.85}
1576
+ >
1577
+ <path d="M460 220 C460 270, 200 270, 160 320" />
1578
+ <path d="M460 220 C460 280, 460 280, 460 320" />
1579
+ <path d="M460 220 C460 270, 720 270, 760 320" />
1580
+ </g>
1581
+
1582
+ {/* ───────────────── WINGS ───────────────── */}
1583
+ {wings.map((wing) => (
1584
+ <g
1585
+ key={wing.label}
1586
+ opacity={sWings}
1587
+ transform={`translate(${wing.cx}, ${330 + (1 - sWings) * 16})`}
1588
+ >
1589
+ {/* Ground shadow */}
1590
+ <ellipse cx={6} cy={142} rx={92} ry={4} fill="rgba(26,20,16,0.18)" />
1591
+
1592
+ {/* Right side panel — perspective hint (back face peeking) */}
1593
+ <path d="M76 0 L88 6 L88 134 L76 140 Z" fill={C.paperDeep} />
1594
+
1595
+ {/* Roof — front face + side shadow */}
1596
+ <path d="M-82 0 L0 -34 L82 0 Z" fill="url(#roof)" stroke={C.goldDeep} strokeWidth={1.4} />
1597
+ <path d="M82 0 L88 6 L0 -28 L0 -34 Z" fill={C.goldDeep} opacity={0.85} />
1598
+ {/* Roof crest line */}
1599
+ <line x1={0} y1={-34} x2={0} y2={-30} stroke={C.paperDeep} strokeWidth={1.5} />
1600
+
1601
+ {/* Building wall — front face */}
1602
+ <rect
1603
+ x={-78}
1604
+ y={0}
1605
+ width={156}
1606
+ height={136}
1607
+ fill="url(#wall)"
1608
+ stroke={C.goldDeep}
1609
+ strokeWidth={1.6}
1610
+ />
1611
+
1612
+ {/* Cornice line under roof */}
1613
+ <line x1={-78} y1={6} x2={78} y2={6} stroke={C.goldDeep} strokeOpacity="0.4" strokeWidth={0.8} />
1614
+
1615
+ {/* Two LIT windows with radial gold glow */}
1616
+ {[-42, 42].map((wx) => (
1617
+ <g key={wx} transform={`translate(${wx - 18}, 22)`} opacity={winPulse}>
1618
+ {/* Frame */}
1619
+ <rect width={36} height={36} rx={1} fill="url(#window-glow)" stroke={C.goldDeep} strokeWidth={1.4} />
1620
+ {/* Mullions */}
1621
+ <line x1={18} y1={0} x2={18} y2={36} stroke={C.goldDeep} strokeOpacity="0.7" strokeWidth={1.1} />
1622
+ <line x1={0} y1={18} x2={36} y2={18} stroke={C.goldDeep} strokeOpacity="0.7" strokeWidth={1.1} />
1623
+ {/* Sill */}
1624
+ <rect x={-2} y={36} width={40} height={2.4} fill={C.goldDeep} />
1625
+ </g>
1626
+ ))}
1627
+
1628
+ {/* Door */}
1629
+ <rect x={-12} y={88} width={24} height={48} fill={C.inkSoft} stroke={C.goldDeep} strokeWidth={1.2} />
1630
+ <circle cx={6} cy={113} r={1.6} fill={C.gold} />
1631
+ {/* Step under door */}
1632
+ <rect x={-16} y={134} width={32} height={2.4} fill={C.goldDeep} />
1633
+
1634
+ {/* Roof-side small chimney (right) */}
1635
+ <rect x={36} y={-18} width={6} height={10} fill={C.goldDeep} />
1636
+
1637
+ {/* Foundation line */}
1638
+ <line x1={-86} y1={138} x2={94} y2={138} stroke={C.goldDeep} strokeWidth={1} />
1639
+
1640
+ {/* Label below */}
1641
+ <text
1642
+ x={4}
1643
+ y={166}
1644
+ fill={C.inkSoft}
1645
+ fontFamily={MONO}
1646
+ fontSize={13}
1647
+ fontWeight={700}
1648
+ letterSpacing="0.16em"
1649
+ textAnchor="middle"
1650
+ >
1651
+ WING · {wing.label}
1652
+ </text>
1653
+ </g>
1654
+ ))}
1655
+
1656
+ {/* ───── Connectors — middle wing → rooms ───── */}
1657
+ <g
1658
+ fill="none"
1659
+ stroke="url(#connector)"
1660
+ strokeWidth={1.4}
1661
+ strokeDasharray="3 5"
1662
+ strokeDashoffset={dashOffset * 0.7}
1663
+ opacity={sLines * 0.75}
1664
+ >
1665
+ <path d="M460 502 C460 540, 200 540, 160 568" />
1666
+ <path d="M460 502 C460 545, 460 545, 460 568" />
1667
+ <path d="M460 502 C460 540, 720 540, 760 568" />
1668
+ </g>
1669
+
1670
+ {/* ───────────────── ROOMS — engraved nameplates ───────────────── */}
1671
+ {rooms.map((room, i) => (
1672
+ <g
1673
+ key={room.label}
1674
+ opacity={sRooms}
1675
+ transform={`translate(${room.cx}, ${574 + (1 - sRooms) * 12})`}
1676
+ >
1677
+ {/* Drop shadow */}
1678
+ <ellipse cx={0} cy={70} rx={88} ry={3} fill="rgba(26,20,16,0.16)" />
1679
+ {/* Plaque body */}
1680
+ <rect
1681
+ x={-92}
1682
+ y={0}
1683
+ width={184}
1684
+ height={66}
1685
+ rx={6}
1686
+ fill="url(#plaque)"
1687
+ stroke={C.goldDeep}
1688
+ strokeWidth={1.8}
1689
+ />
1690
+ {/* Inner inset border */}
1691
+ <rect
1692
+ x={-86}
1693
+ y={5}
1694
+ width={172}
1695
+ height={56}
1696
+ rx={4}
1697
+ fill="none"
1698
+ stroke={C.gold}
1699
+ strokeOpacity="0.6"
1700
+ strokeWidth={0.8}
1701
+ />
1702
+ {/* Tiny corner studs */}
1703
+ {[
1704
+ [-82, 10],
1705
+ [82, 10],
1706
+ [-82, 56],
1707
+ [82, 56],
1708
+ ].map(([x, y]) => (
1709
+ <circle key={`${x}-${y}`} cx={x} cy={y} r={1.6} fill={C.goldDeep} />
1710
+ ))}
1711
+ {/* Room name in serif italic */}
1712
+ <text
1713
+ x={0}
1714
+ y={40}
1715
+ fill={C.ink}
1716
+ fontFamily={SERIF}
1717
+ fontSize={26}
1718
+ fontWeight={600}
1719
+ textAnchor="middle"
1720
+ fontStyle="italic"
1721
+ >
1722
+ {room.label}
1723
+ </text>
1724
+ {/* Gold underscore ornament */}
1725
+ <line x1={-22} y1={50} x2={22} y2={50} stroke={C.goldDeep} strokeWidth={1.4} />
1726
+ <circle cx={0} cy={50} r={2} fill={C.goldDeep} />
1727
+ {/* Tiny mono label below plaque */}
1728
+ <text
1729
+ x={0}
1730
+ y={86}
1731
+ fill={C.inkSoft}
1732
+ fontFamily={MONO}
1733
+ fontSize={11}
1734
+ fontWeight={700}
1735
+ letterSpacing="0.22em"
1736
+ textAnchor="middle"
1737
+ >
1738
+ ROOM · {String(i + 1).padStart(2, "0")}
1739
+ </text>
1740
+ </g>
1741
+ ))}
1742
+
1743
+ {/* ───── Connectors — middle room → drawers (target each drawer cx + 78 mid) ───── */}
1744
+ <g
1745
+ fill="none"
1746
+ stroke="url(#connector)"
1747
+ strokeWidth={1.2}
1748
+ strokeDasharray="2 4"
1749
+ strokeDashoffset={dashOffset * 0.5}
1750
+ opacity={sLines * 0.65}
1751
+ >
1752
+ <path d="M460 670 C460 690, 220 690, 199 712" />
1753
+ <path d="M460 670 C460 695, 373 695, 373 712" />
1754
+ <path d="M460 670 C460 695, 547 695, 547 712" />
1755
+ <path d="M460 670 C460 690, 700 690, 721 712" />
1756
+ </g>
1757
+
1758
+ {/* ───────────────── DRAWERS — readable memory cards ───────────────── */}
1759
+ <g opacity={sDrawers}>
1760
+ {drawers.map((d, i) => (
1761
+ <g
1762
+ key={d.time}
1763
+ transform={`translate(${d.cx}, ${712 + (1 - sDrawers) * 8})`}
1764
+ >
1765
+ {/* Shadow */}
1766
+ <ellipse cx={78} cy={92} rx={76} ry={3} fill="rgba(26,20,16,0.18)" />
1767
+ {/* Drawer body */}
1768
+ <rect
1769
+ x={0}
1770
+ y={0}
1771
+ width={156}
1772
+ height={88}
1773
+ rx={5}
1774
+ fill="url(#drawer-body)"
1775
+ stroke={C.goldDeep}
1776
+ strokeWidth={1.5}
1777
+ />
1778
+ {/* Top tab — gold strip */}
1779
+ <rect x={0} y={0} width={156} height={20} rx={5} fill={C.goldDeep} />
1780
+ <rect x={0} y={14} width={156} height={6} fill={C.goldDeep} />
1781
+ {/* Tab inner highlight */}
1782
+ <line x1={4} y1={3} x2={152} y2={3} stroke={C.gold} strokeOpacity="0.7" strokeWidth={0.8} />
1783
+ {/* Time label inside tab */}
1784
+ <text
1785
+ x={10}
1786
+ y={14}
1787
+ fill={C.paper}
1788
+ fontFamily={MONO}
1789
+ fontSize={10}
1790
+ fontWeight={700}
1791
+ letterSpacing="0.1em"
1792
+ >
1793
+ {d.time}
1794
+ </text>
1795
+ {/* Two pull rings */}
1796
+ <circle cx={56} cy={11} r={2.4} fill={C.paper} opacity={0.55} />
1797
+ <circle cx={108} cy={11} r={2.4} fill={C.paper} opacity={0.55} />
1798
+ {/* Memory body — primary line */}
1799
+ <text
1800
+ x={10}
1801
+ y={44}
1802
+ fill={C.ink}
1803
+ fontFamily={MONO}
1804
+ fontSize={12}
1805
+ fontWeight={700}
1806
+ >
1807
+ {d.body}
1808
+ </text>
1809
+ {/* Memory body — secondary tag */}
1810
+ <text
1811
+ x={10}
1812
+ y={64}
1813
+ fill={C.inkSoft}
1814
+ fontFamily={MONO}
1815
+ fontSize={10}
1816
+ fontWeight={500}
1817
+ opacity={0.85}
1818
+ >
1819
+ · {d.tag}
1820
+ </text>
1821
+ {/* Bottom hairline */}
1822
+ <line x1={6} y1={78} x2={150} y2={78} stroke={C.goldDeep} strokeOpacity="0.35" strokeWidth={0.8} />
1823
+ {/* Index marker */}
1824
+ <text
1825
+ x={148}
1826
+ y={64}
1827
+ fill={C.goldDeep}
1828
+ fontFamily={MONO}
1829
+ fontSize={9}
1830
+ fontWeight={700}
1831
+ textAnchor="end"
1832
+ opacity={0.55}
1833
+ >
1834
+ #{String(i + 1).padStart(2, "0")}
1835
+ </text>
1836
+ </g>
1837
+ ))}
1838
+ {/* Caption beneath drawer row */}
1839
+ <g transform="translate(460, 815)">
1840
+ <line x1={-80} y1={0} x2={-26} y2={0} stroke={C.goldDeep} strokeOpacity="0.4" strokeWidth={1} />
1841
+ <line x1={26} y1={0} x2={80} y2={0} stroke={C.goldDeep} strokeOpacity="0.4" strokeWidth={1} />
1842
+ <text
1843
+ x={0}
1844
+ y={5}
1845
+ fill={C.inkSoft}
1846
+ fontFamily={MONO}
1847
+ fontSize={13}
1848
+ fontWeight={700}
1849
+ letterSpacing="0.22em"
1850
+ textAnchor="middle"
1851
+ >
1852
+ DRAWERS · MEMORIES
1853
+ </text>
1854
+ </g>
1855
+ </g>
1856
+ </svg>
1857
+ );
1858
+ };
1859
+
1860
+ const Scene5: React.FC = () => {
1861
+ const frame = useCurrentFrame();
1862
+ const { fps } = useVideoConfig();
1863
+
1864
+ // Pill in top
1865
+ const pillOp = fadeIn(frame, 0, 14);
1866
+
1867
+ // Aristotle / Cicero quote block — type each name in
1868
+ const aristotleOp = fadeIn(frame, 12);
1869
+ const ciceroOp = fadeIn(frame, 30);
1870
+
1871
+ // Headline
1872
+ const headOp = fadeIn(frame, 48, 18);
1873
+ const headY = interpolate(frame, [48, 70], [22, 0], {
1874
+ extrapolateLeft: "clamp",
1875
+ extrapolateRight: "clamp",
1876
+ });
1877
+
1878
+ // bottom caption — "word-for-word"
1879
+ const capOp = fadeIn(frame, 320);
1880
+ const capY = interpolate(frame, [320, 350], [16, 0], {
1881
+ extrapolateLeft: "clamp",
1882
+ extrapolateRight: "clamp",
1883
+ });
1884
+
1885
+ const exit = fadeOut(frame, SCENES.S5.dur - 16, 12);
1886
+
1887
+ return (
1888
+ <AbsoluteFill style={{ fontFamily: FONT, opacity: exit }}>
1889
+ <PaperBg frame={frame} />
1890
+
1891
+ {/* Pill */}
1892
+ <div
1893
+ style={{
1894
+ position: "absolute",
1895
+ top: SAFE_TOP - 20,
1896
+ left: 0,
1897
+ right: 0,
1898
+ textAlign: "center",
1899
+ opacity: pillOp,
1900
+ }}
1901
+ >
1902
+ <Pill bg="rgba(184,137,61,0.14)" fg={C.goldDeep} border="rgba(184,137,61,0.40)">
1903
+ ANCIENT GREEK · 500 BC
1904
+ </Pill>
1905
+ </div>
1906
+
1907
+ {/* Quote attributions */}
1908
+ <div
1909
+ style={{
1910
+ position: "absolute",
1911
+ top: SAFE_TOP + 40,
1912
+ left: 0,
1913
+ right: 0,
1914
+ textAlign: "center",
1915
+ fontFamily: SERIF,
1916
+ }}
1917
+ >
1918
+ <div
1919
+ style={{
1920
+ display: "flex",
1921
+ justifyContent: "center",
1922
+ gap: 48,
1923
+ fontSize: 38,
1924
+ color: C.inkSoft,
1925
+ fontStyle: "italic",
1926
+ }}
1927
+ >
1928
+ <span style={{ opacity: aristotleOp }}>
1929
+ <span style={{ color: C.gold }}>—&nbsp;</span>Aristotle
1930
+ </span>
1931
+ <span style={{ opacity: ciceroOp }}>
1932
+ <span style={{ color: C.gold }}>—&nbsp;</span>Cicero
1933
+ </span>
1934
+ </div>
1935
+ <div
1936
+ style={{
1937
+ marginTop: 4,
1938
+ fontSize: 22,
1939
+ color: C.inkSoft,
1940
+ opacity: aristotleOp * 0.7,
1941
+ fontFamily: MONO,
1942
+ letterSpacing: "0.12em",
1943
+ }}
1944
+ >
1945
+ USED THIS · 2,400 YEARS
1946
+ </div>
1947
+ </div>
1948
+
1949
+ {/* Headline */}
1950
+ <div
1951
+ style={{
1952
+ position: "absolute",
1953
+ top: 460,
1954
+ left: 0,
1955
+ right: 0,
1956
+ textAlign: "center",
1957
+ padding: "0 60px",
1958
+ opacity: headOp,
1959
+ transform: `translateY(${headY}px)`,
1960
+ }}
1961
+ >
1962
+ <div
1963
+ style={{
1964
+ fontSize: 64,
1965
+ fontWeight: 700,
1966
+ color: C.ink,
1967
+ letterSpacing: "-0.035em",
1968
+ lineHeight: 1.0,
1969
+ }}
1970
+ >
1971
+ Your projects become<br />
1972
+ <span style={{ fontFamily: SERIF, fontStyle: "italic", color: C.goldDeep }}>
1973
+ a building you can walk.
1974
+ </span>
1975
+ </div>
1976
+ </div>
1977
+
1978
+ {/* Architecture diagram */}
1979
+ <ArchitectureDiagram frame={frame} fps={fps} />
1980
+
1981
+ {/* Bottom caption — "word-for-word" */}
1982
+ <div
1983
+ style={{
1984
+ position: "absolute",
1985
+ left: 0,
1986
+ right: 0,
1987
+ top: 1410,
1988
+ textAlign: "center",
1989
+ opacity: capOp,
1990
+ transform: `translateY(${capY}px)`,
1991
+ padding: "0 60px",
1992
+ }}
1993
+ >
1994
+ <div
1995
+ style={{
1996
+ display: "inline-block",
1997
+ padding: "20px 32px",
1998
+ background: "rgba(26,20,16,0.06)",
1999
+ border: `1px solid rgba(184,137,61,0.30)`,
2000
+ borderRadius: 16,
2001
+ fontFamily: MONO,
2002
+ fontSize: 28,
2003
+ color: C.ink,
2004
+ fontWeight: 600,
2005
+ letterSpacing: "-0.01em",
2006
+ }}
2007
+ >
2008
+ word-for-word · <span style={{ color: C.goldDeep }}>open on demand</span>
2009
+ </div>
2010
+ </div>
2011
+ </AbsoluteFill>
2012
+ );
2013
+ };
2014
+
2015
+ // ═══════════════════════════════════════════════════════════════════════════
2016
+ // SCENE 6 — THE HOOK MAGIC (with screen recording)
2017
+ // "There's a hook that fires right before context compaction.
2018
+ // this setup already saved it. Locally."
2019
+ // ═══════════════════════════════════════════════════════════════════════════
2020
+
2021
+ const Scene6: React.FC = () => {
2022
+ const frame = useCurrentFrame();
2023
+ const { fps } = useVideoConfig();
2024
+
2025
+ // Title
2026
+ const titleOp = fadeIn(frame, 0, 14);
2027
+ const titleY = interpolate(frame, [0, 18], [22, 0], {
2028
+ extrapolateLeft: "clamp",
2029
+ extrapolateRight: "clamp",
2030
+ });
2031
+
2032
+ // Hook diagram pop in
2033
+ const hookSpring = sp(frame, fps, 1.5, "snappy");
2034
+ const hookOp = interpolate(hookSpring, [0, 0.4], [0, 1], {
2035
+ extrapolateLeft: "clamp",
2036
+ extrapolateRight: "clamp",
2037
+ });
2038
+ const hookY = interpolate(hookSpring, [0, 1], [40, 0]);
2039
+
2040
+ // Locally pill — 7s
2041
+ const localOp = fadeIn(frame, 230);
2042
+ const localY = interpolate(frame, [230, 255], [22, 0], {
2043
+ extrapolateLeft: "clamp",
2044
+ extrapolateRight: "clamp",
2045
+ });
2046
+
2047
+ // Pulse on the hook arrow
2048
+ const pulse = (Math.sin(frame * 0.18) + 1) * 0.5;
2049
+
2050
+ const exit = fadeOut(frame, SCENES.S6.dur - 16, 12);
2051
+
2052
+ return (
2053
+ <AbsoluteFill style={{ fontFamily: FONT, opacity: exit }}>
2054
+ <DarkBg frame={frame} tint="emerald" />
2055
+
2056
+ {/* Title */}
2057
+ <div
2058
+ style={{
2059
+ position: "absolute",
2060
+ top: SAFE_TOP - 8,
2061
+ left: 0,
2062
+ right: 0,
2063
+ textAlign: "center",
2064
+ opacity: titleOp,
2065
+ transform: `translateY(${titleY}px)`,
2066
+ padding: "0 60px",
2067
+ }}
2068
+ >
2069
+ <Pill bg="rgba(0,210,148,0.10)" fg={C.emerald} border="rgba(0,210,148,0.35)" dot={C.emerald}>
2070
+ THE PRE-COMPACTION HOOK
2071
+ </Pill>
2072
+ <div
2073
+ style={{
2074
+ marginTop: 22,
2075
+ fontSize: 60,
2076
+ fontWeight: 700,
2077
+ color: C.fg,
2078
+ letterSpacing: "-0.035em",
2079
+ lineHeight: 1.05,
2080
+ }}
2081
+ >
2082
+ Fires the second Claude<br />
2083
+ is <span style={{ color: C.emerald }}>about to forget.</span>
2084
+ </div>
2085
+ </div>
2086
+
2087
+ {/* Hook flow diagram */}
2088
+ <Sequence from={50} durationInFrames={SCENES.S6.dur}>
2089
+ <AbsoluteFill style={{ opacity: hookOp, transform: `translateY(${hookY}px)` }}>
2090
+ <HookFlowDiagram frame={frame - 50} pulse={pulse} />
2091
+ </AbsoluteFill>
2092
+ </Sequence>
2093
+
2094
+ {/* Screen recording */}
2095
+ <Sequence from={120} durationInFrames={SCENES.S6.dur - 120}>
2096
+ <ScreenRecording />
2097
+ </Sequence>
2098
+
2099
+ {/* Bottom locally pill */}
2100
+ <div
2101
+ style={{
2102
+ position: "absolute",
2103
+ left: 0,
2104
+ right: 0,
2105
+ bottom: H - SAFE_BOT_Y + 40,
2106
+ display: "flex",
2107
+ justifyContent: "center",
2108
+ gap: 16,
2109
+ opacity: localOp,
2110
+ transform: `translateY(${localY}px)`,
2111
+ }}
2112
+ >
2113
+ <Pill bg="rgba(0,210,148,0.12)" fg={C.emerald} border="rgba(0,210,148,0.40)" dot={C.emerald} size={24}>
2114
+ SAVED LOCALLY · 0 NETWORK
2115
+ </Pill>
2116
+ </div>
2117
+ </AbsoluteFill>
2118
+ );
2119
+ };
2120
+
2121
+ const HookFlowDiagram: React.FC<{ frame: number; pulse: number }> = ({
2122
+ frame,
2123
+ pulse,
2124
+ }) => {
2125
+ // 3-step pipe diagram: [CTX FULL] → [HOOK FIRES] → [SAVED]
2126
+ const t1 = tween(frame, 0, 16, 0, 1);
2127
+ const t2 = tween(frame, 24, 16, 0, 1);
2128
+ const t3 = tween(frame, 50, 16, 0, 1);
2129
+
2130
+ return (
2131
+ <div
2132
+ style={{
2133
+ position: "absolute",
2134
+ top: 540,
2135
+ left: 60,
2136
+ right: 60,
2137
+ display: "flex",
2138
+ flexDirection: "column",
2139
+ gap: 18,
2140
+ }}
2141
+ >
2142
+ {/* Row 1 — CTX FULL */}
2143
+ <div
2144
+ style={{
2145
+ opacity: t1,
2146
+ transform: `translateY(${(1 - t1) * 14}px)`,
2147
+ background: C.surface,
2148
+ border: `1px solid ${C.borderLoud}`,
2149
+ borderRadius: 18,
2150
+ padding: "20px 24px",
2151
+ display: "flex",
2152
+ alignItems: "center",
2153
+ gap: 16,
2154
+ fontFamily: MONO,
2155
+ color: C.fgSoft,
2156
+ }}
2157
+ >
2158
+ <div
2159
+ style={{
2160
+ width: 12,
2161
+ height: 12,
2162
+ borderRadius: 999,
2163
+ background: "#fcbb00",
2164
+ boxShadow: "0 0 14px rgba(252,187,0,0.45)",
2165
+ }}
2166
+ />
2167
+ <div style={{ fontSize: 22, fontWeight: 600, letterSpacing: "0.04em" }}>
2168
+ context window · 98% full
2169
+ </div>
2170
+ <div
2171
+ style={{
2172
+ marginLeft: "auto",
2173
+ display: "flex",
2174
+ alignItems: "center",
2175
+ gap: 6,
2176
+ color: "#fcbb00",
2177
+ fontSize: 18,
2178
+ fontWeight: 600,
2179
+ }}
2180
+ >
2181
+ <span style={{ width: 80, height: 6, borderRadius: 999, background: "rgba(255,255,255,0.06)", overflow: "hidden", display: "inline-block" }}>
2182
+ <span
2183
+ style={{
2184
+ display: "block",
2185
+ width: "98%",
2186
+ height: "100%",
2187
+ background: "linear-gradient(90deg, #fcbb00, #ef4444)",
2188
+ }}
2189
+ />
2190
+ </span>
2191
+ </div>
2192
+ </div>
2193
+
2194
+ {/* Arrow + HOOK */}
2195
+ <div
2196
+ style={{
2197
+ opacity: t2,
2198
+ display: "flex",
2199
+ flexDirection: "column",
2200
+ alignItems: "center",
2201
+ gap: 10,
2202
+ transform: `translateY(${(1 - t2) * 14}px)`,
2203
+ }}
2204
+ >
2205
+ <div
2206
+ style={{
2207
+ width: 4,
2208
+ height: 36,
2209
+ background: `linear-gradient(${C.fgDim}, ${C.emerald})`,
2210
+ borderRadius: 999,
2211
+ }}
2212
+ />
2213
+ <div
2214
+ style={{
2215
+ display: "flex",
2216
+ alignItems: "center",
2217
+ gap: 14,
2218
+ padding: "16px 28px",
2219
+ background: `linear-gradient(135deg, rgba(0,210,148,0.18), rgba(0,135,90,0.12))`,
2220
+ border: `1px solid rgba(0,210,148,${0.4 + pulse * 0.3})`,
2221
+ borderRadius: 999,
2222
+ fontFamily: MONO,
2223
+ color: C.emerald,
2224
+ fontWeight: 700,
2225
+ fontSize: 24,
2226
+ letterSpacing: "0.05em",
2227
+ boxShadow: `0 0 ${30 + pulse * 30}px rgba(0,210,148,${0.18 + pulse * 0.12})`,
2228
+ }}
2229
+ >
2230
+ {/* Inline hook icon — emerald, no external file */}
2231
+ <svg width={26} height={26} viewBox="0 0 24 24" fill="none">
2232
+ <path
2233
+ d="M12 3 L12 13 A4 4 0 1 1 8 17"
2234
+ stroke={C.emerald}
2235
+ strokeWidth={2.4}
2236
+ strokeLinecap="round"
2237
+ strokeLinejoin="round"
2238
+ />
2239
+ <circle cx={12} cy={3} r={1.6} fill={C.emerald} />
2240
+ </svg>
2241
+ PRE-COMPACTION HOOK FIRES
2242
+ </div>
2243
+ <div
2244
+ style={{
2245
+ width: 4,
2246
+ height: 36,
2247
+ background: `linear-gradient(${C.emerald}, ${C.fgDim})`,
2248
+ borderRadius: 999,
2249
+ }}
2250
+ />
2251
+ </div>
2252
+
2253
+ {/* SAVED */}
2254
+ <div
2255
+ style={{
2256
+ opacity: t3,
2257
+ transform: `translateY(${(1 - t3) * 14}px)`,
2258
+ background: `linear-gradient(135deg, rgba(0,210,148,0.08), rgba(0,135,90,0.04))`,
2259
+ border: `1px solid rgba(0,210,148,0.35)`,
2260
+ borderRadius: 18,
2261
+ padding: "20px 24px",
2262
+ display: "flex",
2263
+ alignItems: "center",
2264
+ gap: 16,
2265
+ fontFamily: MONO,
2266
+ color: C.fg,
2267
+ }}
2268
+ >
2269
+ <Img
2270
+ src={staticFile("captures/your-asset.png" /* REFERENCE-STRIP */)}
2271
+ style={{
2272
+ width: 28,
2273
+ height: 28,
2274
+ filter: "brightness(0) saturate(100%) invert(72%) sepia(76%) saturate(363%) hue-rotate(95deg) brightness(91%) contrast(86%)",
2275
+ }}
2276
+ />
2277
+ <div style={{ fontSize: 22, fontWeight: 600, letterSpacing: "0.04em" }}>
2278
+ memory persisted · ~/.mempalace/
2279
+ </div>
2280
+ </div>
2281
+ </div>
2282
+ );
2283
+ };
2284
+
2285
+ // Screen recording placed in stylized terminal frame
2286
+ const ScreenRecording: React.FC = () => {
2287
+ const frame = useCurrentFrame();
2288
+
2289
+ // mempalace.mov is 7.15s × 60fps = 545 native frames; we'll loop using OffthreadVideo
2290
+ const op = fadeIn(frame, 0, 18);
2291
+ const enterY = interpolate(frame, [0, 24], [40, 0], {
2292
+ extrapolateLeft: "clamp",
2293
+ extrapolateRight: "clamp",
2294
+ });
2295
+
2296
+ return (
2297
+ <AbsoluteFill
2298
+ style={{
2299
+ opacity: op,
2300
+ transform: `translateY(${enterY}px)`,
2301
+ }}
2302
+ >
2303
+ <div
2304
+ style={{
2305
+ position: "absolute",
2306
+ top: 1010,
2307
+ left: 80,
2308
+ right: 80,
2309
+ height: 460,
2310
+ borderRadius: 22,
2311
+ overflow: "hidden",
2312
+ background: C.surface,
2313
+ border: `1px solid ${C.borderLoud}`,
2314
+ boxShadow:
2315
+ "0 30px 80px -10px rgba(0,0,0,0.6), inset 0 1px 0 rgba(255,255,255,0.06)",
2316
+ }}
2317
+ >
2318
+ {/* title bar */}
2319
+ <div
2320
+ style={{
2321
+ display: "flex",
2322
+ alignItems: "center",
2323
+ gap: 10,
2324
+ padding: "14px 20px",
2325
+ background: "rgba(255,255,255,0.04)",
2326
+ borderBottom: `1px solid ${C.border}`,
2327
+ }}
2328
+ >
2329
+ <div style={{ width: 11, height: 11, borderRadius: 999, background: "#ff5f57" }} />
2330
+ <div style={{ width: 11, height: 11, borderRadius: 999, background: "#febc2e" }} />
2331
+ <div style={{ width: 11, height: 11, borderRadius: 999, background: "#28c840" }} />
2332
+ <div
2333
+ style={{
2334
+ marginLeft: 14,
2335
+ fontFamily: MONO,
2336
+ fontSize: 16,
2337
+ color: C.fgMuted,
2338
+ letterSpacing: "0.05em",
2339
+ }}
2340
+ >
2341
+ ~/.mempalace · live
2342
+ </div>
2343
+ <div
2344
+ style={{
2345
+ marginLeft: "auto",
2346
+ display: "flex",
2347
+ alignItems: "center",
2348
+ gap: 6,
2349
+ fontFamily: MONO,
2350
+ fontSize: 14,
2351
+ color: C.emerald,
2352
+ letterSpacing: "0.1em",
2353
+ fontWeight: 600,
2354
+ }}
2355
+ >
2356
+ <span
2357
+ style={{
2358
+ width: 6,
2359
+ height: 6,
2360
+ borderRadius: 999,
2361
+ background: C.emerald,
2362
+ boxShadow: `0 0 8px ${C.emerald}`,
2363
+ }}
2364
+ />
2365
+ REC
2366
+ </div>
2367
+ </div>
2368
+ {/* video */}
2369
+ <div style={{ position: "relative", width: "100%", height: "100%", background: "#000" }}>
2370
+ {/* REFERENCE-STRIP: <OffthreadVideo> removed — bring your own clip */}
2371
+ {/* subtle scanline overlay for "screen rec" feel */}
2372
+ <div
2373
+ style={{
2374
+ position: "absolute",
2375
+ inset: 0,
2376
+ backgroundImage: `repeating-linear-gradient(0deg, rgba(255,255,255,0.03) 0px, rgba(255,255,255,0.03) 1px, transparent 1px, transparent 3px)`,
2377
+ pointerEvents: "none",
2378
+ mixBlendMode: "overlay",
2379
+ }}
2380
+ />
2381
+ </div>
2382
+ </div>
2383
+ </AbsoluteFill>
2384
+ );
2385
+ };
2386
+
2387
+ // ═══════════════════════════════════════════════════════════════════════════
2388
+ // SCENE 7 — SOCIAL PROOF
2389
+ // "Zero-Human Company · 79 employees · USC professor · locally · 0 API · 0 sub · MIT"
2390
+ // ═══════════════════════════════════════════════════════════════════════════
2391
+
2392
+ const Scene7: React.FC = () => {
2393
+ const frame = useCurrentFrame();
2394
+ const { fps } = useVideoConfig();
2395
+
2396
+ // Pill in
2397
+ const titleOp = fadeIn(frame, 0, 14);
2398
+ const titleY = interpolate(frame, [0, 18], [22, 0], {
2399
+ extrapolateLeft: "clamp",
2400
+ extrapolateRight: "clamp",
2401
+ });
2402
+
2403
+ // 79 counter
2404
+ const seventyNineCount = Math.round(tween(frame, 30, 40, 0, 79));
2405
+
2406
+ // Stagger the 6 stat cards
2407
+ const cards = [
2408
+ {
2409
+ kicker: "DEPLOYED AT",
2410
+ head: "Zero-Human Co.",
2411
+ sub: `${seventyNineCount} employees`,
2412
+ tint: C.emerald,
2413
+ delay: 0.5,
2414
+ mono: false,
2415
+ },
2416
+ {
2417
+ kicker: "ARCHITECTURE",
2418
+ head: "USC professor",
2419
+ sub: "blessed",
2420
+ tint: "#fcbb00",
2421
+ delay: 1.0,
2422
+ mono: false,
2423
+ },
2424
+ {
2425
+ kicker: "RUNTIME",
2426
+ head: "100% local",
2427
+ sub: "no cloud",
2428
+ tint: C.emerald,
2429
+ delay: 1.5,
2430
+ mono: false,
2431
+ },
2432
+ {
2433
+ kicker: "API CALLS",
2434
+ head: "0",
2435
+ sub: "zero outbound",
2436
+ tint: C.emerald,
2437
+ delay: 2.0,
2438
+ mono: true,
2439
+ },
2440
+ {
2441
+ kicker: "SUBSCRIPTION",
2442
+ head: "$0",
2443
+ sub: "forever",
2444
+ tint: C.emerald,
2445
+ delay: 2.5,
2446
+ mono: true,
2447
+ },
2448
+ {
2449
+ kicker: "LICENSE",
2450
+ head: "MIT",
2451
+ sub: "fork it",
2452
+ tint: C.indigo,
2453
+ delay: 3.0,
2454
+ mono: true,
2455
+ },
2456
+ ];
2457
+
2458
+ const exit = fadeOut(frame, SCENES.S7.dur - 16, 12);
2459
+
2460
+ return (
2461
+ <AbsoluteFill style={{ fontFamily: FONT, opacity: exit }}>
2462
+ <DarkBg frame={frame} tint="emerald" />
2463
+
2464
+ {/* Title */}
2465
+ <div
2466
+ style={{
2467
+ position: "absolute",
2468
+ top: SAFE_TOP - 8,
2469
+ left: 0,
2470
+ right: 0,
2471
+ textAlign: "center",
2472
+ opacity: titleOp,
2473
+ transform: `translateY(${titleY}px)`,
2474
+ padding: "0 60px",
2475
+ }}
2476
+ >
2477
+ <Pill bg="rgba(0,210,148,0.10)" fg={C.emerald} border="rgba(0,210,148,0.35)" dot={C.emerald}>
2478
+ SHIPPED · APPROVED · OPEN
2479
+ </Pill>
2480
+ <div
2481
+ style={{
2482
+ marginTop: 22,
2483
+ fontSize: 64,
2484
+ fontWeight: 700,
2485
+ color: C.fg,
2486
+ letterSpacing: "-0.035em",
2487
+ lineHeight: 1.05,
2488
+ }}
2489
+ >
2490
+ Already in production.<br />
2491
+ <span style={{ color: C.fgMuted }}>Reviewed.</span>
2492
+ <span style={{ color: C.fg }}> Free.</span>
2493
+ </div>
2494
+ </div>
2495
+
2496
+ {/* Stat grid 2x3 */}
2497
+ <div
2498
+ style={{
2499
+ position: "absolute",
2500
+ top: 660,
2501
+ left: 60,
2502
+ right: 60,
2503
+ display: "grid",
2504
+ gridTemplateColumns: "1fr 1fr",
2505
+ gap: 16,
2506
+ }}
2507
+ >
2508
+ {cards.map((c, i) => {
2509
+ const cs = sp(frame, fps, c.delay, "snappy");
2510
+ const op = interpolate(cs, [0, 0.4], [0, 1], {
2511
+ extrapolateLeft: "clamp",
2512
+ extrapolateRight: "clamp",
2513
+ });
2514
+ const y = interpolate(cs, [0, 1], [40, 0]);
2515
+ return (
2516
+ <div
2517
+ key={c.kicker}
2518
+ style={{
2519
+ opacity: op,
2520
+ transform: `translateY(${y}px)`,
2521
+ background: C.surface,
2522
+ border: `1px solid ${C.borderLoud}`,
2523
+ borderRadius: 22,
2524
+ padding: "26px 26px",
2525
+ position: "relative",
2526
+ overflow: "hidden",
2527
+ boxShadow: "0 18px 50px -10px rgba(0,0,0,0.5), inset 0 1px 0 rgba(255,255,255,0.05)",
2528
+ }}
2529
+ >
2530
+ {/* tint bar */}
2531
+ <div
2532
+ style={{
2533
+ position: "absolute",
2534
+ left: 0,
2535
+ top: 0,
2536
+ bottom: 0,
2537
+ width: 4,
2538
+ background: c.tint,
2539
+ }}
2540
+ />
2541
+ <div
2542
+ style={{
2543
+ fontFamily: MONO,
2544
+ fontSize: 14,
2545
+ color: c.tint,
2546
+ letterSpacing: "0.16em",
2547
+ fontWeight: 600,
2548
+ }}
2549
+ >
2550
+ {c.kicker}
2551
+ </div>
2552
+ <div
2553
+ style={{
2554
+ marginTop: 10,
2555
+ fontSize: c.mono ? 84 : 44,
2556
+ fontFamily: c.mono ? MONO : FONT,
2557
+ fontWeight: c.mono ? 700 : 700,
2558
+ color: C.fg,
2559
+ letterSpacing: "-0.04em",
2560
+ lineHeight: 1,
2561
+ }}
2562
+ >
2563
+ {c.head}
2564
+ </div>
2565
+ <div
2566
+ style={{
2567
+ marginTop: 10,
2568
+ fontSize: 22,
2569
+ color: C.fgMuted,
2570
+ fontWeight: 500,
2571
+ letterSpacing: "-0.01em",
2572
+ }}
2573
+ >
2574
+ {c.sub}
2575
+ </div>
2576
+ </div>
2577
+ );
2578
+ })}
2579
+ </div>
2580
+ </AbsoluteFill>
2581
+ );
2582
+ };
2583
+
2584
+ // ═══════════════════════════════════════════════════════════════════════════
2585
+ // SCENE 8 — CTA
2586
+ // "Comment 'AI' below and I'll share the official GitHub repo plus
2587
+ // the Claude Code setup. Once Claude actually remembers what you taught it...
2588
+ // you're done with every other memory tool."
2589
+ // ═══════════════════════════════════════════════════════════════════════════
2590
+
2591
+ const Scene8: React.FC = () => {
2592
+ const frame = useCurrentFrame();
2593
+ const { fps } = useVideoConfig();
2594
+
2595
+ // Headline letters
2596
+ const head = "COMMENT";
2597
+
2598
+ // 'AI' — gradient + WebkitBackgroundClip do not propagate to per-letter
2599
+ // child spans, so animate as a single unit via parent transform
2600
+ const aiSpring = sp(frame, fps, 0.55, "bouncy");
2601
+ const aiOp = interpolate(aiSpring, [0, 0.4], [0, 1], {
2602
+ extrapolateLeft: "clamp",
2603
+ extrapolateRight: "clamp",
2604
+ });
2605
+ const aiY = interpolate(aiSpring, [0, 1], [50, 0]);
2606
+ const aiScale = interpolate(aiSpring, [0, 0.55, 1], [0.7, 1.1, 1]);
2607
+
2608
+ // Big CTA in
2609
+ const subOp = fadeIn(frame, 36);
2610
+ const subY = interpolate(frame, [36, 60], [22, 0], {
2611
+ extrapolateLeft: "clamp",
2612
+ extrapolateRight: "clamp",
2613
+ });
2614
+
2615
+ // payload chips
2616
+ const c1 = sp(frame, fps, 1.6, "snappy");
2617
+ const c2 = sp(frame, fps, 1.9, "snappy");
2618
+
2619
+ // closer line
2620
+ const closerOp = fadeIn(frame, 96);
2621
+ const closerY = interpolate(frame, [96, 124], [22, 0], {
2622
+ extrapolateLeft: "clamp",
2623
+ extrapolateRight: "clamp",
2624
+ });
2625
+
2626
+ // arrow bounce — points to comment area
2627
+ const arrowAt = 150;
2628
+ const arrowOp = fadeIn(frame, arrowAt, 12);
2629
+ const arrowBounce = (Math.sin((frame - arrowAt) * 0.18) + 1) * 0.5;
2630
+ const arrowY = arrowBounce * 14;
2631
+
2632
+ return (
2633
+ <AbsoluteFill style={{ fontFamily: FONT }}>
2634
+ <DarkBg frame={frame} tint="indigo" />
2635
+
2636
+ {/* Top context */}
2637
+ <div
2638
+ style={{
2639
+ position: "absolute",
2640
+ top: SAFE_TOP - 10,
2641
+ left: 0,
2642
+ right: 0,
2643
+ textAlign: "center",
2644
+ opacity: fadeIn(frame, 0, 12),
2645
+ }}
2646
+ >
2647
+ <Pill bg="rgba(98,95,255,0.12)" fg="#a4b3ff" border="rgba(98,95,255,0.40)" dot={C.indigo}>
2648
+ GET THE REPO
2649
+ </Pill>
2650
+ </div>
2651
+
2652
+ {/* Big "COMMENT 'AI'" */}
2653
+ <div
2654
+ style={{
2655
+ position: "absolute",
2656
+ top: 480,
2657
+ left: 0,
2658
+ right: 0,
2659
+ textAlign: "center",
2660
+ padding: "0 50px",
2661
+ }}
2662
+ >
2663
+ <div
2664
+ style={{
2665
+ display: "flex",
2666
+ justifyContent: "center",
2667
+ fontSize: 156,
2668
+ fontWeight: 800,
2669
+ color: C.fg,
2670
+ letterSpacing: "-0.06em",
2671
+ lineHeight: 0.95,
2672
+ }}
2673
+ >
2674
+ {letterStagger(head, frame, fps, 0.05, 0.04).map((s, i) => (
2675
+ <span
2676
+ key={`h-${i}`}
2677
+ style={{
2678
+ display: "inline-block",
2679
+ opacity: s.opacity,
2680
+ transform: `translateY(${s.y}px) scale(${s.scale})`,
2681
+ }}
2682
+ >
2683
+ {s.ch}
2684
+ </span>
2685
+ ))}
2686
+ </div>
2687
+ <div
2688
+ style={{
2689
+ marginTop: 16,
2690
+ fontSize: 240,
2691
+ fontWeight: 800,
2692
+ letterSpacing: "-0.07em",
2693
+ lineHeight: 0.92,
2694
+ background: `linear-gradient(135deg, ${C.indigo} 0%, #a4b3ff 100%)`,
2695
+ WebkitBackgroundClip: "text",
2696
+ backgroundClip: "text",
2697
+ WebkitTextFillColor: "transparent",
2698
+ filter: `drop-shadow(0 0 40px rgba(98,95,255,0.45))`,
2699
+ opacity: aiOp,
2700
+ transform: `translateY(${aiY}px) scale(${aiScale})`,
2701
+ transformOrigin: "center",
2702
+ }}
2703
+ >
2704
+ 'AI'
2705
+ </div>
2706
+ </div>
2707
+
2708
+ {/* Sub: "I'll DM you the repo" */}
2709
+ <div
2710
+ style={{
2711
+ position: "absolute",
2712
+ top: 980,
2713
+ left: 0,
2714
+ right: 0,
2715
+ textAlign: "center",
2716
+ opacity: subOp,
2717
+ transform: `translateY(${subY}px)`,
2718
+ padding: "0 70px",
2719
+ }}
2720
+ >
2721
+ <div
2722
+ style={{
2723
+ fontSize: 36,
2724
+ color: C.fgSoft,
2725
+ fontWeight: 500,
2726
+ letterSpacing: "-0.01em",
2727
+ lineHeight: 1.3,
2728
+ }}
2729
+ >
2730
+ and I'll send you the repo +<br />
2731
+ one-line setup
2732
+ </div>
2733
+ </div>
2734
+
2735
+ {/* Payload chips */}
2736
+ <div
2737
+ style={{
2738
+ position: "absolute",
2739
+ top: 1130,
2740
+ left: 0,
2741
+ right: 0,
2742
+ display: "flex",
2743
+ justifyContent: "center",
2744
+ gap: 14,
2745
+ }}
2746
+ >
2747
+ <div
2748
+ style={{
2749
+ opacity: interpolate(c1, [0, 0.4], [0, 1], { extrapolateLeft: "clamp", extrapolateRight: "clamp" }),
2750
+ transform: `translateY(${interpolate(c1, [0, 1], [20, 0])}px) scale(${interpolate(c1, [0, 0.6, 1], [0.9, 1.04, 1])})`,
2751
+ }}
2752
+ >
2753
+ <div
2754
+ style={{
2755
+ display: "inline-flex",
2756
+ alignItems: "center",
2757
+ gap: 12,
2758
+ padding: "14px 22px",
2759
+ background: "rgba(255,255,255,0.06)",
2760
+ border: `1px solid ${C.borderLoud}`,
2761
+ borderRadius: 999,
2762
+ fontFamily: MONO,
2763
+ fontSize: 22,
2764
+ color: C.fg,
2765
+ fontWeight: 600,
2766
+ }}
2767
+ >
2768
+ <Img
2769
+ src={staticFile("captures/your-asset.png" /* REFERENCE-STRIP */)}
2770
+ style={{ width: 22, height: 22, filter: "brightness(0) invert(1)" }}
2771
+ />
2772
+ github repo
2773
+ </div>
2774
+ </div>
2775
+ <div
2776
+ style={{
2777
+ opacity: interpolate(c2, [0, 0.4], [0, 1], { extrapolateLeft: "clamp", extrapolateRight: "clamp" }),
2778
+ transform: `translateY(${interpolate(c2, [0, 1], [20, 0])}px) scale(${interpolate(c2, [0, 0.6, 1], [0.9, 1.04, 1])})`,
2779
+ }}
2780
+ >
2781
+ <div
2782
+ style={{
2783
+ display: "inline-flex",
2784
+ alignItems: "center",
2785
+ gap: 12,
2786
+ padding: "14px 22px",
2787
+ background: "rgba(98,95,255,0.10)",
2788
+ border: `1px solid rgba(98,95,255,0.40)`,
2789
+ borderRadius: 999,
2790
+ fontFamily: MONO,
2791
+ fontSize: 22,
2792
+ color: "#a4b3ff",
2793
+ fontWeight: 600,
2794
+ }}
2795
+ >
2796
+ <Img
2797
+ src={staticFile("captures/your-asset.png" /* REFERENCE-STRIP */)}
2798
+ style={{
2799
+ width: 22,
2800
+ height: 22,
2801
+ filter: "brightness(0) saturate(100%) invert(70%) sepia(15%) saturate(2300%) hue-rotate(214deg) brightness(108%) contrast(101%)",
2802
+ }}
2803
+ />
2804
+ claude-code setup
2805
+ </div>
2806
+ </div>
2807
+ </div>
2808
+
2809
+ {/* Closer */}
2810
+ <div
2811
+ style={{
2812
+ position: "absolute",
2813
+ top: 1280,
2814
+ left: 0,
2815
+ right: 0,
2816
+ textAlign: "center",
2817
+ padding: "0 80px",
2818
+ opacity: closerOp,
2819
+ transform: `translateY(${closerY}px)`,
2820
+ }}
2821
+ >
2822
+ <div
2823
+ style={{
2824
+ fontSize: 32,
2825
+ fontFamily: SERIF,
2826
+ fontStyle: "italic",
2827
+ color: C.fgMuted,
2828
+ letterSpacing: "-0.01em",
2829
+ lineHeight: 1.35,
2830
+ }}
2831
+ >
2832
+ Once Claude remembers what you taught it…<br />
2833
+ <span style={{ color: C.fg, fontWeight: 600 }}>
2834
+ you're done with every other memory tool.
2835
+ </span>
2836
+ </div>
2837
+ </div>
2838
+
2839
+ {/* Arrow pointing down to comment field */}
2840
+ <div
2841
+ style={{
2842
+ position: "absolute",
2843
+ left: 0,
2844
+ right: 0,
2845
+ bottom: H - SAFE_BOT_Y - 60,
2846
+ display: "flex",
2847
+ justifyContent: "center",
2848
+ opacity: arrowOp,
2849
+ transform: `translateY(${arrowY}px)`,
2850
+ }}
2851
+ >
2852
+ <svg width={92} height={92} viewBox="0 0 92 92">
2853
+ <defs>
2854
+ <linearGradient id="arr-grad" x1="0" y1="0" x2="0" y2="1">
2855
+ <stop offset="0%" stopColor={C.indigo} />
2856
+ <stop offset="100%" stopColor="#a4b3ff" />
2857
+ </linearGradient>
2858
+ </defs>
2859
+ <circle cx={46} cy={46} r={42} fill="rgba(98,95,255,0.12)" stroke="rgba(98,95,255,0.4)" strokeWidth={1.5} />
2860
+ <path
2861
+ d="M46 22 L46 64 M28 50 L46 68 L64 50"
2862
+ stroke="url(#arr-grad)"
2863
+ strokeWidth={5}
2864
+ strokeLinecap="round"
2865
+ strokeLinejoin="round"
2866
+ fill="none"
2867
+ />
2868
+ </svg>
2869
+ </div>
2870
+ </AbsoluteFill>
2871
+ );
2872
+ };
2873
+
2874
+ // ═══════════════════════════════════════════════════════════════════════════
2875
+ // COMPOSITION ROOT
2876
+ // ═══════════════════════════════════════════════════════════════════════════
2877
+
2878
+ export const MemPalaceReel: React.FC = () => {
2879
+ return (
2880
+ <AbsoluteFill style={{ background: C.bg }}>
2881
+ {/* REFERENCE-STRIP: <Audio> tag removed — bring your own voiceover */}
2882
+
2883
+ <Sequence from={SCENES.S1.from} durationInFrames={SCENES.S1.dur}>
2884
+ <Scene1 />
2885
+ </Sequence>
2886
+ <Sequence from={SCENES.S2.from} durationInFrames={SCENES.S2.dur}>
2887
+ <Scene2 />
2888
+ </Sequence>
2889
+ <Sequence from={SCENES.S3.from} durationInFrames={SCENES.S3.dur}>
2890
+ <Scene3 />
2891
+ </Sequence>
2892
+ <Sequence from={SCENES.S4.from} durationInFrames={SCENES.S4.dur}>
2893
+ <Scene4 />
2894
+ </Sequence>
2895
+ <Sequence from={SCENES.S5.from} durationInFrames={SCENES.S5.dur}>
2896
+ <Scene5 />
2897
+ </Sequence>
2898
+ <Sequence from={SCENES.S6.from} durationInFrames={SCENES.S6.dur}>
2899
+ <Scene6 />
2900
+ </Sequence>
2901
+ <Sequence from={SCENES.S7.from} durationInFrames={SCENES.S7.dur}>
2902
+ <Scene7 />
2903
+ </Sequence>
2904
+ <Sequence from={SCENES.S8.from} durationInFrames={SCENES.S8.dur}>
2905
+ <Scene8 />
2906
+ </Sequence>
2907
+ </AbsoluteFill>
2908
+ );
2909
+ };