@devinilabs/reelstack 1.3.2 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cli/scaffold.js +124 -16
- package/package.json +1 -1
- package/reference/dark/codedrop.tsx +908 -0
- package/reference/dark/gpt55.tsx +2608 -0
- package/reference/dark/resourcescta.tsx +609 -0
- package/reference/dark/skills.tsx +1460 -0
- package/reference/dark/stitch2.tsx +162 -0
- package/reference/glass/claudewatchcta.tsx +599 -0
- package/reference/glass/gstack.tsx +3020 -0
- package/reference/glass/jcode.tsx +2267 -0
- package/reference/glass/lilagents.tsx +2649 -0
- package/reference/paper/devini3d.tsx +1799 -0
- package/skill/SKILL.md +25 -16
|
@@ -0,0 +1,609 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* REFERENCE — ResourcesCTAReel (Family: dark)
|
|
3
|
+
*
|
|
4
|
+
* Canonical example of the dark family's motion vocabulary, frame-locked
|
|
5
|
+
* BEAT structure, and scene choreography. Bundled with ReelStack v1.4+
|
|
6
|
+
* for STUDY and as the source for the scaffold-from-reference flow.
|
|
7
|
+
*
|
|
8
|
+
* Asset imports stripped (look for REFERENCE-STRIP markers). Bring your own
|
|
9
|
+
* voiceover, brand SVGs, captures.
|
|
10
|
+
*
|
|
11
|
+
* License: study + adapt patterns OK. Verbatim re-publication as your own
|
|
12
|
+
* template NOT OK. See ReelStack LICENSE.
|
|
13
|
+
*
|
|
14
|
+
* Source: my-video/src/ResourcesCTAReel.tsx
|
|
15
|
+
* Bundled at: 2026-05-12T19:40:04.832Z
|
|
16
|
+
*/
|
|
17
|
+
import React from "react";
|
|
18
|
+
import {
|
|
19
|
+
useCurrentFrame,
|
|
20
|
+
useVideoConfig,
|
|
21
|
+
interpolate,
|
|
22
|
+
AbsoluteFill,
|
|
23
|
+
} from "remotion";
|
|
24
|
+
import { gsap } from "gsap";
|
|
25
|
+
import { ds } from "./designSystem";
|
|
26
|
+
|
|
27
|
+
// ═══════════════════════════════════════════════════════════════
|
|
28
|
+
// TOKENS
|
|
29
|
+
// ═══════════════════════════════════════════════════════════════
|
|
30
|
+
|
|
31
|
+
const FONT = ds.font.sans;
|
|
32
|
+
const MONO = ds.font.mono;
|
|
33
|
+
|
|
34
|
+
const C = {
|
|
35
|
+
bg: "#0a0a0b",
|
|
36
|
+
bgLift: "#141416",
|
|
37
|
+
surface: "#1a1a1d",
|
|
38
|
+
surfaceLift: "#222226",
|
|
39
|
+
border: "rgba(255,255,255,0.08)",
|
|
40
|
+
borderLoud: "rgba(255,255,255,0.14)",
|
|
41
|
+
fg: "#f5f5f7",
|
|
42
|
+
fgSoft: "#d1d1d6",
|
|
43
|
+
fgMuted: "#8e8e93",
|
|
44
|
+
fgDim: "#5a5a60",
|
|
45
|
+
yt: "#FF0033",
|
|
46
|
+
ytDeep: "#cc0029",
|
|
47
|
+
ytSoft: "#ff5577",
|
|
48
|
+
ytGlow: "rgba(255,0,51,0.45)",
|
|
49
|
+
gold: "#F0C23A",
|
|
50
|
+
} as const;
|
|
51
|
+
|
|
52
|
+
// ═══════════════════════════════════════════════════════════════
|
|
53
|
+
// GSAP EASES — industry-standard curves via gsap.parseEase
|
|
54
|
+
// ═══════════════════════════════════════════════════════════════
|
|
55
|
+
|
|
56
|
+
const ease = {
|
|
57
|
+
expoOut: gsap.parseEase("expo.out"),
|
|
58
|
+
power4Out: gsap.parseEase("power4.out"),
|
|
59
|
+
power3Out: gsap.parseEase("power3.out"),
|
|
60
|
+
power2InOut: gsap.parseEase("power2.inOut"),
|
|
61
|
+
backOut: gsap.parseEase("back.out(2)"),
|
|
62
|
+
backOutSoft: gsap.parseEase("back.out(1.4)"),
|
|
63
|
+
elasticOut: gsap.parseEase("elastic.out(1, 0.55)"),
|
|
64
|
+
circOut: gsap.parseEase("circ.out"),
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
// Tween driven by gsap ease — maps local frames to an output range
|
|
68
|
+
const tween = (
|
|
69
|
+
frame: number,
|
|
70
|
+
fps: number,
|
|
71
|
+
delaySec: number,
|
|
72
|
+
durSec: number,
|
|
73
|
+
from: number,
|
|
74
|
+
to: number,
|
|
75
|
+
easeFn: (t: number) => number = ease.expoOut,
|
|
76
|
+
) => {
|
|
77
|
+
const start = delaySec * fps;
|
|
78
|
+
const end = start + durSec * fps;
|
|
79
|
+
return interpolate(frame, [start, end], [from, to], {
|
|
80
|
+
easing: easeFn,
|
|
81
|
+
extrapolateLeft: "clamp",
|
|
82
|
+
extrapolateRight: "clamp",
|
|
83
|
+
});
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
// ═══════════════════════════════════════════════════════════════
|
|
87
|
+
// BACKGROUND — drifting spotlights, grid, grain, vignette
|
|
88
|
+
// ═══════════════════════════════════════════════════════════════
|
|
89
|
+
|
|
90
|
+
const Background: React.FC<{ frame: number }> = ({ frame }) => {
|
|
91
|
+
const drift = (Math.sin(frame * 0.004) + 1) * 0.5;
|
|
92
|
+
const drift2 = (Math.cos(frame * 0.003) + 1) * 0.5;
|
|
93
|
+
const vignettePulse = 0.38 + Math.sin(frame * 0.008) * 0.04;
|
|
94
|
+
|
|
95
|
+
return (
|
|
96
|
+
<AbsoluteFill>
|
|
97
|
+
<div style={{ width: "100%", height: "100%", background: C.bg }} />
|
|
98
|
+
<div
|
|
99
|
+
style={{
|
|
100
|
+
position: "absolute",
|
|
101
|
+
inset: 0,
|
|
102
|
+
background: `radial-gradient(ellipse 820px 920px at ${20 + drift * 55}% ${18 + drift2 * 36}%, rgba(255,0,51,0.15) 0%, transparent 62%)`,
|
|
103
|
+
}}
|
|
104
|
+
/>
|
|
105
|
+
<div
|
|
106
|
+
style={{
|
|
107
|
+
position: "absolute",
|
|
108
|
+
inset: 0,
|
|
109
|
+
background: `radial-gradient(ellipse 620px 820px at ${82 - drift * 48}% ${72 + drift2 * 22}%, rgba(255,100,130,0.08) 0%, transparent 55%)`,
|
|
110
|
+
}}
|
|
111
|
+
/>
|
|
112
|
+
<div
|
|
113
|
+
style={{
|
|
114
|
+
position: "absolute",
|
|
115
|
+
inset: 0,
|
|
116
|
+
backgroundImage: `
|
|
117
|
+
linear-gradient(rgba(255,255,255,0.035) 1px, transparent 1px),
|
|
118
|
+
linear-gradient(90deg, rgba(255,255,255,0.035) 1px, transparent 1px)
|
|
119
|
+
`,
|
|
120
|
+
backgroundSize: "72px 72px",
|
|
121
|
+
maskImage:
|
|
122
|
+
"radial-gradient(ellipse at 50% 50%, black 30%, transparent 85%)",
|
|
123
|
+
WebkitMaskImage:
|
|
124
|
+
"radial-gradient(ellipse at 50% 50%, black 30%, transparent 85%)",
|
|
125
|
+
}}
|
|
126
|
+
/>
|
|
127
|
+
<div
|
|
128
|
+
style={{
|
|
129
|
+
position: "absolute",
|
|
130
|
+
inset: 0,
|
|
131
|
+
opacity: 0.06,
|
|
132
|
+
mixBlendMode: "overlay",
|
|
133
|
+
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>")`,
|
|
134
|
+
}}
|
|
135
|
+
/>
|
|
136
|
+
<div
|
|
137
|
+
style={{
|
|
138
|
+
position: "absolute",
|
|
139
|
+
inset: 0,
|
|
140
|
+
background: `radial-gradient(ellipse at 50% 50%, transparent 42%, rgba(0,0,0,${vignettePulse}) 100%)`,
|
|
141
|
+
}}
|
|
142
|
+
/>
|
|
143
|
+
</AbsoluteFill>
|
|
144
|
+
);
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
// ═══════════════════════════════════════════════════════════════
|
|
148
|
+
// SAFE ZONE — YouTube Shorts: top ~280px, bottom ~410px
|
|
149
|
+
// ═══════════════════════════════════════════════════════════════
|
|
150
|
+
|
|
151
|
+
const SafeZone: React.FC<{ children: React.ReactNode }> = ({ children }) => (
|
|
152
|
+
<div
|
|
153
|
+
style={{
|
|
154
|
+
position: "absolute",
|
|
155
|
+
top: 280,
|
|
156
|
+
left: 60,
|
|
157
|
+
right: 140,
|
|
158
|
+
bottom: 410,
|
|
159
|
+
}}
|
|
160
|
+
>
|
|
161
|
+
{children}
|
|
162
|
+
</div>
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
// ═══════════════════════════════════════════════════════════════
|
|
166
|
+
// MAIN — 7s Resources + Like/Subscribe CTA
|
|
167
|
+
// ═══════════════════════════════════════════════════════════════
|
|
168
|
+
|
|
169
|
+
export const ResourcesCTAReel: React.FC = () => {
|
|
170
|
+
const frame = useCurrentFrame();
|
|
171
|
+
const { fps } = useVideoConfig();
|
|
172
|
+
const f = frame;
|
|
173
|
+
|
|
174
|
+
// ── Top eyebrow badge ─────────────────────────────────
|
|
175
|
+
const badgeOp = tween(f, fps, 0.05, 0.45, 0, 1, ease.expoOut);
|
|
176
|
+
const badgeY = tween(f, fps, 0.05, 0.6, -30, 0, ease.power4Out);
|
|
177
|
+
|
|
178
|
+
// ── Hero word reveal — "FOR · RESOURCES" ──────────────
|
|
179
|
+
const heroWords = ["FOR", "RESOURCES"];
|
|
180
|
+
const heroDelays = [0.2, 0.35];
|
|
181
|
+
|
|
182
|
+
// ── Secondary "check the" ─────────────────────────────
|
|
183
|
+
const subOp = tween(f, fps, 0.85, 0.45, 0, 1, ease.expoOut);
|
|
184
|
+
const subY = tween(f, fps, 0.85, 0.6, 20, 0, ease.power3Out);
|
|
185
|
+
|
|
186
|
+
// ── "PINNED COMMENT" gradient pop ─────────────────────
|
|
187
|
+
const pinnedScale = tween(f, fps, 1.05, 0.9, 0.6, 1, ease.backOut);
|
|
188
|
+
const pinnedOp = tween(f, fps, 1.05, 0.4, 0, 1, ease.expoOut);
|
|
189
|
+
const pinnedGlow = 0.35 + Math.sin(f * 0.12) * 0.15;
|
|
190
|
+
|
|
191
|
+
// ── Comment bubble icon ───────────────────────────────
|
|
192
|
+
const bubbleScale = tween(f, fps, 1.5, 0.7, 0, 1, ease.elasticOut);
|
|
193
|
+
const bubbleOp = tween(f, fps, 1.5, 0.3, 0, 1, ease.expoOut);
|
|
194
|
+
const bubbleBob = Math.sin(f * 0.14) * 6;
|
|
195
|
+
|
|
196
|
+
// ── Down arrow pointing to pinned area ────────────────
|
|
197
|
+
const arrowOp = tween(f, fps, 2.1, 0.4, 0, 1, ease.expoOut);
|
|
198
|
+
const arrowBounce = Math.sin(f * 0.18) * 10;
|
|
199
|
+
|
|
200
|
+
// ── Divider ───────────────────────────────────────────
|
|
201
|
+
const dividerScale = tween(f, fps, 2.3, 0.6, 0, 1, ease.power4Out);
|
|
202
|
+
|
|
203
|
+
// ── Action pills ──────────────────────────────────────
|
|
204
|
+
const likeOp = tween(f, fps, 2.7, 0.45, 0, 1, ease.expoOut);
|
|
205
|
+
const likeY = tween(f, fps, 2.7, 0.7, 30, 0, ease.backOutSoft);
|
|
206
|
+
|
|
207
|
+
const subscribeOp = tween(f, fps, 3.0, 0.45, 0, 1, ease.expoOut);
|
|
208
|
+
const subscribeY = tween(f, fps, 3.0, 0.7, 30, 0, ease.backOutSoft);
|
|
209
|
+
|
|
210
|
+
// Heart fill + pop at 3.9s
|
|
211
|
+
const heartFilled = f > 3.9 * fps;
|
|
212
|
+
const heartPop = tween(f, fps, 3.9, 0.55, 0, 1, ease.elasticOut);
|
|
213
|
+
const heartScale = heartFilled ? 1 + heartPop * 0.35 - heartPop * 0.25 : 1;
|
|
214
|
+
const heartBurstOp = tween(f, fps, 3.9, 0.8, 1, 0, ease.expoOut);
|
|
215
|
+
|
|
216
|
+
// Subscribe activates at 4.4s — bell ring + label flip
|
|
217
|
+
const subActive = f > 4.4 * fps;
|
|
218
|
+
const bellRingT = tween(f, fps, 4.4, 0.9, 0, 1, ease.power2InOut);
|
|
219
|
+
const bellTilt = Math.sin(bellRingT * Math.PI * 4) * (1 - bellRingT) * 14;
|
|
220
|
+
const subPulse = 0.45 + Math.sin(f * 0.15) * 0.2;
|
|
221
|
+
|
|
222
|
+
return (
|
|
223
|
+
<AbsoluteFill style={{ backgroundColor: C.bg, fontFamily: FONT }}>
|
|
224
|
+
<Background frame={f} />
|
|
225
|
+
|
|
226
|
+
<SafeZone>
|
|
227
|
+
{/* ── EYEBROW BADGE ─────────────────────────────── */}
|
|
228
|
+
<div
|
|
229
|
+
style={{
|
|
230
|
+
position: "absolute",
|
|
231
|
+
top: 0,
|
|
232
|
+
width: "100%",
|
|
233
|
+
display: "flex",
|
|
234
|
+
justifyContent: "center",
|
|
235
|
+
opacity: badgeOp,
|
|
236
|
+
transform: `translateY(${badgeY}px)`,
|
|
237
|
+
}}
|
|
238
|
+
>
|
|
239
|
+
<div
|
|
240
|
+
style={{
|
|
241
|
+
display: "inline-flex",
|
|
242
|
+
alignItems: "center",
|
|
243
|
+
gap: 12,
|
|
244
|
+
padding: "12px 22px",
|
|
245
|
+
borderRadius: 999,
|
|
246
|
+
background: "rgba(255,0,51,0.08)",
|
|
247
|
+
border: `1.5px solid ${C.yt}`,
|
|
248
|
+
boxShadow: `0 0 24px rgba(255,0,51,0.25)`,
|
|
249
|
+
}}
|
|
250
|
+
>
|
|
251
|
+
<div
|
|
252
|
+
style={{
|
|
253
|
+
width: 10,
|
|
254
|
+
height: 10,
|
|
255
|
+
borderRadius: "50%",
|
|
256
|
+
background: C.yt,
|
|
257
|
+
boxShadow: `0 0 12px ${C.yt}`,
|
|
258
|
+
}}
|
|
259
|
+
/>
|
|
260
|
+
<span
|
|
261
|
+
style={{
|
|
262
|
+
fontFamily: MONO,
|
|
263
|
+
fontSize: 22,
|
|
264
|
+
fontWeight: 600,
|
|
265
|
+
color: C.fgSoft,
|
|
266
|
+
letterSpacing: "0.18em",
|
|
267
|
+
textTransform: "uppercase",
|
|
268
|
+
}}
|
|
269
|
+
>
|
|
270
|
+
Pinned Comment
|
|
271
|
+
</span>
|
|
272
|
+
</div>
|
|
273
|
+
</div>
|
|
274
|
+
|
|
275
|
+
{/* ── HERO HEADLINE — "FOR RESOURCES" ───────────── */}
|
|
276
|
+
<div
|
|
277
|
+
style={{
|
|
278
|
+
position: "absolute",
|
|
279
|
+
top: 90,
|
|
280
|
+
width: "100%",
|
|
281
|
+
display: "flex",
|
|
282
|
+
flexDirection: "column",
|
|
283
|
+
alignItems: "center",
|
|
284
|
+
gap: 4,
|
|
285
|
+
fontFamily: FONT,
|
|
286
|
+
}}
|
|
287
|
+
>
|
|
288
|
+
{heroWords.map((w, i) => {
|
|
289
|
+
const op = tween(f, fps, heroDelays[i], 0.45, 0, 1, ease.expoOut);
|
|
290
|
+
const y = tween(f, fps, heroDelays[i], 0.7, 60, 0, ease.power4Out);
|
|
291
|
+
const scale = tween(
|
|
292
|
+
f,
|
|
293
|
+
fps,
|
|
294
|
+
heroDelays[i],
|
|
295
|
+
0.8,
|
|
296
|
+
0.82,
|
|
297
|
+
1,
|
|
298
|
+
ease.backOutSoft,
|
|
299
|
+
);
|
|
300
|
+
const isAccent = i === 1;
|
|
301
|
+
return (
|
|
302
|
+
<span
|
|
303
|
+
key={w}
|
|
304
|
+
style={{
|
|
305
|
+
display: "inline-block",
|
|
306
|
+
opacity: op,
|
|
307
|
+
transform: `translateY(${y}px) scale(${scale})`,
|
|
308
|
+
fontSize: isAccent ? 138 : 64,
|
|
309
|
+
fontWeight: isAccent ? 800 : 600,
|
|
310
|
+
letterSpacing: "-0.045em",
|
|
311
|
+
lineHeight: 0.95,
|
|
312
|
+
color: isAccent ? "transparent" : C.fgSoft,
|
|
313
|
+
background: isAccent
|
|
314
|
+
? `linear-gradient(135deg, ${C.fg} 0%, ${C.fg} 40%, ${C.ytSoft} 100%)`
|
|
315
|
+
: undefined,
|
|
316
|
+
WebkitBackgroundClip: isAccent ? "text" : undefined,
|
|
317
|
+
backgroundClip: isAccent ? "text" : undefined,
|
|
318
|
+
WebkitTextFillColor: isAccent ? "transparent" : undefined,
|
|
319
|
+
filter: isAccent
|
|
320
|
+
? `drop-shadow(0 0 40px rgba(255,255,255,0.15))`
|
|
321
|
+
: undefined,
|
|
322
|
+
textTransform: i === 0 ? "uppercase" : "none",
|
|
323
|
+
}}
|
|
324
|
+
>
|
|
325
|
+
{w}
|
|
326
|
+
</span>
|
|
327
|
+
);
|
|
328
|
+
})}
|
|
329
|
+
</div>
|
|
330
|
+
|
|
331
|
+
{/* ── "check the" ───────────────────────────────── */}
|
|
332
|
+
<div
|
|
333
|
+
style={{
|
|
334
|
+
position: "absolute",
|
|
335
|
+
top: 320,
|
|
336
|
+
width: "100%",
|
|
337
|
+
textAlign: "center",
|
|
338
|
+
opacity: subOp,
|
|
339
|
+
transform: `translateY(${subY}px)`,
|
|
340
|
+
fontFamily: FONT,
|
|
341
|
+
fontSize: 34,
|
|
342
|
+
fontWeight: 500,
|
|
343
|
+
color: C.fgMuted,
|
|
344
|
+
letterSpacing: "-0.01em",
|
|
345
|
+
}}
|
|
346
|
+
>
|
|
347
|
+
check the
|
|
348
|
+
</div>
|
|
349
|
+
|
|
350
|
+
{/* ── "PINNED COMMENT" gradient ─────────────────── */}
|
|
351
|
+
<div
|
|
352
|
+
style={{
|
|
353
|
+
position: "absolute",
|
|
354
|
+
top: 370,
|
|
355
|
+
width: "100%",
|
|
356
|
+
display: "flex",
|
|
357
|
+
alignItems: "center",
|
|
358
|
+
justifyContent: "center",
|
|
359
|
+
gap: 20,
|
|
360
|
+
opacity: pinnedOp,
|
|
361
|
+
transform: `scale(${pinnedScale})`,
|
|
362
|
+
}}
|
|
363
|
+
>
|
|
364
|
+
{/* Comment bubble icon */}
|
|
365
|
+
<div
|
|
366
|
+
style={{
|
|
367
|
+
width: 88,
|
|
368
|
+
height: 88,
|
|
369
|
+
borderRadius: 20,
|
|
370
|
+
background: `linear-gradient(135deg, ${C.yt}, ${C.ytDeep})`,
|
|
371
|
+
display: "flex",
|
|
372
|
+
alignItems: "center",
|
|
373
|
+
justifyContent: "center",
|
|
374
|
+
boxShadow: `0 14px 34px -8px ${C.ytGlow}, inset 0 1px 0 rgba(255,255,255,0.25)`,
|
|
375
|
+
opacity: bubbleOp,
|
|
376
|
+
transform: `scale(${bubbleScale}) translateY(${bubbleBob}px)`,
|
|
377
|
+
flexShrink: 0,
|
|
378
|
+
}}
|
|
379
|
+
>
|
|
380
|
+
<svg
|
|
381
|
+
width="50"
|
|
382
|
+
height="50"
|
|
383
|
+
viewBox="0 0 24 24"
|
|
384
|
+
fill="none"
|
|
385
|
+
stroke="#fff"
|
|
386
|
+
strokeWidth="2.4"
|
|
387
|
+
strokeLinejoin="round"
|
|
388
|
+
strokeLinecap="round"
|
|
389
|
+
>
|
|
390
|
+
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" />
|
|
391
|
+
</svg>
|
|
392
|
+
{/* Pin dot */}
|
|
393
|
+
<div
|
|
394
|
+
style={{
|
|
395
|
+
position: "absolute",
|
|
396
|
+
top: -6,
|
|
397
|
+
right: -6,
|
|
398
|
+
width: 22,
|
|
399
|
+
height: 22,
|
|
400
|
+
borderRadius: "50%",
|
|
401
|
+
background: C.gold,
|
|
402
|
+
border: `3px solid ${C.bg}`,
|
|
403
|
+
boxShadow: `0 0 14px rgba(240,194,58,0.6)`,
|
|
404
|
+
}}
|
|
405
|
+
/>
|
|
406
|
+
</div>
|
|
407
|
+
|
|
408
|
+
<span
|
|
409
|
+
style={{
|
|
410
|
+
fontFamily: FONT,
|
|
411
|
+
fontSize: 78,
|
|
412
|
+
fontWeight: 800,
|
|
413
|
+
letterSpacing: "-0.045em",
|
|
414
|
+
lineHeight: 1,
|
|
415
|
+
background: `linear-gradient(135deg, ${C.yt}, ${C.ytSoft})`,
|
|
416
|
+
WebkitBackgroundClip: "text",
|
|
417
|
+
backgroundClip: "text",
|
|
418
|
+
WebkitTextFillColor: "transparent",
|
|
419
|
+
textTransform: "uppercase",
|
|
420
|
+
filter: `drop-shadow(0 0 28px rgba(255,0,51,${pinnedGlow}))`,
|
|
421
|
+
}}
|
|
422
|
+
>
|
|
423
|
+
Pinned
|
|
424
|
+
</span>
|
|
425
|
+
</div>
|
|
426
|
+
|
|
427
|
+
{/* ── Down arrow hint ───────────────────────────── */}
|
|
428
|
+
<div
|
|
429
|
+
style={{
|
|
430
|
+
position: "absolute",
|
|
431
|
+
top: 520,
|
|
432
|
+
width: "100%",
|
|
433
|
+
display: "flex",
|
|
434
|
+
justifyContent: "center",
|
|
435
|
+
opacity: arrowOp,
|
|
436
|
+
transform: `translateY(${arrowBounce}px)`,
|
|
437
|
+
}}
|
|
438
|
+
>
|
|
439
|
+
<svg
|
|
440
|
+
width={48}
|
|
441
|
+
height={48}
|
|
442
|
+
viewBox="0 0 24 24"
|
|
443
|
+
fill="none"
|
|
444
|
+
stroke={C.fgMuted}
|
|
445
|
+
strokeWidth="2.4"
|
|
446
|
+
strokeLinecap="round"
|
|
447
|
+
strokeLinejoin="round"
|
|
448
|
+
>
|
|
449
|
+
<path d="M12 5v14M5 12l7 7 7-7" />
|
|
450
|
+
</svg>
|
|
451
|
+
</div>
|
|
452
|
+
|
|
453
|
+
{/* ── DIVIDER ───────────────────────────────────── */}
|
|
454
|
+
<div
|
|
455
|
+
style={{
|
|
456
|
+
position: "absolute",
|
|
457
|
+
top: 620,
|
|
458
|
+
width: "100%",
|
|
459
|
+
display: "flex",
|
|
460
|
+
justifyContent: "center",
|
|
461
|
+
}}
|
|
462
|
+
>
|
|
463
|
+
<div
|
|
464
|
+
style={{
|
|
465
|
+
height: 2,
|
|
466
|
+
width: `${dividerScale * 60}%`,
|
|
467
|
+
background: `linear-gradient(90deg, transparent, ${C.borderLoud}, transparent)`,
|
|
468
|
+
transformOrigin: "center",
|
|
469
|
+
}}
|
|
470
|
+
/>
|
|
471
|
+
</div>
|
|
472
|
+
|
|
473
|
+
{/* ── ACTION PILLS — Like + Subscribe ───────────── */}
|
|
474
|
+
<div
|
|
475
|
+
style={{
|
|
476
|
+
position: "absolute",
|
|
477
|
+
bottom: 20,
|
|
478
|
+
left: 0,
|
|
479
|
+
right: 0,
|
|
480
|
+
display: "flex",
|
|
481
|
+
flexDirection: "column",
|
|
482
|
+
gap: 20,
|
|
483
|
+
}}
|
|
484
|
+
>
|
|
485
|
+
{/* LIKE pill */}
|
|
486
|
+
<div
|
|
487
|
+
style={{
|
|
488
|
+
position: "relative",
|
|
489
|
+
opacity: likeOp,
|
|
490
|
+
transform: `translateY(${likeY}px)`,
|
|
491
|
+
padding: "22px 30px",
|
|
492
|
+
borderRadius: 22,
|
|
493
|
+
background: heartFilled
|
|
494
|
+
? `linear-gradient(135deg, rgba(255,0,51,0.16), ${C.surface})`
|
|
495
|
+
: C.surface,
|
|
496
|
+
border: `1.5px solid ${heartFilled ? C.yt : C.borderLoud}`,
|
|
497
|
+
display: "flex",
|
|
498
|
+
alignItems: "center",
|
|
499
|
+
gap: 22,
|
|
500
|
+
boxShadow: heartFilled
|
|
501
|
+
? `0 0 36px -10px ${C.ytGlow}, inset 0 1px 0 rgba(255,255,255,0.05)`
|
|
502
|
+
: `inset 0 1px 0 rgba(255,255,255,0.04)`,
|
|
503
|
+
}}
|
|
504
|
+
>
|
|
505
|
+
{/* Heart burst ring on fill */}
|
|
506
|
+
{heartFilled && (
|
|
507
|
+
<div
|
|
508
|
+
style={{
|
|
509
|
+
position: "absolute",
|
|
510
|
+
left: 38,
|
|
511
|
+
top: "50%",
|
|
512
|
+
width: 90,
|
|
513
|
+
height: 90,
|
|
514
|
+
marginTop: -45,
|
|
515
|
+
borderRadius: "50%",
|
|
516
|
+
border: `2.5px solid ${C.yt}`,
|
|
517
|
+
opacity: heartBurstOp * 0.6,
|
|
518
|
+
transform: `scale(${0.5 + (1 - heartBurstOp) * 1.2})`,
|
|
519
|
+
pointerEvents: "none",
|
|
520
|
+
}}
|
|
521
|
+
/>
|
|
522
|
+
)}
|
|
523
|
+
<svg
|
|
524
|
+
width={56}
|
|
525
|
+
height={56}
|
|
526
|
+
viewBox="0 0 24 24"
|
|
527
|
+
fill={heartFilled ? C.yt : "none"}
|
|
528
|
+
stroke={heartFilled ? C.yt : C.fg}
|
|
529
|
+
strokeWidth="2"
|
|
530
|
+
strokeLinejoin="round"
|
|
531
|
+
strokeLinecap="round"
|
|
532
|
+
style={{
|
|
533
|
+
transform: `scale(${heartScale})`,
|
|
534
|
+
flexShrink: 0,
|
|
535
|
+
filter: heartFilled
|
|
536
|
+
? `drop-shadow(0 0 14px ${C.ytGlow})`
|
|
537
|
+
: undefined,
|
|
538
|
+
}}
|
|
539
|
+
>
|
|
540
|
+
<path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z" />
|
|
541
|
+
</svg>
|
|
542
|
+
<span
|
|
543
|
+
style={{
|
|
544
|
+
fontFamily: FONT,
|
|
545
|
+
fontSize: 42,
|
|
546
|
+
fontWeight: 700,
|
|
547
|
+
color: C.fg,
|
|
548
|
+
letterSpacing: "-0.03em",
|
|
549
|
+
flex: 1,
|
|
550
|
+
}}
|
|
551
|
+
>
|
|
552
|
+
{heartFilled ? "Liked" : "Like"}
|
|
553
|
+
</span>
|
|
554
|
+
</div>
|
|
555
|
+
|
|
556
|
+
{/* SUBSCRIBE pill */}
|
|
557
|
+
<div
|
|
558
|
+
style={{
|
|
559
|
+
opacity: subscribeOp,
|
|
560
|
+
transform: `translateY(${subscribeY}px)`,
|
|
561
|
+
padding: "24px 30px",
|
|
562
|
+
borderRadius: 22,
|
|
563
|
+
background: subActive
|
|
564
|
+
? `linear-gradient(135deg, ${C.yt}, ${C.ytSoft})`
|
|
565
|
+
: `linear-gradient(135deg, ${C.yt}, ${C.ytDeep})`,
|
|
566
|
+
border: `1.5px solid ${subActive ? C.ytSoft : C.yt}`,
|
|
567
|
+
display: "flex",
|
|
568
|
+
alignItems: "center",
|
|
569
|
+
gap: 22,
|
|
570
|
+
boxShadow: `0 20px 44px -12px rgba(255,0,51,${subPulse}), 0 0 60px rgba(255,0,51,${subPulse * 0.4})`,
|
|
571
|
+
}}
|
|
572
|
+
>
|
|
573
|
+
<svg
|
|
574
|
+
width={56}
|
|
575
|
+
height={56}
|
|
576
|
+
viewBox="0 0 24 24"
|
|
577
|
+
fill="none"
|
|
578
|
+
stroke="#fff"
|
|
579
|
+
strokeWidth="2.4"
|
|
580
|
+
strokeLinejoin="round"
|
|
581
|
+
strokeLinecap="round"
|
|
582
|
+
style={{
|
|
583
|
+
transform: `rotate(${bellTilt}deg)`,
|
|
584
|
+
transformOrigin: "50% 20%",
|
|
585
|
+
flexShrink: 0,
|
|
586
|
+
}}
|
|
587
|
+
>
|
|
588
|
+
<path d="M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9" />
|
|
589
|
+
<path d="M10.3 21a1.94 1.94 0 0 0 3.4 0" />
|
|
590
|
+
</svg>
|
|
591
|
+
<span
|
|
592
|
+
style={{
|
|
593
|
+
fontFamily: FONT,
|
|
594
|
+
fontSize: 44,
|
|
595
|
+
fontWeight: 800,
|
|
596
|
+
color: "#fff",
|
|
597
|
+
letterSpacing: "-0.03em",
|
|
598
|
+
flex: 1,
|
|
599
|
+
textTransform: "uppercase",
|
|
600
|
+
}}
|
|
601
|
+
>
|
|
602
|
+
{subActive ? "Subscribed" : "Subscribe"}
|
|
603
|
+
</span>
|
|
604
|
+
</div>
|
|
605
|
+
</div>
|
|
606
|
+
</SafeZone>
|
|
607
|
+
</AbsoluteFill>
|
|
608
|
+
);
|
|
609
|
+
};
|