hyperframes 0.1.13 → 0.1.15

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 (31) hide show
  1. package/README.md +5 -1
  2. package/dist/cli.js +940 -380
  3. package/dist/hyperframe-runtime.js +8 -4
  4. package/dist/hyperframe.manifest.json +1 -1
  5. package/dist/hyperframe.runtime.iife.js +8 -4
  6. package/dist/skills/hyperframes-captions/SKILL.md +195 -29
  7. package/dist/skills/hyperframes-compose/SKILL.md +1 -0
  8. package/dist/skills/hyperframes-compose/house-style.md +2 -2
  9. package/dist/studio/assets/index-CLmYRLY-.css +1 -0
  10. package/dist/studio/assets/index-CRvFpc0E.js +84 -0
  11. package/dist/studio/index.html +2 -2
  12. package/dist/templates/_shared/CLAUDE.md +14 -2
  13. package/dist/templates/blank/compositions/captions.html +3 -0
  14. package/dist/templates/blank/index.html +1 -1
  15. package/dist/templates/decision-tree/compositions/decision_tree.html +420 -0
  16. package/dist/templates/decision-tree/index.html +56 -0
  17. package/dist/templates/generators.ts +32 -0
  18. package/dist/templates/kinetic-type/compositions/main-graphics.html +588 -0
  19. package/dist/templates/kinetic-type/index.html +61 -0
  20. package/dist/templates/nyt-graph/compositions/nyt-chart.html +450 -0
  21. package/dist/templates/nyt-graph/index.html +51 -0
  22. package/dist/templates/product-promo/assets/figma-cursors.svg +30 -0
  23. package/dist/templates/product-promo/assets/figma-logo-pieces.svg +15 -0
  24. package/dist/templates/product-promo/assets/figma-logo-pills.svg +6 -0
  25. package/dist/templates/product-promo/compositions/scene1-logo-intro.html +189 -0
  26. package/dist/templates/product-promo/compositions/scene2-4-canvas.html +570 -0
  27. package/dist/templates/product-promo/compositions/scene5-logo-outro.html +226 -0
  28. package/dist/templates/product-promo/index.html +158 -0
  29. package/package.json +2 -2
  30. package/dist/studio/assets/index-2uBPlHR_.css +0 -1
  31. package/dist/studio/assets/index-uQ8cgxb3.js +0 -92
@@ -4,8 +4,8 @@
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <title>HyperFrames Studio</title>
7
- <script type="module" crossorigin src="/assets/index-uQ8cgxb3.js"></script>
8
- <link rel="stylesheet" crossorigin href="/assets/index-2uBPlHR_.css">
7
+ <script type="module" crossorigin src="/assets/index-CRvFpc0E.js"></script>
8
+ <link rel="stylesheet" crossorigin href="/assets/index-CLmYRLY-.css">
9
9
  </head>
10
10
  <body>
11
11
  <div id="root"></div>
@@ -7,7 +7,7 @@
7
7
  | Skill | Command | When to use |
8
8
  | ------------------------ | ----------------------- | ------------------------------------------------------------------------------------------------------ |
9
9
  | **hyperframes-compose** | `/hyperframes-compose` | Creating or editing ANY HTML composition — videos, animations, title cards, overlays, sub-compositions |
10
- | **hyperframes-captions** | `/hyperframes-captions` | Building captions from whisper transcripts style detection, per-word styling |
10
+ | **hyperframes-captions** | `/hyperframes-captions` | Any text synced to audio: captions, subtitles, lyrics, karaoke. Also covers transcription strategy. |
11
11
  | **gsap-core** | `/gsap-core` | GSAP tweens: `gsap.to()`, `from()`, `fromTo()`, easing, stagger, defaults |
12
12
  | **gsap-timeline** | `/gsap-timeline` | Timeline sequencing, position parameter, labels, nesting |
13
13
  | **gsap-performance** | `/gsap-performance` | Animation performance — transforms over layout props, will-change, batching |
@@ -21,7 +21,9 @@
21
21
  ```bash
22
22
  npx hyperframes dev # preview in browser (studio editor)
23
23
  npx hyperframes render # render to MP4
24
- npx hyperframes lint # validate compositions
24
+ npx hyperframes lint # validate compositions (errors + warnings)
25
+ npx hyperframes lint --verbose # include info-level findings
26
+ npx hyperframes lint --json # machine-readable output for CI
25
27
  npx hyperframes docs <topic> # reference docs in terminal
26
28
  ```
27
29
 
@@ -48,6 +50,16 @@ https://hyperframes.heygen.com/llms.txt
48
50
  - `meta.json` — project metadata (id, name)
49
51
  - `transcript.json` — whisper word-level transcript (if generated)
50
52
 
53
+ ## Linting — ALWAYS RUN AFTER CHANGES
54
+
55
+ After creating or editing any `.html` composition, **always** run the linter before considering the task complete:
56
+
57
+ ```bash
58
+ npx hyperframes lint
59
+ ```
60
+
61
+ Fix all errors before presenting the result. Warnings are informational and usually safe to ignore.
62
+
51
63
  ## Key Rules
52
64
 
53
65
  1. Every timed element needs `data-start`, `data-duration`, and `data-track-index`
@@ -37,6 +37,8 @@
37
37
  0 2px 8px rgba(0, 0, 0, 0.8),
38
38
  0 0 2px rgba(0, 0, 0, 0.9);
39
39
  white-space: nowrap;
40
+ max-width: 1600px;
41
+ overflow: hidden;
40
42
  }
41
43
  </style>
42
44
 
@@ -83,6 +85,7 @@
83
85
  index < lines.length - 1 ? Math.min(line.end, lines[index + 1].start) : line.end;
84
86
 
85
87
  tl.to(el, { opacity: 0, y: -10, duration: 0.25, ease: "power2.in" }, hideTime - 0.25);
88
+ tl.set(el, { opacity: 0, visibility: "hidden" }, hideTime);
86
89
  });
87
90
 
88
91
  window.__timelines["captions"] = tl;
@@ -31,10 +31,10 @@
31
31
  >
32
32
  <video
33
33
  id="a-roll"
34
+ class="clip"
34
35
  src="__VIDEO_SRC__"
35
36
  muted
36
37
  playsinline
37
- data-start="0"
38
38
  data-duration="__VIDEO_DURATION__"
39
39
  data-track-index="0"
40
40
  style="position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover"
@@ -0,0 +1,420 @@
1
+ <template id="decision-tree-template">
2
+ <div data-composition-id="decision-tree" data-width="1920" data-height="1080" data-duration="12">
3
+ <div class="canvas">
4
+ <svg class="connectors" width="1920" height="1080" viewBox="0 0 1920 1080">
5
+ <!-- Level 1 to 2 -->
6
+ <path
7
+ id="path-1-L"
8
+ class="connector"
9
+ d="M 960 130 L 960 200 L 600 200 L 600 270"
10
+ fill="none"
11
+ stroke="#333"
12
+ stroke-width="4"
13
+ />
14
+ <path
15
+ id="path-1-R"
16
+ class="connector"
17
+ d="M 960 130 L 960 200 L 1320 200 L 1320 270"
18
+ fill="none"
19
+ stroke="#333"
20
+ stroke-width="4"
21
+ />
22
+
23
+ <!-- Level 2 to 3 (Left side) -->
24
+ <path
25
+ id="path-2-LL"
26
+ class="connector"
27
+ d="M 600 330 L 600 400 L 400 400 L 400 470"
28
+ fill="none"
29
+ stroke="#333"
30
+ stroke-width="4"
31
+ />
32
+ <path
33
+ id="path-2-LR"
34
+ class="connector"
35
+ d="M 600 330 L 600 400 L 800 400 L 800 470"
36
+ fill="none"
37
+ stroke="#333"
38
+ stroke-width="4"
39
+ />
40
+
41
+ <!-- Level 2 to 3 (Right side) -->
42
+ <path
43
+ id="path-2-RL"
44
+ class="connector"
45
+ d="M 1320 330 L 1320 400 L 1120 400 L 1120 470"
46
+ fill="none"
47
+ stroke="#333"
48
+ stroke-width="4"
49
+ />
50
+ <path
51
+ id="path-2-RR"
52
+ class="connector"
53
+ d="M 1320 330 L 1320 400 L 1520 400 L 1520 470"
54
+ fill="none"
55
+ stroke="#333"
56
+ stroke-width="4"
57
+ />
58
+ </svg>
59
+
60
+ <div id="label-yes" class="connector-label">Yes</div>
61
+ <div id="label-not-sure" class="connector-label">Not sure</div>
62
+
63
+ <!-- Level 1 -->
64
+ <div id="node-root" class="node sticky yellow" style="left: 960px; top: 100px">
65
+ Should I learn to code?
66
+ </div>
67
+
68
+ <!-- Level 2 -->
69
+ <div id="node-yes" class="node sticky green" style="left: 600px; top: 300px">Yes</div>
70
+ <div id="node-not-sure" class="node sticky peach" style="left: 1320px; top: 300px">
71
+ Not sure
72
+ </div>
73
+
74
+ <!-- Level 3 -->
75
+ <div id="node-python" class="node sticky lavender" style="left: 400px; top: 500px">
76
+ <div style="position: relative; display: inline-block">
77
+ <span id="python-text">Start with Pythom</span>
78
+ <svg class="squiggle-container" width="64" height="6" viewBox="0 0 64 6">
79
+ <path
80
+ id="squiggle"
81
+ d="M 0 3 Q 2 0 4 3 T 8 3 T 12 3 T 16 3 T 20 3 T 24 3 T 28 3 T 32 3 T 36 3 T 40 3 T 44 3 T 48 3 T 52 3 T 56 3 T 60 3 T 64 3"
82
+ fill="none"
83
+ stroke="#E53935"
84
+ stroke-width="2"
85
+ />
86
+ </svg>
87
+ </div>
88
+ </div>
89
+ <div id="node-nocode" class="node sticky blue" style="left: 800px; top: 500px">
90
+ Try no-code first
91
+ </div>
92
+ <div id="node-website" class="node sticky pink" style="left: 1120px; top: 500px">
93
+ Build a personal website
94
+ </div>
95
+ <div id="node-course" class="node sticky yellow" style="left: 1520px; top: 500px">
96
+ Take a free intro course
97
+ </div>
98
+
99
+ <!-- Cursor -->
100
+ <div id="cursor" class="cursor-container">
101
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none">
102
+ <path
103
+ d="M5.65376 12.3673H5.46026L5.31717 12.4976L0.500002 16.8829L0.500002 1.19841L11.7841 12.3673H5.65376Z"
104
+ fill="white"
105
+ stroke="black"
106
+ />
107
+ </svg>
108
+ <div class="cursor-tag">You</div>
109
+ </div>
110
+
111
+ <!-- Emoji -->
112
+ <div id="emoji-thumb" class="emoji">👍</div>
113
+
114
+ <!-- Overlay -->
115
+ <div id="fade-overlay"></div>
116
+ </div>
117
+
118
+ <style>
119
+ [data-composition-id="decision-tree"] {
120
+ width: 1920px;
121
+ height: 1080px;
122
+ background-color: #ffffff;
123
+ background-image: radial-gradient(#e5e5e5 1px, transparent 1px);
124
+ background-size: 20px 20px;
125
+ position: absolute;
126
+ overflow: hidden;
127
+ font-family: "Inter", sans-serif;
128
+ }
129
+
130
+ [data-composition-id="decision-tree"] .canvas {
131
+ width: 100%;
132
+ height: 100%;
133
+ position: absolute;
134
+ transform: scale(1.2);
135
+ transform-origin: 960px 300px;
136
+ }
137
+
138
+ [data-composition-id="decision-tree"] .node {
139
+ position: absolute;
140
+ transform: translate(-50%, -50%) scale(0);
141
+ padding: 16px 24px;
142
+ border-radius: 12px;
143
+ font-size: 18px;
144
+ font-weight: 500;
145
+ color: #333;
146
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
147
+ white-space: nowrap;
148
+ display: flex;
149
+ flex-direction: column;
150
+ align-items: center;
151
+ justify-content: center;
152
+ z-index: 10;
153
+ }
154
+
155
+ [data-composition-id="decision-tree"] .node.yellow {
156
+ background-color: #e8d44d;
157
+ }
158
+ [data-composition-id="decision-tree"] .node.green {
159
+ background-color: #c2e8a0;
160
+ }
161
+ [data-composition-id="decision-tree"] .node.peach {
162
+ background-color: #f5c5a3;
163
+ }
164
+ [data-composition-id="decision-tree"] .node.lavender {
165
+ background-color: #d4c5f9;
166
+ }
167
+ [data-composition-id="decision-tree"] .node.blue {
168
+ background-color: #a8d8f0;
169
+ }
170
+ [data-composition-id="decision-tree"] .node.pink {
171
+ background-color: #f8b4c8;
172
+ }
173
+
174
+ [data-composition-id="decision-tree"] .connectors {
175
+ position: absolute;
176
+ top: 0;
177
+ left: 0;
178
+ z-index: 5;
179
+ pointer-events: none;
180
+ }
181
+
182
+ [data-composition-id="decision-tree"] .connector {
183
+ stroke-dasharray: 1000;
184
+ stroke-dashoffset: 1000;
185
+ }
186
+
187
+ [data-composition-id="decision-tree"] .connector-label {
188
+ position: absolute;
189
+ font-size: 14px;
190
+ font-weight: 600;
191
+ color: #333;
192
+ background: white;
193
+ padding: 2px 6px;
194
+ border-radius: 4px;
195
+ opacity: 0;
196
+ z-index: 6;
197
+ transform: translate(-50%, -50%);
198
+ }
199
+
200
+ #label-yes {
201
+ left: 780px;
202
+ top: 230px;
203
+ }
204
+ #label-not-sure {
205
+ left: 1140px;
206
+ top: 230px;
207
+ }
208
+
209
+ [data-composition-id="decision-tree"] .cursor-container {
210
+ position: absolute;
211
+ left: 1920px;
212
+ top: 1080px;
213
+ z-index: 100;
214
+ pointer-events: none;
215
+ display: flex;
216
+ align-items: flex-start;
217
+ }
218
+
219
+ [data-composition-id="decision-tree"] .cursor-tag {
220
+ background-color: #9747ff;
221
+ color: white;
222
+ padding: 2px 8px;
223
+ border-radius: 4px;
224
+ font-size: 12px;
225
+ font-weight: 600;
226
+ margin-left: 4px;
227
+ margin-top: 12px;
228
+ }
229
+
230
+ [data-composition-id="decision-tree"] .squiggle-container {
231
+ position: absolute;
232
+ right: 0;
233
+ bottom: -4px;
234
+ opacity: 0;
235
+ }
236
+
237
+ [data-composition-id="decision-tree"] .emoji {
238
+ position: absolute;
239
+ font-size: 32px;
240
+ left: 510px;
241
+ top: 500px;
242
+ transform: translate(-50%, -50%) scale(0);
243
+ z-index: 20;
244
+ }
245
+
246
+ [data-composition-id="decision-tree"] #fade-overlay {
247
+ position: absolute;
248
+ top: 0;
249
+ left: 0;
250
+ width: 100%;
251
+ height: 100%;
252
+ background-color: white;
253
+ opacity: 0;
254
+ z-index: 1000;
255
+ pointer-events: none;
256
+ }
257
+
258
+ .selection-border {
259
+ position: absolute;
260
+ top: -4px;
261
+ left: -4px;
262
+ right: -4px;
263
+ bottom: -4px;
264
+ border: 2px solid #0b84f3;
265
+ border-radius: 16px;
266
+ pointer-events: none;
267
+ opacity: 0;
268
+ }
269
+
270
+ .text-highlight {
271
+ background-color: #d0e4ff;
272
+ }
273
+ </style>
274
+
275
+ <script>
276
+ (function () {
277
+ const tl = gsap.timeline({ paused: true });
278
+
279
+ // 1. Root node scales from 0→100% over 0.4s
280
+ tl.to("#node-root", { scale: 1, duration: 0.4, ease: "power2.out" });
281
+
282
+ // 2. Hold 0.6s
283
+ tl.addLabel("hold1", "+=0.6");
284
+
285
+ // 3. Two elbow connectors draw downward-left and downward-right simultaneously (0.5s)
286
+ tl.to(
287
+ ".connector#path-1-L, .connector#path-1-R",
288
+ {
289
+ strokeDashoffset: 0,
290
+ duration: 0.5,
291
+ ease: "none",
292
+ },
293
+ "hold1",
294
+ );
295
+
296
+ // Labels fade in at midpoint
297
+ tl.to(
298
+ "#label-yes, #label-not-sure",
299
+ {
300
+ opacity: 1,
301
+ duration: 0.2,
302
+ },
303
+ "hold1+=0.25",
304
+ );
305
+
306
+ // 4. Level 2 nodes scale in at endpoints with 0.2s stagger
307
+ tl.to("#node-yes", { scale: 1, duration: 0.4, ease: "back.out(1.7)" }, "hold1+=0.4");
308
+ tl.to("#node-not-sure", { scale: 1, duration: 0.4, ease: "back.out(1.7)" }, "hold1+=0.6");
309
+
310
+ // 5. Hold 0.5s
311
+ tl.addLabel("hold2", "+=0.5");
312
+
313
+ // 6. Four connectors draw down from level 2 (0.4s)
314
+ tl.to(
315
+ ".connector#path-2-LL, .connector#path-2-LR, .connector#path-2-RL, .connector#path-2-RR",
316
+ {
317
+ strokeDashoffset: 0,
318
+ duration: 0.4,
319
+ ease: "none",
320
+ },
321
+ "hold2",
322
+ );
323
+
324
+ // 7. Four leaf nodes scale in with 0.15s stagger
325
+ const leafNodes = ["#node-python", "#node-nocode", "#node-website", "#node-course"];
326
+ leafNodes.forEach((id, i) => {
327
+ tl.to(id, { scale: 1, duration: 0.4, ease: "back.out(1.7)" }, `hold2+=${0.3 + i * 0.15}`);
328
+ });
329
+
330
+ // Red wavy underline appears with text
331
+ tl.to(".squiggle-container", { opacity: 1, duration: 0.1 }, "hold2+=0.3");
332
+
333
+ // 8. Hold 0.8s
334
+ tl.addLabel("hold3", "+=0.8");
335
+
336
+ // 9. Cursor drifts in from bottom-right (1s) to lavender sticky
337
+ tl.to(
338
+ "#cursor",
339
+ {
340
+ left: 450,
341
+ top: 540,
342
+ duration: 1,
343
+ ease: "power1.inOut",
344
+ },
345
+ "hold3",
346
+ );
347
+
348
+ // 10. Cursor clicks. Selection border appears.
349
+ tl.add(() => {
350
+ const node = document.querySelector("#node-python");
351
+ if (!node) return;
352
+ if (!node.querySelector(".selection-border")) {
353
+ const border = document.createElement("div");
354
+ border.className = "selection-border";
355
+ node.appendChild(border);
356
+ }
357
+ gsap.set(".selection-border", { opacity: 1 });
358
+ }, "hold3+=1");
359
+
360
+ // Visual click feedback
361
+ tl.to("#cursor", { scale: 0.8, duration: 0.05, yoyo: true, repeat: 1 }, "hold3+=1");
362
+
363
+ // 11. Hold 0.3s
364
+ tl.addLabel("hold4", "+=0.3");
365
+
366
+ // 12. Cursor double-clicks "Pythom" — highlight in blue
367
+ tl.to("#cursor", { scale: 0.8, duration: 0.05, yoyo: true, repeat: 3 }, "hold4");
368
+ tl.add(() => {
369
+ const textEl = document.querySelector("#python-text");
370
+ if (!textEl) return;
371
+ textEl.innerHTML = 'Start with <span class="text-highlight">Pythom</span>';
372
+ }, "hold4+=0.1");
373
+
374
+ // 13. Hold 0.2s
375
+ tl.addLabel("hold5", "+=0.2");
376
+
377
+ // 14. Highlighted text deletes and "Python" types in (fast)
378
+ // Squiggle disappears when last char lands
379
+ const typingStart = tl.labels["hold5"];
380
+ const words = ["P", "Py", "Pyt", "Pyth", "Pytho", "Python"];
381
+ words.forEach((word, i) => {
382
+ tl.add(
383
+ () => {
384
+ const el = document.querySelector("#python-text");
385
+ if (el) el.innerHTML = `Start with <span class="text-highlight">${word}</span>`;
386
+ },
387
+ typingStart + i * 0.05,
388
+ );
389
+ });
390
+
391
+ // Remove highlight and squiggle at the end of typing
392
+ const typingEnd = typingStart + words.length * 0.05;
393
+ tl.add(() => {
394
+ const pyEl = document.querySelector("#python-text");
395
+ if (pyEl) pyEl.innerHTML = "Start with Python";
396
+ gsap.set(".squiggle-container", { opacity: 0 });
397
+ }, typingEnd);
398
+
399
+ // 15. Hold 0.5s
400
+ tl.addLabel("hold6", typingEnd + 0.5);
401
+
402
+ // 16. Cursor moves slightly and clicks away to deselect
403
+ tl.to("#cursor", { left: 500, top: 580, duration: 0.3 }, "hold6");
404
+ tl.to("#cursor", { scale: 0.8, duration: 0.05, yoyo: true, repeat: 1 }, "hold6+=0.3");
405
+ tl.to(".selection-border", { opacity: 0, duration: 0.1 }, "hold6+=0.3");
406
+
407
+ // 17. 👍 emoji pops in
408
+ tl.to("#emoji-thumb", { scale: 1, duration: 0.15, ease: "back.out(2)" }, "hold6+=0.4");
409
+
410
+ // 18. Hold 2s
411
+ tl.addLabel("hold7", "+=2");
412
+
413
+ // 19. Fade entire screen to white over 0.5s
414
+ tl.to("#fade-overlay", { opacity: 1, duration: 0.5 }, "hold7");
415
+
416
+ window.__timelines["decision-tree"] = tl;
417
+ })();
418
+ </script>
419
+ </div>
420
+ </template>
@@ -0,0 +1,56 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=1920, height=1080" />
6
+ <title>Decision Tree</title>
7
+ <script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
8
+ <style>
9
+ * {
10
+ margin: 0;
11
+ padding: 0;
12
+ box-sizing: border-box;
13
+ }
14
+ html,
15
+ body {
16
+ margin: 0;
17
+ width: 1920px;
18
+ height: 1080px;
19
+ overflow: hidden;
20
+ background: #ffffff;
21
+ font-family: "Inter", sans-serif;
22
+ }
23
+ </style>
24
+ <link
25
+ href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=block"
26
+ rel="stylesheet"
27
+ />
28
+ </head>
29
+ <body>
30
+ <div
31
+ id="root"
32
+ data-composition-id="main"
33
+ data-start="0"
34
+ data-duration="15"
35
+ data-width="1920"
36
+ data-height="1080"
37
+ >
38
+ <div
39
+ id="decision-tree-comp"
40
+ data-composition-id="decision-tree"
41
+ data-composition-src="compositions/decision_tree.html"
42
+ data-start="0"
43
+ data-duration="15"
44
+ data-track-index="0"
45
+ data-width="1920"
46
+ data-height="1080"
47
+ ></div>
48
+ </div>
49
+
50
+ <script>
51
+ window.__timelines = window.__timelines || {};
52
+ const tl = gsap.timeline({ paused: true });
53
+ window.__timelines["main"] = tl;
54
+ </script>
55
+ </body>
56
+ </html>
@@ -0,0 +1,32 @@
1
+ export type TemplateId =
2
+ | "blank"
3
+ | "warm-grain"
4
+ | "play-mode"
5
+ | "swiss-grid"
6
+ | "vignelli"
7
+ | "decision-tree"
8
+ | "kinetic-type"
9
+ | "product-promo"
10
+ | "nyt-graph";
11
+
12
+ export interface TemplateOption {
13
+ id: TemplateId;
14
+ label: string;
15
+ hint: string;
16
+ }
17
+
18
+ export const TEMPLATES: TemplateOption[] = [
19
+ { id: "blank", label: "Blank", hint: "Empty composition — just the scaffolding" },
20
+ { id: "warm-grain", label: "Warm Grain", hint: "Cream aesthetic with grain texture" },
21
+ { id: "play-mode", label: "Play Mode", hint: "Playful elastic animations" },
22
+ { id: "swiss-grid", label: "Swiss Grid", hint: "Structured grid layout" },
23
+ { id: "vignelli", label: "Vignelli", hint: "Bold typography with red accents" },
24
+ { id: "decision-tree", label: "Decision Tree", hint: "Animated flowchart with branching paths" },
25
+ { id: "kinetic-type", label: "Kinetic Type", hint: "Bold kinetic typography promo" },
26
+ {
27
+ id: "product-promo",
28
+ label: "Product Promo",
29
+ hint: "Multi-scene product showcase with SVG assets",
30
+ },
31
+ { id: "nyt-graph", label: "NYT Graph", hint: "Animated data chart in print editorial style" },
32
+ ];