@vanillaskyai/video 0.1.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 (162) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/LICENSE +201 -0
  3. package/PUBLIC-API.md +276 -0
  4. package/README.md +152 -0
  5. package/SECURITY.md +22 -0
  6. package/SUPPORT.md +28 -0
  7. package/bin/vanillasky.js +3 -0
  8. package/dist/bg-confetti-WNQXS7ZS.js +89 -0
  9. package/dist/bg-emoji-ETI47GLS.js +150 -0
  10. package/dist/bg-media-SVUZAMGX.js +99 -0
  11. package/dist/brand-message-F3RD4P3P.js +253 -0
  12. package/dist/builtin-server-F7YXRVCH.js +8 -0
  13. package/dist/catalog-types-BIhSpOWK.d.ts +74 -0
  14. package/dist/chart-bar-CNSC7BNK.js +304 -0
  15. package/dist/chart-counter-MCRHKN77.js +299 -0
  16. package/dist/chart-progress-ring-RMI4A7ZK.js +296 -0
  17. package/dist/check-runtime.d.ts +15 -0
  18. package/dist/check-runtime.js +102 -0
  19. package/dist/chunk-2E6T633S.js +27 -0
  20. package/dist/chunk-352ZZCET.js +83 -0
  21. package/dist/chunk-73NTSFFI.js +81 -0
  22. package/dist/chunk-A2X45FET.js +46 -0
  23. package/dist/chunk-ABPKM4GK.js +187 -0
  24. package/dist/chunk-ABZACD4G.js +216 -0
  25. package/dist/chunk-BCRUUJ6A.js +756 -0
  26. package/dist/chunk-CHLGW22F.js +3551 -0
  27. package/dist/chunk-CJ7F2CE6.js +104 -0
  28. package/dist/chunk-CR7FE7BW.js +109 -0
  29. package/dist/chunk-E7CL7UPB.js +109 -0
  30. package/dist/chunk-EE6PK6XC.js +18 -0
  31. package/dist/chunk-ERY4S6QV.js +385 -0
  32. package/dist/chunk-EVG2ZYIY.js +125 -0
  33. package/dist/chunk-FNAEQ7QO.js +155 -0
  34. package/dist/chunk-FQ47WXAJ.js +38 -0
  35. package/dist/chunk-FSG2PYWG.js +336 -0
  36. package/dist/chunk-GHJEAP4O.js +504 -0
  37. package/dist/chunk-GRMUV7QY.js +262 -0
  38. package/dist/chunk-IFOW65Z6.js +205 -0
  39. package/dist/chunk-IXCUY3B3.js +786 -0
  40. package/dist/chunk-L5BTTCZV.js +97 -0
  41. package/dist/chunk-LMG3GHLL.js +107 -0
  42. package/dist/chunk-OOPIETLW.js +101 -0
  43. package/dist/chunk-P22HHFDK.js +43 -0
  44. package/dist/chunk-QAAO7OAY.js +19 -0
  45. package/dist/chunk-S7HFLYNN.js +35 -0
  46. package/dist/chunk-SPVTJH3F.js +24 -0
  47. package/dist/chunk-W2XJKG6C.js +3177 -0
  48. package/dist/chunk-XB7LH4J2.js +301 -0
  49. package/dist/chunk-XGRA2MUV.js +242 -0
  50. package/dist/chunk-XTOXFMLM.js +105 -0
  51. package/dist/chunk-YC5MFXA5.js +132 -0
  52. package/dist/cli.d.ts +1 -0
  53. package/dist/cli.js +1681 -0
  54. package/dist/compose-video-CN3VSWST.js +13 -0
  55. package/dist/cta-logo-SVA5GP2A.js +209 -0
  56. package/dist/cta-media-E7VLLK42.js +215 -0
  57. package/dist/events-_wfjbmEp.d.ts +81 -0
  58. package/dist/incoming-call-62MFZL3Z.js +320 -0
  59. package/dist/index.d.ts +15 -0
  60. package/dist/index.js +14 -0
  61. package/dist/infographic-before-after-AXCDAWND.js +460 -0
  62. package/dist/infographic-feature-list-GW5GUE6O.js +266 -0
  63. package/dist/infographic-problem-solution-7NURJANA.js +340 -0
  64. package/dist/infographic-stat-row-J7IZR6ZN.js +241 -0
  65. package/dist/infographic-steps-VSZM6F5M.js +400 -0
  66. package/dist/kit-BMNcHqa9.d.ts +79 -0
  67. package/dist/prompt-input-LOUCRXCY.js +236 -0
  68. package/dist/react.d.ts +81 -0
  69. package/dist/react.js +835 -0
  70. package/dist/reaction-QG4CYZLQ.js +119 -0
  71. package/dist/server.d.ts +141 -0
  72. package/dist/server.js +498 -0
  73. package/dist/showcase-code-RUTFZR3H.js +351 -0
  74. package/dist/showcase-phone-6CP6XEU5.js +272 -0
  75. package/dist/showcase-terminal-MGAEQWPR.js +306 -0
  76. package/dist/showcase-web-Y4W2HVRJ.js +390 -0
  77. package/dist/social-conversation-75SD2637.js +695 -0
  78. package/dist/social-milestone-XI2QE5BO.js +292 -0
  79. package/dist/social-notification-2UOHQCUY.js +244 -0
  80. package/dist/social-review-stack-FDYZGZTJ.js +239 -0
  81. package/dist/social-testimonial-OQ7N52GJ.js +284 -0
  82. package/dist/social-tweet-5OLCNPPC.js +295 -0
  83. package/dist/state-DZcKuS32.d.ts +3 -0
  84. package/dist/system-prompt-A6UXNTPK.js +8 -0
  85. package/dist/template-catalog.d.ts +24 -0
  86. package/dist/template-catalog.js +6 -0
  87. package/dist/templates.d.ts +25 -0
  88. package/dist/templates.js +19 -0
  89. package/dist/test.d.ts +149 -0
  90. package/dist/test.js +272 -0
  91. package/dist/text-stream-6XHC7J47.js +10 -0
  92. package/dist/types-B-PZR6G_.d.ts +208 -0
  93. package/dist/validate-T7GBU2YF.js +8 -0
  94. package/docs/agent-integration.md +39 -0
  95. package/docs/architecture.md +84 -0
  96. package/docs/branding-and-personalization.md +87 -0
  97. package/docs/concepts.md +106 -0
  98. package/docs/custom-templates.md +329 -0
  99. package/docs/customization.md +141 -0
  100. package/docs/errors.md +70 -0
  101. package/docs/getting-started.md +111 -0
  102. package/docs/input-and-first-scene.md +41 -0
  103. package/docs/integrate-nextjs.md +79 -0
  104. package/docs/media-and-audio.md +69 -0
  105. package/docs/motion-and-effects.md +107 -0
  106. package/docs/persistence.md +95 -0
  107. package/docs/production.md +122 -0
  108. package/docs/prompt-and-input.md +185 -0
  109. package/docs/provider-integration.md +115 -0
  110. package/docs/reference/protocol.md +96 -0
  111. package/docs/reference/provider-adapters.md +158 -0
  112. package/docs/responsive-orientation.md +37 -0
  113. package/docs/security.md +38 -0
  114. package/docs/streaming-protocol.md +15 -0
  115. package/docs/testing.md +137 -0
  116. package/docs/use-cases.md +59 -0
  117. package/examples/custom-template/README.md +19 -0
  118. package/examples/custom-template/minimal-text.tsx +82 -0
  119. package/examples/custom-template/structured-data.tsx +104 -0
  120. package/examples/custom-template/supplied-media.tsx +92 -0
  121. package/examples/nextjs-quickstart/.env.example +2 -0
  122. package/examples/nextjs-quickstart/README.md +22 -0
  123. package/examples/nextjs-quickstart/next-env.d.ts +4 -0
  124. package/examples/nextjs-quickstart/package.json +24 -0
  125. package/examples/nextjs-quickstart/src/app/api/video/route.ts +22 -0
  126. package/examples/nextjs-quickstart/src/app/layout.tsx +5 -0
  127. package/examples/nextjs-quickstart/src/app/page.tsx +31 -0
  128. package/examples/nextjs-quickstart/tsconfig.json +26 -0
  129. package/package.json +167 -0
  130. package/registry/items/backgrounds.json +52 -0
  131. package/registry/items/barChart.json +237 -0
  132. package/registry/items/beforeAfter.json +180 -0
  133. package/registry/items/bigNumber.json +225 -0
  134. package/registry/items/brandMessage.json +158 -0
  135. package/registry/items/cardList.json +206 -0
  136. package/registry/items/chatMessenger.json +151 -0
  137. package/registry/items/chatWhatsapp.json +151 -0
  138. package/registry/items/codeEditor.json +184 -0
  139. package/registry/items/confetti.json +107 -0
  140. package/registry/items/ctaLogo.json +160 -0
  141. package/registry/items/ctaMedia.json +193 -0
  142. package/registry/items/emojiBurst.json +101 -0
  143. package/registry/items/incomingCall.json +170 -0
  144. package/registry/items/media.json +179 -0
  145. package/registry/items/milestone.json +205 -0
  146. package/registry/items/motion.json +45 -0
  147. package/registry/items/notification.json +182 -0
  148. package/registry/items/phoneMockup.json +254 -0
  149. package/registry/items/problemSolution.json +186 -0
  150. package/registry/items/progressRing.json +199 -0
  151. package/registry/items/promptInput.json +148 -0
  152. package/registry/items/reaction.json +170 -0
  153. package/registry/items/reviewStack.json +224 -0
  154. package/registry/items/steps.json +207 -0
  155. package/registry/items/template-context.json +31 -0
  156. package/registry/items/terminal.json +212 -0
  157. package/registry/items/testimonial.json +164 -0
  158. package/registry/items/theme.json +47 -0
  159. package/registry/items/tripleStats.json +228 -0
  160. package/registry/items/tweet.json +203 -0
  161. package/registry/items/typography.json +47 -0
  162. package/registry/items/webMockup.json +273 -0
@@ -0,0 +1,3551 @@
1
+ // src/visual-system/catalog/catalog.generated.ts
2
+ var GENERATED_BUILTIN_TEMPLATE_CATALOG = [
3
+ {
4
+ "id": "media",
5
+ "label": "Full-bleed media",
6
+ "description": "Full-bleed photo, video, or brand gradient with a centered headline.",
7
+ "family": "Media & motion",
8
+ "jobs": [
9
+ "setup",
10
+ "atmosphere"
11
+ ],
12
+ "register": "motion-led",
13
+ "useWhen": "A visual hook, scenic beat, or concrete product-context line benefits from immersive media.",
14
+ "avoidWhen": "The scene needs structured facts, exact comparisons, a device frame, or attributed proof.",
15
+ "usesGlobalTextEffect": true,
16
+ "usesGlobalTransition": true,
17
+ "transitionTiming": {
18
+ "entryReadyProgress": 0.2,
19
+ "holdProgress": 0.7
20
+ },
21
+ "usesGlobalBackgroundEffect": true,
22
+ "textCanvas": "open",
23
+ "schema": {
24
+ "type": "object",
25
+ "properties": {
26
+ "texts": {
27
+ "type": "string",
28
+ "title": "Text",
29
+ "description": "Title text shown on the scene",
30
+ "default": "Make an impact."
31
+ },
32
+ "mediaUrl": {
33
+ "type": "string",
34
+ "title": "Background media",
35
+ "description": "Optional photo or video URL behind this scene. When set, replaces the brand gradient.",
36
+ "format": "uri",
37
+ "default": ""
38
+ },
39
+ "mediaKeyword": {
40
+ "type": "string",
41
+ "title": "Background search keyword",
42
+ "description": "2-4 word English term for Pexels stock-footage search (auto-fills mediaUrl).",
43
+ "format": "stock-media-keyword",
44
+ "default": ""
45
+ },
46
+ "mediaType": {
47
+ "type": "string",
48
+ "title": "Background media type",
49
+ "description": "auto detects photo/video from URL. 'gradient' is a deliberate mode \u2014 atmospheric brand-color scene with no stock footage.",
50
+ "enum": [
51
+ "auto",
52
+ "photo",
53
+ "video",
54
+ "gradient"
55
+ ],
56
+ "default": "auto"
57
+ },
58
+ "mediaPoster": {
59
+ "type": "string",
60
+ "title": "Background poster image",
61
+ "description": "Still image URL shown while a video backdrop is decoding its first frame. Auto-filled from Pexels' thumbnail when fillPexelsUrls sets a video mediaUrl. Hides the gradient flash that would otherwise appear in the ~50\u2013400ms gap between a <video> mounting and decoding its first frame.",
62
+ "format": "uri",
63
+ "default": ""
64
+ },
65
+ "mediaPosition": {
66
+ "type": "string",
67
+ "title": "Background focal position",
68
+ "description": "Controls which part of a photo or video stays visible when cover-cropped. Pick the subject's side or vertical anchor after inspecting the frame.",
69
+ "enum": [
70
+ "center",
71
+ "top",
72
+ "bottom",
73
+ "left",
74
+ "right"
75
+ ],
76
+ "default": "center"
77
+ },
78
+ "mediaTreatment": {
79
+ "type": "string",
80
+ "title": "Background contrast treatment",
81
+ "description": "subtle preserves a visual hero; cinematic adds balanced contrast; text-safe adds a stronger wash for copy-heavy scenes.",
82
+ "enum": [
83
+ "subtle",
84
+ "cinematic",
85
+ "text-safe"
86
+ ],
87
+ "default": "cinematic"
88
+ },
89
+ "confetti": {
90
+ "type": "boolean",
91
+ "title": "Confetti",
92
+ "description": "Layer falling confetti particles over the backdrop. Set true when the copy is celebratory (launch, milestone, win, anniversary, achievement). Works on top of any mediaType \u2014 photo, video, or gradient.",
93
+ "default": false
94
+ }
95
+ },
96
+ "required": [
97
+ "texts"
98
+ ],
99
+ "additionalProperties": false,
100
+ "x-vanillasky": {
101
+ "allowsStockMedia": true
102
+ }
103
+ },
104
+ "minDuration": 1,
105
+ "preferredDuration": 2,
106
+ "timing": {
107
+ "contentFields": [
108
+ "texts"
109
+ ],
110
+ "contentUnit": "words"
111
+ }
112
+ },
113
+ {
114
+ "id": "reaction",
115
+ "label": "Reaction clip",
116
+ "description": "Full-bleed reaction video with a short meme-like punchline.",
117
+ "family": "Media & motion",
118
+ "jobs": [
119
+ "payoff",
120
+ "punctuation"
121
+ ],
122
+ "register": "motion-led",
123
+ "useWhen": "One funny, self-aware, celebratory, or dramatic beat deserves a reaction payoff.",
124
+ "avoidWhen": "The tone is calm or formal, no supplied clip is available, or the video already uses a reaction.",
125
+ "usesGlobalTextEffect": true,
126
+ "usesGlobalTransition": true,
127
+ "transitionTiming": {
128
+ "entryReadyProgress": 0.2,
129
+ "holdProgress": 0.7
130
+ },
131
+ "usesGlobalBackgroundEffect": false,
132
+ "textCanvas": "open",
133
+ "schema": {
134
+ "type": "object",
135
+ "properties": {
136
+ "texts": {
137
+ "type": "string",
138
+ "title": "Punchline",
139
+ "description": "Short meme-like punchline shown over the reaction clip. Keep it to 2-6 words.",
140
+ "default": "Still editing manually?"
141
+ },
142
+ "reactionTag": {
143
+ "type": "string",
144
+ "title": "Reaction tag",
145
+ "description": "Pexels meme search intent. Pick from the punchline/problem copy first, then the overall video topic.",
146
+ "enum": [
147
+ "launch",
148
+ "productLaunch",
149
+ "goLive",
150
+ "shipIt",
151
+ "wow",
152
+ "excited",
153
+ "happy",
154
+ "success",
155
+ "done",
156
+ "panic",
157
+ "waiting",
158
+ "celebration",
159
+ "cheers",
160
+ "applause",
161
+ "highFive",
162
+ "teamwork",
163
+ "fail",
164
+ "confused",
165
+ "thinking",
166
+ "thanks",
167
+ "letsGo",
168
+ "manual",
169
+ "office",
170
+ "meeting",
171
+ "deadline",
172
+ "coding",
173
+ "debugging",
174
+ "computer",
175
+ "startup",
176
+ "presentation",
177
+ "sales",
178
+ "growth",
179
+ "money"
180
+ ],
181
+ "default": "manual"
182
+ },
183
+ "mediaUrl": {
184
+ "type": "string",
185
+ "title": "Reaction clip",
186
+ "description": "Resolved Pexels MP4 URL.",
187
+ "format": "uri",
188
+ "default": ""
189
+ },
190
+ "mediaType": {
191
+ "type": "string",
192
+ "title": "Media type",
193
+ "description": "Always 'video' for reaction clips.",
194
+ "default": "video"
195
+ },
196
+ "mediaPoster": {
197
+ "type": "string",
198
+ "title": "Reaction poster image",
199
+ "description": "Still image shown while the reaction clip decodes its first frame.",
200
+ "format": "uri",
201
+ "default": ""
202
+ }
203
+ },
204
+ "required": [
205
+ "texts",
206
+ "reactionTag"
207
+ ],
208
+ "additionalProperties": false,
209
+ "x-vanillasky": {
210
+ "requiredAnyOf": [
211
+ [
212
+ "mediaUrl"
213
+ ]
214
+ ]
215
+ }
216
+ },
217
+ "minDuration": 2.5,
218
+ "preferredDuration": 3,
219
+ "timing": {
220
+ "contentFields": [
221
+ "texts"
222
+ ],
223
+ "contentUnit": "words"
224
+ }
225
+ },
226
+ {
227
+ "id": "confetti",
228
+ "label": "Confetti",
229
+ "description": "Falling confetti over a brand gradient with a centered headline.",
230
+ "family": "Media & motion",
231
+ "jobs": [
232
+ "payoff",
233
+ "punctuation"
234
+ ],
235
+ "register": "motion-led",
236
+ "useWhen": "A launch, win, anniversary, or achievement needs a broad celebratory beat.",
237
+ "avoidWhen": "The moment is not celebratory or a specific numeric achievement should be the visual focus.",
238
+ "usesGlobalTextEffect": true,
239
+ "usesGlobalTransition": true,
240
+ "transitionTiming": {
241
+ "entryReadyProgress": 0.2,
242
+ "holdProgress": 0.7
243
+ },
244
+ "usesGlobalBackgroundEffect": false,
245
+ "textCanvas": "open",
246
+ "schema": {
247
+ "type": "object",
248
+ "properties": {
249
+ "texts": {
250
+ "type": "string",
251
+ "title": "Text",
252
+ "description": "Title text shown on the scene",
253
+ "default": "Celebrate."
254
+ }
255
+ },
256
+ "required": [
257
+ "texts"
258
+ ],
259
+ "additionalProperties": false
260
+ },
261
+ "minDuration": 1,
262
+ "preferredDuration": 2,
263
+ "timing": {
264
+ "contentFields": [
265
+ "texts"
266
+ ],
267
+ "contentUnit": "words"
268
+ }
269
+ },
270
+ {
271
+ "id": "emojiBurst",
272
+ "label": "Emoji burst",
273
+ "description": "A fixed mix of hearts, stars, rockets, sparkles, and celebration emoji bursts radially behind a headline.",
274
+ "family": "Media & motion",
275
+ "jobs": [
276
+ "payoff",
277
+ "punctuation"
278
+ ],
279
+ "register": "motion-led",
280
+ "useWhen": "A playful launch, delight, or achievement benefits from an energetic mixed-emoji celebration.",
281
+ "avoidWhen": "A custom emoji theme is required, the tone is restrained, or falling confetti is the clearer motif.",
282
+ "usesGlobalTextEffect": true,
283
+ "usesGlobalTransition": true,
284
+ "transitionTiming": {
285
+ "entryReadyProgress": 0.2,
286
+ "holdProgress": 0.7
287
+ },
288
+ "usesGlobalBackgroundEffect": false,
289
+ "textCanvas": "open",
290
+ "schema": {
291
+ "type": "object",
292
+ "properties": {
293
+ "texts": {
294
+ "type": "string",
295
+ "title": "Text",
296
+ "description": "Title text shown on the scene",
297
+ "default": "Let's go!"
298
+ }
299
+ },
300
+ "required": [
301
+ "texts"
302
+ ],
303
+ "additionalProperties": false
304
+ },
305
+ "minDuration": 1,
306
+ "preferredDuration": 2,
307
+ "timing": {
308
+ "contentFields": [
309
+ "texts"
310
+ ],
311
+ "contentUnit": "words"
312
+ }
313
+ },
314
+ {
315
+ "id": "bigNumber",
316
+ "label": "Big number",
317
+ "description": "A single animated count-up metric with headline and label.",
318
+ "family": "Data & metrics",
319
+ "jobs": [
320
+ "claim",
321
+ "proof"
322
+ ],
323
+ "register": "typography-led",
324
+ "useWhen": "One exact grounded number is the hero: growth, speed, rank, customer count, or reduction.",
325
+ "avoidWhen": "No exact number is supplied, three peer values matter equally, or the value means completion progress.",
326
+ "usesGlobalTextEffect": false,
327
+ "usesGlobalTransition": true,
328
+ "transitionTiming": {
329
+ "entryReadyProgress": 0.2,
330
+ "holdProgress": 0.7
331
+ },
332
+ "usesGlobalBackgroundEffect": true,
333
+ "textCanvas": "tight",
334
+ "schema": {
335
+ "type": "object",
336
+ "properties": {
337
+ "texts": {
338
+ "type": "string",
339
+ "title": "Text",
340
+ "description": "Concise title shown above the metric (48 characters maximum).",
341
+ "minLength": 1,
342
+ "maxLength": 48,
343
+ "default": "Our biggest milestone yet."
344
+ },
345
+ "value": {
346
+ "type": "number",
347
+ "title": "Target number",
348
+ "description": "The number to count up to",
349
+ "default": 1e3
350
+ },
351
+ "decimalPlaces": {
352
+ "type": "number",
353
+ "title": "Decimal places",
354
+ "description": "Optional display precision; when omitted, precision is inferred from the value"
355
+ },
356
+ "confetti": {
357
+ "type": "boolean",
358
+ "title": "Celebrate with confetti",
359
+ "description": "Add a confetti burst when the metric is explicitly celebratory",
360
+ "default": false
361
+ },
362
+ "label": {
363
+ "type": "string",
364
+ "title": "Label",
365
+ "description": "Short label displayed below the number (32 characters maximum).",
366
+ "minLength": 1,
367
+ "maxLength": 32,
368
+ "default": "Total users"
369
+ },
370
+ "prefix": {
371
+ "type": "string",
372
+ "title": "Prefix",
373
+ "description": "Compact prefix before the number (e.g. $, \u20AC; 2 characters maximum).",
374
+ "maxLength": 2,
375
+ "default": ""
376
+ },
377
+ "unit": {
378
+ "type": "string",
379
+ "title": "Unit suffix",
380
+ "description": "Compact suffix after the number (e.g. %, +, k, M; 4 characters maximum).",
381
+ "maxLength": 4,
382
+ "default": ""
383
+ },
384
+ "mediaUrl": {
385
+ "type": "string",
386
+ "title": "Background media",
387
+ "description": "Optional photo or video URL behind this scene. When set, replaces the brand gradient.",
388
+ "format": "uri",
389
+ "default": ""
390
+ },
391
+ "mediaKeyword": {
392
+ "type": "string",
393
+ "title": "Background search keyword",
394
+ "description": "2-4 word English term for Pexels stock-footage search (auto-fills mediaUrl).",
395
+ "format": "stock-media-keyword",
396
+ "default": ""
397
+ },
398
+ "mediaType": {
399
+ "type": "string",
400
+ "title": "Background media type",
401
+ "description": "auto detects photo/video from URL. 'gradient' is a deliberate mode \u2014 atmospheric brand-color scene with no stock footage.",
402
+ "enum": [
403
+ "auto",
404
+ "photo",
405
+ "video",
406
+ "gradient"
407
+ ],
408
+ "default": "auto"
409
+ },
410
+ "mediaPoster": {
411
+ "type": "string",
412
+ "title": "Background poster image",
413
+ "description": "Still image URL shown while a video backdrop is decoding its first frame. Auto-filled from Pexels' thumbnail when fillPexelsUrls sets a video mediaUrl. Hides the gradient flash that would otherwise appear in the ~50\u2013400ms gap between a <video> mounting and decoding its first frame.",
414
+ "format": "uri",
415
+ "default": ""
416
+ },
417
+ "mediaPosition": {
418
+ "type": "string",
419
+ "title": "Background focal position",
420
+ "description": "Controls which part of a photo or video stays visible when cover-cropped. Pick the subject's side or vertical anchor after inspecting the frame.",
421
+ "enum": [
422
+ "center",
423
+ "top",
424
+ "bottom",
425
+ "left",
426
+ "right"
427
+ ],
428
+ "default": "center"
429
+ },
430
+ "mediaTreatment": {
431
+ "type": "string",
432
+ "title": "Background contrast treatment",
433
+ "description": "subtle preserves a visual hero; cinematic adds balanced contrast; text-safe adds a stronger wash for copy-heavy scenes.",
434
+ "enum": [
435
+ "subtle",
436
+ "cinematic",
437
+ "text-safe"
438
+ ],
439
+ "default": "cinematic"
440
+ }
441
+ },
442
+ "required": [
443
+ "texts",
444
+ "value",
445
+ "label"
446
+ ],
447
+ "additionalProperties": false,
448
+ "x-vanillasky": {
449
+ "requiresStat": true,
450
+ "allowsStockMedia": true
451
+ }
452
+ },
453
+ "minDuration": 1.5,
454
+ "preferredDuration": 4,
455
+ "timing": {
456
+ "contentFields": [
457
+ "texts",
458
+ "label"
459
+ ],
460
+ "contentUnit": "words"
461
+ }
462
+ },
463
+ {
464
+ "id": "barChart",
465
+ "label": "Bar chart",
466
+ "description": "Animated labeled bars compare grounded values beneath a headline.",
467
+ "family": "Data & metrics",
468
+ "jobs": [
469
+ "proof"
470
+ ],
471
+ "register": "motion-led",
472
+ "useWhen": "Two or more grounded labeled values need a readable comparison, ranking, or relative-size view.",
473
+ "avoidWhen": "Labels or exact values are missing, one metric is the hero, or the data represents progress toward a whole.",
474
+ "usesGlobalTextEffect": false,
475
+ "usesGlobalTransition": true,
476
+ "transitionTiming": {
477
+ "entryReadyProgress": 0.2,
478
+ "holdProgress": 0.7
479
+ },
480
+ "usesGlobalBackgroundEffect": true,
481
+ "textCanvas": "tight",
482
+ "schema": {
483
+ "type": "object",
484
+ "properties": {
485
+ "texts": {
486
+ "type": "string",
487
+ "title": "Text",
488
+ "description": "Title text shown above the chart",
489
+ "default": "Revenue up 300%."
490
+ },
491
+ "bars": {
492
+ "type": "array",
493
+ "title": "Comparison values",
494
+ "description": "Array of 2-6 grounded values with short category labels. Values are shown exactly and bar heights are scaled relative to the largest value. Use only facts supported by the input.",
495
+ "examples": [
496
+ [
497
+ {
498
+ "label": "Free",
499
+ "value": 24
500
+ },
501
+ {
502
+ "label": "Pro",
503
+ "value": 61
504
+ },
505
+ {
506
+ "label": "Business",
507
+ "value": 88
508
+ }
509
+ ]
510
+ ],
511
+ "items": {
512
+ "type": "object",
513
+ "properties": {
514
+ "label": {
515
+ "type": "string",
516
+ "description": "Short category label shown below the bar (18 characters or fewer).",
517
+ "maxLength": 18
518
+ },
519
+ "value": {
520
+ "type": "number",
521
+ "description": "Exact grounded value shown above the bar.",
522
+ "format": "grounded-stat",
523
+ "minimum": 0
524
+ }
525
+ },
526
+ "required": [
527
+ "label",
528
+ "value"
529
+ ],
530
+ "additionalProperties": false
531
+ },
532
+ "minItems": 2,
533
+ "maxItems": 6,
534
+ "default": [
535
+ {
536
+ "label": "Q1",
537
+ "value": 42
538
+ },
539
+ {
540
+ "label": "Q2",
541
+ "value": 58
542
+ },
543
+ {
544
+ "label": "Q3",
545
+ "value": 76
546
+ },
547
+ {
548
+ "label": "Q4",
549
+ "value": 91
550
+ }
551
+ ]
552
+ },
553
+ "mediaUrl": {
554
+ "type": "string",
555
+ "title": "Background media",
556
+ "description": "Optional photo or video URL behind this scene. When set, replaces the brand gradient.",
557
+ "format": "uri",
558
+ "default": ""
559
+ },
560
+ "mediaKeyword": {
561
+ "type": "string",
562
+ "title": "Background search keyword",
563
+ "description": "2-4 word English term for Pexels stock-footage search (auto-fills mediaUrl).",
564
+ "format": "stock-media-keyword",
565
+ "default": ""
566
+ },
567
+ "mediaType": {
568
+ "type": "string",
569
+ "title": "Background media type",
570
+ "description": "auto detects photo/video from URL. 'gradient' is a deliberate mode \u2014 atmospheric brand-color scene with no stock footage.",
571
+ "enum": [
572
+ "auto",
573
+ "photo",
574
+ "video",
575
+ "gradient"
576
+ ],
577
+ "default": "auto"
578
+ },
579
+ "mediaPoster": {
580
+ "type": "string",
581
+ "title": "Background poster image",
582
+ "description": "Still image URL shown while a video backdrop is decoding its first frame. Auto-filled from Pexels' thumbnail when fillPexelsUrls sets a video mediaUrl. Hides the gradient flash that would otherwise appear in the ~50\u2013400ms gap between a <video> mounting and decoding its first frame.",
583
+ "format": "uri",
584
+ "default": ""
585
+ },
586
+ "mediaPosition": {
587
+ "type": "string",
588
+ "title": "Background focal position",
589
+ "description": "Controls which part of a photo or video stays visible when cover-cropped. Pick the subject's side or vertical anchor after inspecting the frame.",
590
+ "enum": [
591
+ "center",
592
+ "top",
593
+ "bottom",
594
+ "left",
595
+ "right"
596
+ ],
597
+ "default": "center"
598
+ },
599
+ "mediaTreatment": {
600
+ "type": "string",
601
+ "title": "Background contrast treatment",
602
+ "description": "subtle preserves a visual hero; cinematic adds balanced contrast; text-safe adds a stronger wash for copy-heavy scenes.",
603
+ "enum": [
604
+ "subtle",
605
+ "cinematic",
606
+ "text-safe"
607
+ ],
608
+ "default": "cinematic"
609
+ }
610
+ },
611
+ "required": [
612
+ "texts",
613
+ "bars"
614
+ ],
615
+ "additionalProperties": false,
616
+ "x-vanillasky": {
617
+ "requiresStat": true,
618
+ "allowsStockMedia": true
619
+ }
620
+ },
621
+ "minDuration": 2,
622
+ "preferredDuration": 4,
623
+ "timing": {
624
+ "contentFields": [
625
+ "bars"
626
+ ],
627
+ "contentUnit": "items"
628
+ }
629
+ },
630
+ {
631
+ "id": "progressRing",
632
+ "label": "Progress ring",
633
+ "description": "An animated circular progress value with headline and label.",
634
+ "family": "Data & metrics",
635
+ "jobs": [
636
+ "proof"
637
+ ],
638
+ "register": "motion-led",
639
+ "useWhen": "An exact percentage represents completion, readiness, health, adoption, or goal progress.",
640
+ "avoidWhen": "The percentage is a generic uplift or reduction rather than progress toward a bounded whole.",
641
+ "usesGlobalTextEffect": false,
642
+ "usesGlobalTransition": true,
643
+ "transitionTiming": {
644
+ "entryReadyProgress": 0.2,
645
+ "holdProgress": 0.7
646
+ },
647
+ "usesGlobalBackgroundEffect": true,
648
+ "textCanvas": "tight",
649
+ "schema": {
650
+ "type": "object",
651
+ "properties": {
652
+ "texts": {
653
+ "type": "string",
654
+ "title": "Text",
655
+ "description": "Concise title shown above the ring (48 characters maximum).",
656
+ "minLength": 1,
657
+ "maxLength": 48,
658
+ "default": "Almost there."
659
+ },
660
+ "value": {
661
+ "type": "number",
662
+ "title": "Value",
663
+ "description": "Percentage value 0-100",
664
+ "default": 75
665
+ },
666
+ "decimalPlaces": {
667
+ "type": "number",
668
+ "title": "Decimal places",
669
+ "description": "Optional display precision; when omitted, precision is inferred from the value"
670
+ },
671
+ "label": {
672
+ "type": "string",
673
+ "title": "Label",
674
+ "description": "Short label shown below the ring (32 characters maximum).",
675
+ "minLength": 1,
676
+ "maxLength": 32,
677
+ "default": "Complete"
678
+ },
679
+ "unit": {
680
+ "type": "string",
681
+ "title": "Unit",
682
+ "description": "Compact suffix after the number (e.g. %, x, k; 3 characters maximum).",
683
+ "maxLength": 3,
684
+ "default": "%"
685
+ },
686
+ "mediaUrl": {
687
+ "type": "string",
688
+ "title": "Background media",
689
+ "description": "Optional photo or video URL behind this scene. When set, replaces the brand gradient.",
690
+ "format": "uri",
691
+ "default": ""
692
+ },
693
+ "mediaKeyword": {
694
+ "type": "string",
695
+ "title": "Background search keyword",
696
+ "description": "2-4 word English term for Pexels stock-footage search (auto-fills mediaUrl).",
697
+ "format": "stock-media-keyword",
698
+ "default": ""
699
+ },
700
+ "mediaType": {
701
+ "type": "string",
702
+ "title": "Background media type",
703
+ "description": "auto detects photo/video from URL. 'gradient' is a deliberate mode \u2014 atmospheric brand-color scene with no stock footage.",
704
+ "enum": [
705
+ "auto",
706
+ "photo",
707
+ "video",
708
+ "gradient"
709
+ ],
710
+ "default": "auto"
711
+ },
712
+ "mediaPoster": {
713
+ "type": "string",
714
+ "title": "Background poster image",
715
+ "description": "Still image URL shown while a video backdrop is decoding its first frame. Auto-filled from Pexels' thumbnail when fillPexelsUrls sets a video mediaUrl. Hides the gradient flash that would otherwise appear in the ~50\u2013400ms gap between a <video> mounting and decoding its first frame.",
716
+ "format": "uri",
717
+ "default": ""
718
+ },
719
+ "mediaPosition": {
720
+ "type": "string",
721
+ "title": "Background focal position",
722
+ "description": "Controls which part of a photo or video stays visible when cover-cropped. Pick the subject's side or vertical anchor after inspecting the frame.",
723
+ "enum": [
724
+ "center",
725
+ "top",
726
+ "bottom",
727
+ "left",
728
+ "right"
729
+ ],
730
+ "default": "center"
731
+ },
732
+ "mediaTreatment": {
733
+ "type": "string",
734
+ "title": "Background contrast treatment",
735
+ "description": "subtle preserves a visual hero; cinematic adds balanced contrast; text-safe adds a stronger wash for copy-heavy scenes.",
736
+ "enum": [
737
+ "subtle",
738
+ "cinematic",
739
+ "text-safe"
740
+ ],
741
+ "default": "cinematic"
742
+ }
743
+ },
744
+ "required": [
745
+ "texts",
746
+ "value",
747
+ "label"
748
+ ],
749
+ "additionalProperties": false,
750
+ "x-vanillasky": {
751
+ "requiresStat": true,
752
+ "allowsStockMedia": true
753
+ }
754
+ },
755
+ "minDuration": 2,
756
+ "preferredDuration": 3.5,
757
+ "timing": {
758
+ "contentFields": [
759
+ "texts",
760
+ "label"
761
+ ],
762
+ "contentUnit": "words"
763
+ }
764
+ },
765
+ {
766
+ "id": "phoneMockup",
767
+ "label": "Phone mockup",
768
+ "description": "One to three portrait screenshots presented inside an animated phone frame.",
769
+ "family": "Product showcase",
770
+ "jobs": [
771
+ "proof"
772
+ ],
773
+ "register": "device-led",
774
+ "useWhen": "Supplied portrait screenshots demonstrate a mobile interface or app workflow.",
775
+ "avoidWhen": "No real screenshot is supplied or the interface is primarily landscape or desktop-shaped.",
776
+ "usesGlobalTextEffect": true,
777
+ "usesGlobalTransition": true,
778
+ "transitionTiming": {
779
+ "entryReadyProgress": 0.2,
780
+ "holdProgress": 0.7
781
+ },
782
+ "usesGlobalBackgroundEffect": false,
783
+ "textCanvas": "tight",
784
+ "schema": {
785
+ "type": "object",
786
+ "properties": {
787
+ "texts": {
788
+ "type": "string",
789
+ "title": "Text",
790
+ "description": "Title text shown on the scene",
791
+ "default": "See it in action."
792
+ },
793
+ "screenMediaUrl": {
794
+ "type": "string",
795
+ "title": "Screen screenshot",
796
+ "description": "Screenshot to show on the phone screen",
797
+ "format": "supplied-image",
798
+ "default": ""
799
+ },
800
+ "screen1Url": {
801
+ "type": "string",
802
+ "title": "Screen 1",
803
+ "description": "Set to enable slides mode \u2014 screens slide inside the phone",
804
+ "format": "supplied-image",
805
+ "default": ""
806
+ },
807
+ "screen2Url": {
808
+ "type": "string",
809
+ "title": "Screen 2",
810
+ "description": "Second screen (slides mode)",
811
+ "format": "supplied-image",
812
+ "default": ""
813
+ },
814
+ "screenFit": {
815
+ "type": "string",
816
+ "title": "Screenshot fit",
817
+ "description": "Use cover for immersive product detail; contain when the full interface must remain visible.",
818
+ "enum": [
819
+ "cover",
820
+ "contain"
821
+ ],
822
+ "default": "cover"
823
+ },
824
+ "screenFocusX": {
825
+ "type": "number",
826
+ "title": "Horizontal focus (0-100)",
827
+ "description": "Horizontal percentage of the screenshot to keep in focus. Values are clamped to 0-100.",
828
+ "default": 50
829
+ },
830
+ "screenFocusY": {
831
+ "type": "number",
832
+ "title": "Vertical focus (0-100)",
833
+ "description": "Vertical percentage of the screenshot to keep in focus. Values are clamped to 0-100.",
834
+ "default": 50
835
+ },
836
+ "screenMotion": {
837
+ "type": "string",
838
+ "title": "Screenshot motion",
839
+ "description": "Single-screen treatment: pushIn is the professional default; use pan for wide interfaces and still when motion would distract. Multi-screen slides stay still so motions do not compete.",
840
+ "enum": [
841
+ "still",
842
+ "pushIn",
843
+ "pan"
844
+ ],
845
+ "default": "pushIn"
846
+ },
847
+ "screenCalloutText": {
848
+ "type": "string",
849
+ "title": "Feature callout",
850
+ "description": "Optional 2-4 word annotation anchored to a product detail. Leave empty rather than narrating the headline twice.",
851
+ "default": ""
852
+ },
853
+ "screenCalloutX": {
854
+ "type": "number",
855
+ "title": "Callout horizontal position (0-100)",
856
+ "description": "Horizontal percentage of the product surface for the callout anchor.",
857
+ "default": 70
858
+ },
859
+ "screenCalloutY": {
860
+ "type": "number",
861
+ "title": "Callout vertical position (0-100)",
862
+ "description": "Vertical percentage of the product surface for the callout anchor.",
863
+ "default": 35
864
+ },
865
+ "mediaUrl": {
866
+ "type": "string",
867
+ "title": "Background media",
868
+ "description": "Optional photo or video URL behind this scene. When set, replaces the brand gradient.",
869
+ "format": "uri",
870
+ "default": ""
871
+ },
872
+ "mediaKeyword": {
873
+ "type": "string",
874
+ "title": "Background search keyword",
875
+ "description": "2-4 word English term for Pexels stock-footage search (auto-fills mediaUrl).",
876
+ "default": ""
877
+ },
878
+ "mediaType": {
879
+ "type": "string",
880
+ "title": "Background media type",
881
+ "description": "auto detects photo/video from URL. 'gradient' is a deliberate mode \u2014 atmospheric brand-color scene with no stock footage.",
882
+ "enum": [
883
+ "auto",
884
+ "photo",
885
+ "video",
886
+ "gradient"
887
+ ],
888
+ "default": "auto"
889
+ },
890
+ "mediaPoster": {
891
+ "type": "string",
892
+ "title": "Background poster image",
893
+ "description": "Still image URL shown while a video backdrop is decoding its first frame. Auto-filled from Pexels' thumbnail when fillPexelsUrls sets a video mediaUrl. Hides the gradient flash that would otherwise appear in the ~50\u2013400ms gap between a <video> mounting and decoding its first frame.",
894
+ "format": "uri",
895
+ "default": ""
896
+ },
897
+ "mediaPosition": {
898
+ "type": "string",
899
+ "title": "Background focal position",
900
+ "description": "Controls which part of a photo or video stays visible when cover-cropped. Pick the subject's side or vertical anchor after inspecting the frame.",
901
+ "enum": [
902
+ "center",
903
+ "top",
904
+ "bottom",
905
+ "left",
906
+ "right"
907
+ ],
908
+ "default": "center"
909
+ },
910
+ "mediaTreatment": {
911
+ "type": "string",
912
+ "title": "Background contrast treatment",
913
+ "description": "subtle preserves a visual hero; cinematic adds balanced contrast; text-safe adds a stronger wash for copy-heavy scenes.",
914
+ "enum": [
915
+ "subtle",
916
+ "cinematic",
917
+ "text-safe"
918
+ ],
919
+ "default": "cinematic"
920
+ }
921
+ },
922
+ "required": [
923
+ "texts"
924
+ ],
925
+ "additionalProperties": false
926
+ },
927
+ "minDuration": 2,
928
+ "preferredDuration": 3,
929
+ "timing": {
930
+ "contentFields": [
931
+ "texts",
932
+ "screenCalloutText"
933
+ ],
934
+ "contentUnit": "words"
935
+ }
936
+ },
937
+ {
938
+ "id": "webMockup",
939
+ "label": "Web mockup",
940
+ "description": "One to three desktop screenshots presented in an animated browser or tablet frame.",
941
+ "family": "Product showcase",
942
+ "jobs": [
943
+ "proof"
944
+ ],
945
+ "register": "device-led",
946
+ "useWhen": "Supplied landscape or square screenshots demonstrate a website, dashboard, or desktop product.",
947
+ "avoidWhen": "No real screenshot is supplied or the interface is primarily portrait and mobile-shaped.",
948
+ "usesGlobalTextEffect": true,
949
+ "usesGlobalTransition": true,
950
+ "transitionTiming": {
951
+ "entryReadyProgress": 0.2,
952
+ "holdProgress": 0.7
953
+ },
954
+ "usesGlobalBackgroundEffect": false,
955
+ "textCanvas": "tight",
956
+ "schema": {
957
+ "type": "object",
958
+ "properties": {
959
+ "frame": {
960
+ "type": "string",
961
+ "title": "Device frame",
962
+ "description": "browser shows chrome and an address bar; tablet uses a clean product frame.",
963
+ "enum": [
964
+ "browser",
965
+ "tablet"
966
+ ],
967
+ "default": "browser"
968
+ },
969
+ "texts": {
970
+ "type": "string",
971
+ "title": "Text",
972
+ "description": "Title text shown above the product.",
973
+ "default": "See it in action."
974
+ },
975
+ "screenMediaUrl": {
976
+ "type": "string",
977
+ "title": "Screenshot",
978
+ "description": "Product screenshot shown inside the browser or tablet frame.",
979
+ "format": "supplied-image",
980
+ "default": ""
981
+ },
982
+ "screen1Url": {
983
+ "type": "string",
984
+ "title": "Screen 1",
985
+ "description": "Optional second product screen. Setting it enables slides mode.",
986
+ "format": "supplied-image",
987
+ "default": ""
988
+ },
989
+ "screen2Url": {
990
+ "type": "string",
991
+ "title": "Screen 2",
992
+ "description": "Optional third product screen for slides mode.",
993
+ "format": "supplied-image",
994
+ "default": ""
995
+ },
996
+ "screenFit": {
997
+ "type": "string",
998
+ "title": "Screenshot fit",
999
+ "description": "Use cover for immersive product detail; contain when the full interface must remain visible.",
1000
+ "enum": [
1001
+ "cover",
1002
+ "contain"
1003
+ ],
1004
+ "default": "cover"
1005
+ },
1006
+ "screenFocusX": {
1007
+ "type": "number",
1008
+ "title": "Horizontal focus (0-100)",
1009
+ "description": "Horizontal percentage of the screenshot to keep in focus. Values are clamped to 0-100.",
1010
+ "default": 50
1011
+ },
1012
+ "screenFocusY": {
1013
+ "type": "number",
1014
+ "title": "Vertical focus (0-100)",
1015
+ "description": "Vertical percentage of the screenshot to keep in focus. Values are clamped to 0-100.",
1016
+ "default": 50
1017
+ },
1018
+ "screenMotion": {
1019
+ "type": "string",
1020
+ "title": "Screenshot motion",
1021
+ "description": "Single-screen treatment: pushIn is the professional default; use pan for wide interfaces and still when motion would distract. Multi-screen slides stay still so motions do not compete.",
1022
+ "enum": [
1023
+ "still",
1024
+ "pushIn",
1025
+ "pan"
1026
+ ],
1027
+ "default": "pushIn"
1028
+ },
1029
+ "screenCalloutText": {
1030
+ "type": "string",
1031
+ "title": "Feature callout",
1032
+ "description": "Optional 2-4 word annotation anchored to a product detail. Leave empty rather than narrating the headline twice.",
1033
+ "default": ""
1034
+ },
1035
+ "screenCalloutX": {
1036
+ "type": "number",
1037
+ "title": "Callout horizontal position (0-100)",
1038
+ "description": "Horizontal percentage of the product surface for the callout anchor.",
1039
+ "default": 70
1040
+ },
1041
+ "screenCalloutY": {
1042
+ "type": "number",
1043
+ "title": "Callout vertical position (0-100)",
1044
+ "description": "Vertical percentage of the product surface for the callout anchor.",
1045
+ "default": 35
1046
+ },
1047
+ "addressBarUrl": {
1048
+ "type": "string",
1049
+ "title": "Address bar URL",
1050
+ "description": "URL shown in browser chrome; ignored by the tablet frame.",
1051
+ "examples": [
1052
+ "yourapp.com"
1053
+ ],
1054
+ "default": ""
1055
+ },
1056
+ "mediaUrl": {
1057
+ "type": "string",
1058
+ "title": "Background media",
1059
+ "description": "Optional photo or video URL behind this scene. When set, replaces the brand gradient.",
1060
+ "format": "uri",
1061
+ "default": ""
1062
+ },
1063
+ "mediaKeyword": {
1064
+ "type": "string",
1065
+ "title": "Background search keyword",
1066
+ "description": "2-4 word English term for Pexels stock-footage search (auto-fills mediaUrl).",
1067
+ "default": ""
1068
+ },
1069
+ "mediaType": {
1070
+ "type": "string",
1071
+ "title": "Background media type",
1072
+ "description": "auto detects photo/video from URL. 'gradient' is a deliberate mode \u2014 atmospheric brand-color scene with no stock footage.",
1073
+ "enum": [
1074
+ "auto",
1075
+ "photo",
1076
+ "video",
1077
+ "gradient"
1078
+ ],
1079
+ "default": "auto"
1080
+ },
1081
+ "mediaPoster": {
1082
+ "type": "string",
1083
+ "title": "Background poster image",
1084
+ "description": "Still image URL shown while a video backdrop is decoding its first frame. Auto-filled from Pexels' thumbnail when fillPexelsUrls sets a video mediaUrl. Hides the gradient flash that would otherwise appear in the ~50\u2013400ms gap between a <video> mounting and decoding its first frame.",
1085
+ "format": "uri",
1086
+ "default": ""
1087
+ },
1088
+ "mediaPosition": {
1089
+ "type": "string",
1090
+ "title": "Background focal position",
1091
+ "description": "Controls which part of a photo or video stays visible when cover-cropped. Pick the subject's side or vertical anchor after inspecting the frame.",
1092
+ "enum": [
1093
+ "center",
1094
+ "top",
1095
+ "bottom",
1096
+ "left",
1097
+ "right"
1098
+ ],
1099
+ "default": "center"
1100
+ },
1101
+ "mediaTreatment": {
1102
+ "type": "string",
1103
+ "title": "Background contrast treatment",
1104
+ "description": "subtle preserves a visual hero; cinematic adds balanced contrast; text-safe adds a stronger wash for copy-heavy scenes.",
1105
+ "enum": [
1106
+ "subtle",
1107
+ "cinematic",
1108
+ "text-safe"
1109
+ ],
1110
+ "default": "cinematic"
1111
+ }
1112
+ },
1113
+ "required": [
1114
+ "texts"
1115
+ ],
1116
+ "additionalProperties": false
1117
+ },
1118
+ "minDuration": 2,
1119
+ "preferredDuration": 3,
1120
+ "timing": {
1121
+ "contentFields": [
1122
+ "texts",
1123
+ "screenCalloutText"
1124
+ ],
1125
+ "contentUnit": "words"
1126
+ }
1127
+ },
1128
+ {
1129
+ "id": "codeEditor",
1130
+ "label": "Code editor",
1131
+ "description": "A syntax-styled editor window reveals a short code sample beneath a headline.",
1132
+ "family": "Product showcase",
1133
+ "jobs": [
1134
+ "proof"
1135
+ ],
1136
+ "register": "device-led",
1137
+ "useWhen": "Code, configuration, an API, or an SDK mechanism is central to understanding the product.",
1138
+ "avoidWhen": "The source has no grounded technical detail or a shell command communicates the workflow better.",
1139
+ "usesGlobalTextEffect": false,
1140
+ "usesGlobalTransition": true,
1141
+ "transitionTiming": {
1142
+ "entryReadyProgress": 0.2,
1143
+ "holdProgress": 0.7
1144
+ },
1145
+ "usesGlobalBackgroundEffect": true,
1146
+ "textCanvas": "tight",
1147
+ "schema": {
1148
+ "type": "object",
1149
+ "properties": {
1150
+ "texts": {
1151
+ "type": "string",
1152
+ "title": "Text",
1153
+ "description": "Concise title shown above the editor (48 characters maximum).",
1154
+ "minLength": 1,
1155
+ "maxLength": 48,
1156
+ "default": "Simple as this."
1157
+ },
1158
+ "code": {
1159
+ "type": "string",
1160
+ "title": "Code",
1161
+ "description": "Code snippet \u2014 use \\n for line breaks",
1162
+ "default": 'import { getVideoDuration } from "@vanillaskyai/video";\nimport type { Video } from "@vanillaskyai/video";\n\nexport function duration(video: Video) {\n return getVideoDuration(video);\n}'
1163
+ },
1164
+ "filename": {
1165
+ "type": "string",
1166
+ "title": "Filename",
1167
+ "description": "Filename shown in the editor title bar (32 characters maximum).",
1168
+ "maxLength": 32,
1169
+ "default": "app.ts"
1170
+ },
1171
+ "mediaUrl": {
1172
+ "type": "string",
1173
+ "title": "Background media",
1174
+ "description": "Optional photo or video URL behind this scene. When set, replaces the brand gradient.",
1175
+ "format": "uri",
1176
+ "default": ""
1177
+ },
1178
+ "mediaKeyword": {
1179
+ "type": "string",
1180
+ "title": "Background search keyword",
1181
+ "description": "2-4 word English term for Pexels stock-footage search (auto-fills mediaUrl).",
1182
+ "format": "stock-media-keyword",
1183
+ "default": ""
1184
+ },
1185
+ "mediaType": {
1186
+ "type": "string",
1187
+ "title": "Background media type",
1188
+ "description": "auto detects photo/video from URL. 'gradient' is a deliberate mode \u2014 atmospheric brand-color scene with no stock footage.",
1189
+ "enum": [
1190
+ "auto",
1191
+ "photo",
1192
+ "video",
1193
+ "gradient"
1194
+ ],
1195
+ "default": "auto"
1196
+ },
1197
+ "mediaPoster": {
1198
+ "type": "string",
1199
+ "title": "Background poster image",
1200
+ "description": "Still image URL shown while a video backdrop is decoding its first frame. Auto-filled from Pexels' thumbnail when fillPexelsUrls sets a video mediaUrl. Hides the gradient flash that would otherwise appear in the ~50\u2013400ms gap between a <video> mounting and decoding its first frame.",
1201
+ "format": "uri",
1202
+ "default": ""
1203
+ },
1204
+ "mediaPosition": {
1205
+ "type": "string",
1206
+ "title": "Background focal position",
1207
+ "description": "Controls which part of a photo or video stays visible when cover-cropped. Pick the subject's side or vertical anchor after inspecting the frame.",
1208
+ "enum": [
1209
+ "center",
1210
+ "top",
1211
+ "bottom",
1212
+ "left",
1213
+ "right"
1214
+ ],
1215
+ "default": "center"
1216
+ },
1217
+ "mediaTreatment": {
1218
+ "type": "string",
1219
+ "title": "Background contrast treatment",
1220
+ "description": "subtle preserves a visual hero; cinematic adds balanced contrast; text-safe adds a stronger wash for copy-heavy scenes.",
1221
+ "enum": [
1222
+ "subtle",
1223
+ "cinematic",
1224
+ "text-safe"
1225
+ ],
1226
+ "default": "cinematic"
1227
+ }
1228
+ },
1229
+ "required": [
1230
+ "texts",
1231
+ "code"
1232
+ ],
1233
+ "additionalProperties": false,
1234
+ "x-vanillasky": {
1235
+ "allowsStockMedia": true
1236
+ }
1237
+ },
1238
+ "minDuration": 2,
1239
+ "preferredDuration": 3,
1240
+ "timing": {
1241
+ "contentFields": [
1242
+ "texts",
1243
+ "code"
1244
+ ],
1245
+ "contentUnit": "characters"
1246
+ }
1247
+ },
1248
+ {
1249
+ "id": "terminal",
1250
+ "label": "Terminal",
1251
+ "description": "A terminal window types a command and reveals optional output lines.",
1252
+ "family": "Product showcase",
1253
+ "jobs": [
1254
+ "proof"
1255
+ ],
1256
+ "register": "device-led",
1257
+ "useWhen": "A grounded command, installation, deployment, test, or status flow explains the product.",
1258
+ "avoidWhen": "The source has no real command or a multi-line code sample is the more faithful visual.",
1259
+ "usesGlobalTextEffect": false,
1260
+ "usesGlobalTransition": true,
1261
+ "transitionTiming": {
1262
+ "entryReadyProgress": 0.2,
1263
+ "holdProgress": 0.7
1264
+ },
1265
+ "usesGlobalBackgroundEffect": true,
1266
+ "textCanvas": "tight",
1267
+ "schema": {
1268
+ "type": "object",
1269
+ "properties": {
1270
+ "texts": {
1271
+ "type": "string",
1272
+ "title": "Text",
1273
+ "description": "Concise title shown above the terminal (48 characters maximum).",
1274
+ "minLength": 1,
1275
+ "maxLength": 48,
1276
+ "default": "Ship it."
1277
+ },
1278
+ "command": {
1279
+ "type": "string",
1280
+ "title": "Command",
1281
+ "description": "Command typed character by character. Keep the meaningful invocation within 80 characters; the terminal deliberately ellipsizes longer display lines.",
1282
+ "minLength": 1,
1283
+ "maxLength": 80,
1284
+ "default": "npx vanillasky check"
1285
+ },
1286
+ "output": {
1287
+ "type": "array",
1288
+ "title": "Output lines",
1289
+ "description": 'JSON array of output lines that appear after the command. Send as an array \u2014 terminal output frequently contains commas. Example: ["\u2713 Built /api/auth/login.ts, /api/users/me.ts", "\u2713 12 tests passed", "\u2713 Ready to deploy"]',
1290
+ "examples": [
1291
+ [
1292
+ "\u2713 Scenes composed",
1293
+ "\u2713 Beat-synced to music",
1294
+ "\u2713 Ready to export"
1295
+ ]
1296
+ ],
1297
+ "items": {
1298
+ "type": "string",
1299
+ "minLength": 1,
1300
+ "maxLength": 80
1301
+ },
1302
+ "maxItems": 5,
1303
+ "default": []
1304
+ },
1305
+ "promptPrefix": {
1306
+ "type": "string",
1307
+ "title": "Prompt",
1308
+ "description": "Compact prompt prefix (e.g. $, >, \u2192; 3 characters maximum).",
1309
+ "maxLength": 3,
1310
+ "default": "$"
1311
+ },
1312
+ "mediaUrl": {
1313
+ "type": "string",
1314
+ "title": "Background media",
1315
+ "description": "Optional photo or video URL behind this scene. When set, replaces the brand gradient.",
1316
+ "format": "uri",
1317
+ "default": ""
1318
+ },
1319
+ "mediaKeyword": {
1320
+ "type": "string",
1321
+ "title": "Background search keyword",
1322
+ "description": "2-4 word English term for Pexels stock-footage search (auto-fills mediaUrl).",
1323
+ "format": "stock-media-keyword",
1324
+ "default": ""
1325
+ },
1326
+ "mediaType": {
1327
+ "type": "string",
1328
+ "title": "Background media type",
1329
+ "description": "auto detects photo/video from URL. 'gradient' is a deliberate mode \u2014 atmospheric brand-color scene with no stock footage.",
1330
+ "enum": [
1331
+ "auto",
1332
+ "photo",
1333
+ "video",
1334
+ "gradient"
1335
+ ],
1336
+ "default": "auto"
1337
+ },
1338
+ "mediaPoster": {
1339
+ "type": "string",
1340
+ "title": "Background poster image",
1341
+ "description": "Still image URL shown while a video backdrop is decoding its first frame. Auto-filled from Pexels' thumbnail when fillPexelsUrls sets a video mediaUrl. Hides the gradient flash that would otherwise appear in the ~50\u2013400ms gap between a <video> mounting and decoding its first frame.",
1342
+ "format": "uri",
1343
+ "default": ""
1344
+ },
1345
+ "mediaPosition": {
1346
+ "type": "string",
1347
+ "title": "Background focal position",
1348
+ "description": "Controls which part of a photo or video stays visible when cover-cropped. Pick the subject's side or vertical anchor after inspecting the frame.",
1349
+ "enum": [
1350
+ "center",
1351
+ "top",
1352
+ "bottom",
1353
+ "left",
1354
+ "right"
1355
+ ],
1356
+ "default": "center"
1357
+ },
1358
+ "mediaTreatment": {
1359
+ "type": "string",
1360
+ "title": "Background contrast treatment",
1361
+ "description": "subtle preserves a visual hero; cinematic adds balanced contrast; text-safe adds a stronger wash for copy-heavy scenes.",
1362
+ "enum": [
1363
+ "subtle",
1364
+ "cinematic",
1365
+ "text-safe"
1366
+ ],
1367
+ "default": "cinematic"
1368
+ }
1369
+ },
1370
+ "required": [
1371
+ "texts",
1372
+ "command"
1373
+ ],
1374
+ "additionalProperties": false,
1375
+ "x-vanillasky": {
1376
+ "allowsStockMedia": true
1377
+ }
1378
+ },
1379
+ "minDuration": 2,
1380
+ "preferredDuration": 3,
1381
+ "timing": {
1382
+ "contentFields": [
1383
+ "texts",
1384
+ "command",
1385
+ "output"
1386
+ ],
1387
+ "contentUnit": "characters"
1388
+ }
1389
+ },
1390
+ {
1391
+ "id": "tweet",
1392
+ "label": "X post",
1393
+ "description": "An X-style text post card with author, handle, verification, replies, and likes.",
1394
+ "family": "Social & messaging",
1395
+ "jobs": [
1396
+ "claim",
1397
+ "proof"
1398
+ ],
1399
+ "register": "card-led",
1400
+ "useWhen": "A grounded public announcement, founder update, launch quote, or concise social claim should read as a post.",
1401
+ "avoidWhen": "The copy is generic explanatory prose, attribution is missing, or visual attachments are essential.",
1402
+ "usesGlobalTextEffect": false,
1403
+ "usesGlobalTransition": true,
1404
+ "transitionTiming": {
1405
+ "entryReadyProgress": 0.2,
1406
+ "holdProgress": 0.7
1407
+ },
1408
+ "usesGlobalBackgroundEffect": true,
1409
+ "textCanvas": "open",
1410
+ "schema": {
1411
+ "type": "object",
1412
+ "properties": {
1413
+ "authorName": {
1414
+ "type": "string",
1415
+ "title": "Author name",
1416
+ "description": "Display name shown bold next to the verified checkmark.",
1417
+ "default": "Your brand"
1418
+ },
1419
+ "authorHandle": {
1420
+ "type": "string",
1421
+ "title": "Handle",
1422
+ "description": "Username in @format. Leave empty to auto-derive from author name (e.g. 'Linear' \u2192 '@linear').",
1423
+ "default": ""
1424
+ },
1425
+ "authorVerified": {
1426
+ "type": "boolean",
1427
+ "title": "Verified checkmark",
1428
+ "description": "Show the blue verified checkmark next to the author name.",
1429
+ "examples": [
1430
+ true
1431
+ ],
1432
+ "default": false
1433
+ },
1434
+ "message": {
1435
+ "type": "string",
1436
+ "title": "Tweet text",
1437
+ "description": "The tweet/post content. Reads best at 1-3 lines (~80-180 chars). Emoji land naturally at the end of a phrase.",
1438
+ "default": "Just shipped the new feature \u{1F680}"
1439
+ },
1440
+ "likes": {
1441
+ "type": "number",
1442
+ "title": "Likes",
1443
+ "description": "Target like count (rolls up from 0).",
1444
+ "examples": [
1445
+ 602
1446
+ ],
1447
+ "default": 0
1448
+ },
1449
+ "replies": {
1450
+ "type": "number",
1451
+ "title": "Replies",
1452
+ "description": "Target reply count (rolls up from 0).",
1453
+ "examples": [
1454
+ 19
1455
+ ],
1456
+ "default": 0
1457
+ },
1458
+ "mediaUrl": {
1459
+ "type": "string",
1460
+ "title": "Background media",
1461
+ "description": "Optional photo or video URL behind this scene. When set, replaces the brand gradient.",
1462
+ "format": "uri",
1463
+ "default": ""
1464
+ },
1465
+ "mediaKeyword": {
1466
+ "type": "string",
1467
+ "title": "Background search keyword",
1468
+ "description": "2-4 word English term for Pexels stock-footage search (auto-fills mediaUrl).",
1469
+ "format": "stock-media-keyword",
1470
+ "default": ""
1471
+ },
1472
+ "mediaType": {
1473
+ "type": "string",
1474
+ "title": "Background media type",
1475
+ "description": "auto detects photo/video from URL. 'gradient' is a deliberate mode \u2014 atmospheric brand-color scene with no stock footage.",
1476
+ "enum": [
1477
+ "auto",
1478
+ "photo",
1479
+ "video",
1480
+ "gradient"
1481
+ ],
1482
+ "default": "auto"
1483
+ },
1484
+ "mediaPoster": {
1485
+ "type": "string",
1486
+ "title": "Background poster image",
1487
+ "description": "Still image URL shown while a video backdrop is decoding its first frame. Auto-filled from Pexels' thumbnail when fillPexelsUrls sets a video mediaUrl. Hides the gradient flash that would otherwise appear in the ~50\u2013400ms gap between a <video> mounting and decoding its first frame.",
1488
+ "format": "uri",
1489
+ "default": ""
1490
+ },
1491
+ "mediaPosition": {
1492
+ "type": "string",
1493
+ "title": "Background focal position",
1494
+ "description": "Controls which part of a photo or video stays visible when cover-cropped. Pick the subject's side or vertical anchor after inspecting the frame.",
1495
+ "enum": [
1496
+ "center",
1497
+ "top",
1498
+ "bottom",
1499
+ "left",
1500
+ "right"
1501
+ ],
1502
+ "default": "center"
1503
+ },
1504
+ "mediaTreatment": {
1505
+ "type": "string",
1506
+ "title": "Background contrast treatment",
1507
+ "description": "subtle preserves a visual hero; cinematic adds balanced contrast; text-safe adds a stronger wash for copy-heavy scenes.",
1508
+ "enum": [
1509
+ "subtle",
1510
+ "cinematic",
1511
+ "text-safe"
1512
+ ],
1513
+ "default": "cinematic"
1514
+ }
1515
+ },
1516
+ "required": [
1517
+ "authorName",
1518
+ "message"
1519
+ ],
1520
+ "additionalProperties": false,
1521
+ "x-vanillasky": {
1522
+ "allowsStockMedia": true
1523
+ }
1524
+ },
1525
+ "minDuration": 2.5,
1526
+ "preferredDuration": 3.5,
1527
+ "timing": {
1528
+ "contentFields": [
1529
+ "authorName",
1530
+ "message"
1531
+ ],
1532
+ "contentUnit": "words"
1533
+ }
1534
+ },
1535
+ {
1536
+ "id": "notification",
1537
+ "label": "Notification",
1538
+ "description": "A compact app notification card appears over a branded background.",
1539
+ "family": "Social & messaging",
1540
+ "jobs": [
1541
+ "setup",
1542
+ "proof"
1543
+ ],
1544
+ "register": "card-led",
1545
+ "useWhen": "A real alert, reminder, approval, completion, update, or ready-state is a product moment.",
1546
+ "avoidWhen": "The message is a broad announcement, generic explanation, or not naturally notification-shaped.",
1547
+ "usesGlobalTextEffect": false,
1548
+ "usesGlobalTransition": true,
1549
+ "transitionTiming": {
1550
+ "entryReadyProgress": 0.35,
1551
+ "holdProgress": 0.7
1552
+ },
1553
+ "usesGlobalBackgroundEffect": true,
1554
+ "textCanvas": "tight",
1555
+ "schema": {
1556
+ "type": "object",
1557
+ "properties": {
1558
+ "appName": {
1559
+ "type": "string",
1560
+ "title": "App name",
1561
+ "description": "App name shown in the notification header (e.g. Reminder, Lume, Linear, GitHub; 24 characters maximum).",
1562
+ "minLength": 1,
1563
+ "maxLength": 24,
1564
+ "default": "Reminder"
1565
+ },
1566
+ "appIcon": {
1567
+ "type": "string",
1568
+ "title": "App icon",
1569
+ "description": "Single emoji used as the app icon. Override the default \u{1F514} to retheme: \u{1F4C5} reminder, \u2705 task done, \u{1F48C} message, \u{1F389} celebration, \u26A1 system event.",
1570
+ "format": "emoji",
1571
+ "maxLength": 16,
1572
+ "default": "\u{1F514}"
1573
+ },
1574
+ "message": {
1575
+ "type": "string",
1576
+ "title": "Message",
1577
+ "description": "The notification body. Keep it to a short alert of at most 100 characters; 1-2 lines reads best.",
1578
+ "minLength": 1,
1579
+ "maxLength": 100,
1580
+ "default": "Believe in your dreams, they will lead you."
1581
+ },
1582
+ "mediaUrl": {
1583
+ "type": "string",
1584
+ "title": "Background media",
1585
+ "description": "Optional photo or video URL behind this scene. When set, replaces the brand gradient.",
1586
+ "format": "uri",
1587
+ "default": ""
1588
+ },
1589
+ "mediaKeyword": {
1590
+ "type": "string",
1591
+ "title": "Background search keyword",
1592
+ "description": "2-4 word English term for Pexels stock-footage search (auto-fills mediaUrl).",
1593
+ "format": "stock-media-keyword",
1594
+ "default": ""
1595
+ },
1596
+ "mediaType": {
1597
+ "type": "string",
1598
+ "title": "Background media type",
1599
+ "description": "auto detects photo/video from URL. 'gradient' is a deliberate mode \u2014 atmospheric brand-color scene with no stock footage.",
1600
+ "enum": [
1601
+ "auto",
1602
+ "photo",
1603
+ "video",
1604
+ "gradient"
1605
+ ],
1606
+ "default": "auto"
1607
+ },
1608
+ "mediaPoster": {
1609
+ "type": "string",
1610
+ "title": "Background poster image",
1611
+ "description": "Still image URL shown while a video backdrop is decoding its first frame. Auto-filled from Pexels' thumbnail when fillPexelsUrls sets a video mediaUrl. Hides the gradient flash that would otherwise appear in the ~50\u2013400ms gap between a <video> mounting and decoding its first frame.",
1612
+ "format": "uri",
1613
+ "default": ""
1614
+ },
1615
+ "mediaPosition": {
1616
+ "type": "string",
1617
+ "title": "Background focal position",
1618
+ "description": "Controls which part of a photo or video stays visible when cover-cropped. Pick the subject's side or vertical anchor after inspecting the frame.",
1619
+ "enum": [
1620
+ "center",
1621
+ "top",
1622
+ "bottom",
1623
+ "left",
1624
+ "right"
1625
+ ],
1626
+ "default": "center"
1627
+ },
1628
+ "mediaTreatment": {
1629
+ "type": "string",
1630
+ "title": "Background contrast treatment",
1631
+ "description": "subtle preserves a visual hero; cinematic adds balanced contrast; text-safe adds a stronger wash for copy-heavy scenes.",
1632
+ "enum": [
1633
+ "subtle",
1634
+ "cinematic",
1635
+ "text-safe"
1636
+ ],
1637
+ "default": "cinematic"
1638
+ }
1639
+ },
1640
+ "required": [
1641
+ "appName",
1642
+ "message"
1643
+ ],
1644
+ "additionalProperties": false,
1645
+ "x-vanillasky": {
1646
+ "allowsStockMedia": true
1647
+ }
1648
+ },
1649
+ "minDuration": 2,
1650
+ "preferredDuration": 3,
1651
+ "timing": {
1652
+ "contentFields": [
1653
+ "appName",
1654
+ "message"
1655
+ ],
1656
+ "contentUnit": "words"
1657
+ }
1658
+ },
1659
+ {
1660
+ "id": "chatMessenger",
1661
+ "label": "Messenger chat",
1662
+ "description": "A staggered Messenger-style conversation with three to five message bubbles.",
1663
+ "family": "Social & messaging",
1664
+ "jobs": [
1665
+ "setup",
1666
+ "proof"
1667
+ ],
1668
+ "register": "mockup-led",
1669
+ "useWhen": "A grounded back-and-forth naturally explains a use case, objection, support flow, or punchline.",
1670
+ "avoidWhen": "The source is not conversational, fewer than three meaningful messages exist, or pacing must be brief.",
1671
+ "usesGlobalTextEffect": false,
1672
+ "usesGlobalTransition": false,
1673
+ "usesGlobalBackgroundEffect": false,
1674
+ "textCanvas": "tight",
1675
+ "schema": {
1676
+ "type": "object",
1677
+ "properties": {
1678
+ "msg1": {
1679
+ "type": "string",
1680
+ "title": "Message 1 (received)",
1681
+ "description": "First message \u2014 left/received by default. Append |in or |out to override side. Aim for 3-4 messages total; use 5 with date chips only for a real time gap in a 10s+ scene.",
1682
+ "default": "Have you tried it yet?"
1683
+ },
1684
+ "msg2": {
1685
+ "type": "string",
1686
+ "title": "Message 2 (sent)",
1687
+ "description": "Second message \u2014 right/sent by default via |out. Replace the suffix with |in only when the grounded exchange requires it.",
1688
+ "default": "Yes \u2014 it saves me hours|out"
1689
+ },
1690
+ "msg3": {
1691
+ "type": "string",
1692
+ "title": "Message 3 (received)",
1693
+ "description": "Third message \u2014 left/received by default. Leave blank for a two-message exchange.",
1694
+ "default": "Okay, send me the link"
1695
+ },
1696
+ "msg4": {
1697
+ "type": "string",
1698
+ "title": "Message 4 (sent)",
1699
+ "description": "Fourth message \u2014 right/sent by default. Optional; most chats end here.",
1700
+ "examples": [
1701
+ "Sounds great \u2014 sharing access now"
1702
+ ],
1703
+ "default": ""
1704
+ },
1705
+ "dateChip1": {
1706
+ "type": "string",
1707
+ "title": "Date chip 1",
1708
+ "description": "Optional date label before message 4. Use only for a real time gap.",
1709
+ "default": ""
1710
+ },
1711
+ "dateChip2": {
1712
+ "type": "string",
1713
+ "title": "Date chip 2",
1714
+ "description": "Optional date label before message 5. Use only for a real time gap.",
1715
+ "default": ""
1716
+ },
1717
+ "msg5": {
1718
+ "type": "string",
1719
+ "title": "Message 5 (sent)",
1720
+ "description": "Optional fifth message \u2014 right/sent by default.",
1721
+ "default": ""
1722
+ },
1723
+ "theme": {
1724
+ "type": "string",
1725
+ "title": "Conversation theme",
1726
+ "enum": [
1727
+ "messenger",
1728
+ "whatsapp"
1729
+ ],
1730
+ "default": "messenger"
1731
+ }
1732
+ },
1733
+ "required": [
1734
+ "msg1",
1735
+ "msg2",
1736
+ "msg3"
1737
+ ],
1738
+ "additionalProperties": false
1739
+ },
1740
+ "minDuration": 5,
1741
+ "preferredDuration": 8,
1742
+ "timing": {
1743
+ "contentFields": [
1744
+ "msg1",
1745
+ "msg2",
1746
+ "msg3",
1747
+ "msg4",
1748
+ "msg5"
1749
+ ],
1750
+ "contentUnit": "words"
1751
+ }
1752
+ },
1753
+ {
1754
+ "id": "chatWhatsapp",
1755
+ "label": "WhatsApp chat",
1756
+ "description": "A staggered WhatsApp-style conversation with three to five message bubbles.",
1757
+ "family": "Social & messaging",
1758
+ "jobs": [
1759
+ "setup",
1760
+ "proof"
1761
+ ],
1762
+ "register": "mockup-led",
1763
+ "useWhen": "A grounded customer-service or personal back-and-forth is naturally associated with WhatsApp.",
1764
+ "avoidWhen": "The source is not conversational, fewer than three meaningful messages exist, or pacing must be brief.",
1765
+ "usesGlobalTextEffect": false,
1766
+ "usesGlobalTransition": false,
1767
+ "usesGlobalBackgroundEffect": false,
1768
+ "textCanvas": "tight",
1769
+ "schema": {
1770
+ "type": "object",
1771
+ "properties": {
1772
+ "msg1": {
1773
+ "type": "string",
1774
+ "title": "Message 1 (received)",
1775
+ "description": "First message \u2014 left/received by default. Append |in or |out to override side. Aim for 3-4 messages total; use 5 with date chips only for a real time gap in a 10s+ scene.",
1776
+ "default": "Have you tried it yet?"
1777
+ },
1778
+ "msg2": {
1779
+ "type": "string",
1780
+ "title": "Message 2 (sent)",
1781
+ "description": "Second message \u2014 right/sent by default via |out. Replace the suffix with |in only when the grounded exchange requires it.",
1782
+ "default": "Yes \u2014 it saves me hours|out"
1783
+ },
1784
+ "msg3": {
1785
+ "type": "string",
1786
+ "title": "Message 3 (received)",
1787
+ "description": "Third message \u2014 left/received by default. Leave blank for a two-message exchange.",
1788
+ "default": "Okay, send me the link"
1789
+ },
1790
+ "msg4": {
1791
+ "type": "string",
1792
+ "title": "Message 4 (sent)",
1793
+ "description": "Fourth message \u2014 right/sent by default. Optional; most chats end here.",
1794
+ "examples": [
1795
+ "Sounds great \u2014 sharing access now"
1796
+ ],
1797
+ "default": ""
1798
+ },
1799
+ "dateChip1": {
1800
+ "type": "string",
1801
+ "title": "Date chip 1",
1802
+ "description": "Optional date label before message 4. Use only for a real time gap.",
1803
+ "default": ""
1804
+ },
1805
+ "dateChip2": {
1806
+ "type": "string",
1807
+ "title": "Date chip 2",
1808
+ "description": "Optional date label before message 5. Use only for a real time gap.",
1809
+ "default": ""
1810
+ },
1811
+ "msg5": {
1812
+ "type": "string",
1813
+ "title": "Message 5 (sent)",
1814
+ "description": "Optional fifth message \u2014 right/sent by default.",
1815
+ "default": ""
1816
+ },
1817
+ "theme": {
1818
+ "type": "string",
1819
+ "title": "Conversation theme",
1820
+ "enum": [
1821
+ "messenger",
1822
+ "whatsapp"
1823
+ ],
1824
+ "default": "whatsapp"
1825
+ }
1826
+ },
1827
+ "required": [
1828
+ "msg1",
1829
+ "msg2",
1830
+ "msg3"
1831
+ ],
1832
+ "additionalProperties": false
1833
+ },
1834
+ "minDuration": 5,
1835
+ "preferredDuration": 8,
1836
+ "timing": {
1837
+ "contentFields": [
1838
+ "msg1",
1839
+ "msg2",
1840
+ "msg3",
1841
+ "msg4",
1842
+ "msg5"
1843
+ ],
1844
+ "contentUnit": "words"
1845
+ }
1846
+ },
1847
+ {
1848
+ "id": "milestone",
1849
+ "label": "Milestone",
1850
+ "description": "A large count-up achievement with an optional celebratory badge.",
1851
+ "family": "Data & metrics",
1852
+ "jobs": [
1853
+ "proof",
1854
+ "payoff"
1855
+ ],
1856
+ "register": "typography-led",
1857
+ "useWhen": "An exact grounded achievement count marks users, revenue, years, installs, customers, or adoption.",
1858
+ "avoidWhen": "The value is a release number, price, unbounded percentage, or not an achievement count.",
1859
+ "usesGlobalTextEffect": false,
1860
+ "usesGlobalTransition": false,
1861
+ "usesGlobalBackgroundEffect": false,
1862
+ "textCanvas": "open",
1863
+ "schema": {
1864
+ "type": "object",
1865
+ "properties": {
1866
+ "label": {
1867
+ "type": "string",
1868
+ "title": "Label",
1869
+ "description": "Short label above the number (e.g. Followers, Subscribers, Downloads; 32 characters maximum).",
1870
+ "minLength": 1,
1871
+ "maxLength": 32,
1872
+ "default": "Followers"
1873
+ },
1874
+ "targetNumber": {
1875
+ "type": "number",
1876
+ "title": "Target number",
1877
+ "description": "The milestone number to reach",
1878
+ "default": 1e4
1879
+ },
1880
+ "startNumber": {
1881
+ "type": "number",
1882
+ "title": "Start number",
1883
+ "description": "Number the counter starts rolling from. Defaults to 0.",
1884
+ "default": 0
1885
+ },
1886
+ "badgeText": {
1887
+ "type": "string",
1888
+ "title": "Badge text",
1889
+ "description": "Concise celebration badge text that pops in at the milestone (32 characters maximum).",
1890
+ "maxLength": 32,
1891
+ "examples": [
1892
+ "10K Followers!"
1893
+ ],
1894
+ "default": ""
1895
+ },
1896
+ "badgeEmoji": {
1897
+ "type": "string",
1898
+ "title": "Badge emoji",
1899
+ "description": "Emoji shown in the celebration badge",
1900
+ "format": "emoji",
1901
+ "maxLength": 16,
1902
+ "default": "\u{1F389}"
1903
+ },
1904
+ "mediaUrl": {
1905
+ "type": "string",
1906
+ "title": "Background media",
1907
+ "description": "Optional photo or video URL behind this scene. When set, replaces the brand gradient.",
1908
+ "format": "uri",
1909
+ "default": ""
1910
+ },
1911
+ "mediaKeyword": {
1912
+ "type": "string",
1913
+ "title": "Background search keyword",
1914
+ "description": "2-4 word English term for Pexels stock-footage search (auto-fills mediaUrl).",
1915
+ "format": "stock-media-keyword",
1916
+ "default": ""
1917
+ },
1918
+ "mediaType": {
1919
+ "type": "string",
1920
+ "title": "Background media type",
1921
+ "description": "auto detects photo/video from URL. 'gradient' is a deliberate mode \u2014 atmospheric brand-color scene with no stock footage.",
1922
+ "enum": [
1923
+ "auto",
1924
+ "photo",
1925
+ "video",
1926
+ "gradient"
1927
+ ],
1928
+ "default": "auto"
1929
+ },
1930
+ "mediaPoster": {
1931
+ "type": "string",
1932
+ "title": "Background poster image",
1933
+ "description": "Still image URL shown while a video backdrop is decoding its first frame. Auto-filled from Pexels' thumbnail when fillPexelsUrls sets a video mediaUrl. Hides the gradient flash that would otherwise appear in the ~50\u2013400ms gap between a <video> mounting and decoding its first frame.",
1934
+ "format": "uri",
1935
+ "default": ""
1936
+ },
1937
+ "mediaPosition": {
1938
+ "type": "string",
1939
+ "title": "Background focal position",
1940
+ "description": "Controls which part of a photo or video stays visible when cover-cropped. Pick the subject's side or vertical anchor after inspecting the frame.",
1941
+ "enum": [
1942
+ "center",
1943
+ "top",
1944
+ "bottom",
1945
+ "left",
1946
+ "right"
1947
+ ],
1948
+ "default": "center"
1949
+ },
1950
+ "mediaTreatment": {
1951
+ "type": "string",
1952
+ "title": "Background contrast treatment",
1953
+ "description": "subtle preserves a visual hero; cinematic adds balanced contrast; text-safe adds a stronger wash for copy-heavy scenes.",
1954
+ "enum": [
1955
+ "subtle",
1956
+ "cinematic",
1957
+ "text-safe"
1958
+ ],
1959
+ "default": "cinematic"
1960
+ }
1961
+ },
1962
+ "required": [
1963
+ "label",
1964
+ "targetNumber"
1965
+ ],
1966
+ "additionalProperties": false,
1967
+ "x-vanillasky": {
1968
+ "requiresStat": true,
1969
+ "allowsStockMedia": true
1970
+ }
1971
+ },
1972
+ "minDuration": 2.5,
1973
+ "preferredDuration": 4,
1974
+ "timing": {
1975
+ "contentFields": [
1976
+ "label",
1977
+ "badgeText"
1978
+ ],
1979
+ "contentUnit": "words"
1980
+ }
1981
+ },
1982
+ {
1983
+ "id": "reviewStack",
1984
+ "label": "Review stack",
1985
+ "description": "Three staggered review cards with titles, quotes, authors, and star ratings.",
1986
+ "family": "Social & messaging",
1987
+ "jobs": [
1988
+ "proof"
1989
+ ],
1990
+ "register": "card-led",
1991
+ "useWhen": "Three grounded review excerpts should land together as a stacked social-proof sequence.",
1992
+ "avoidWhen": "Fewer than three grounded review excerpts exist or their exact quoted language is missing.",
1993
+ "usesGlobalTextEffect": false,
1994
+ "usesGlobalTransition": false,
1995
+ "usesGlobalBackgroundEffect": false,
1996
+ "textCanvas": "open",
1997
+ "schema": {
1998
+ "type": "object",
1999
+ "properties": {
2000
+ "review1Title": {
2001
+ "type": "string",
2002
+ "title": "Review 1 \u2014 Title",
2003
+ "description": "Headline of the first review",
2004
+ "format": "grounded-quote",
2005
+ "default": "Life changing app"
2006
+ },
2007
+ "review1Body": {
2008
+ "type": "string",
2009
+ "title": "Review 1 \u2014 Body",
2010
+ "description": "Body text of the first review",
2011
+ "examples": [
2012
+ "Been using this for a month and can't imagine going back"
2013
+ ],
2014
+ "format": "grounded-quote",
2015
+ "default": ""
2016
+ },
2017
+ "review1Author": {
2018
+ "type": "string",
2019
+ "title": "Review 1 \u2014 Author",
2020
+ "description": "Author name for the first review",
2021
+ "examples": [
2022
+ "Sarah M."
2023
+ ],
2024
+ "default": ""
2025
+ },
2026
+ "review2Title": {
2027
+ "type": "string",
2028
+ "title": "Review 2 \u2014 Title",
2029
+ "description": "Headline of the second review",
2030
+ "format": "grounded-quote",
2031
+ "default": "Best in class"
2032
+ },
2033
+ "review2Body": {
2034
+ "type": "string",
2035
+ "title": "Review 2 \u2014 Body",
2036
+ "description": "Body text of the second review",
2037
+ "examples": [
2038
+ "Finally an app that just works. No bloat, no nonsense."
2039
+ ],
2040
+ "format": "grounded-quote",
2041
+ "default": ""
2042
+ },
2043
+ "review2Author": {
2044
+ "type": "string",
2045
+ "title": "Review 2 \u2014 Author",
2046
+ "description": "Author name for the second review",
2047
+ "examples": [
2048
+ "Mike R."
2049
+ ],
2050
+ "default": ""
2051
+ },
2052
+ "review3Title": {
2053
+ "type": "string",
2054
+ "title": "Review 3 \u2014 Title",
2055
+ "description": "Headline of the third review",
2056
+ "format": "grounded-quote",
2057
+ "default": "Exceeded expectations"
2058
+ },
2059
+ "review3Body": {
2060
+ "type": "string",
2061
+ "title": "Review 3 \u2014 Body",
2062
+ "description": "Body text of the third review",
2063
+ "examples": [
2064
+ "Worth every penny. The team clearly cares about quality."
2065
+ ],
2066
+ "format": "grounded-quote",
2067
+ "default": ""
2068
+ },
2069
+ "review3Author": {
2070
+ "type": "string",
2071
+ "title": "Review 3 \u2014 Author",
2072
+ "description": "Author name for the third review",
2073
+ "examples": [
2074
+ "Alex K."
2075
+ ],
2076
+ "default": ""
2077
+ },
2078
+ "mediaUrl": {
2079
+ "type": "string",
2080
+ "title": "Background media",
2081
+ "description": "Optional photo or video URL behind this scene. When set, replaces the brand gradient.",
2082
+ "format": "uri",
2083
+ "default": ""
2084
+ },
2085
+ "mediaKeyword": {
2086
+ "type": "string",
2087
+ "title": "Background search keyword",
2088
+ "description": "2-4 word English term for Pexels stock-footage search (auto-fills mediaUrl).",
2089
+ "format": "stock-media-keyword",
2090
+ "default": ""
2091
+ },
2092
+ "mediaType": {
2093
+ "type": "string",
2094
+ "title": "Background media type",
2095
+ "description": "auto detects photo/video from URL. 'gradient' is a deliberate mode \u2014 atmospheric brand-color scene with no stock footage.",
2096
+ "enum": [
2097
+ "auto",
2098
+ "photo",
2099
+ "video",
2100
+ "gradient"
2101
+ ],
2102
+ "default": "auto"
2103
+ },
2104
+ "mediaPoster": {
2105
+ "type": "string",
2106
+ "title": "Background poster image",
2107
+ "description": "Still image URL shown while a video backdrop is decoding its first frame. Auto-filled from Pexels' thumbnail when fillPexelsUrls sets a video mediaUrl. Hides the gradient flash that would otherwise appear in the ~50\u2013400ms gap between a <video> mounting and decoding its first frame.",
2108
+ "format": "uri",
2109
+ "default": ""
2110
+ },
2111
+ "mediaPosition": {
2112
+ "type": "string",
2113
+ "title": "Background focal position",
2114
+ "description": "Controls which part of a photo or video stays visible when cover-cropped. Pick the subject's side or vertical anchor after inspecting the frame.",
2115
+ "enum": [
2116
+ "center",
2117
+ "top",
2118
+ "bottom",
2119
+ "left",
2120
+ "right"
2121
+ ],
2122
+ "default": "center"
2123
+ },
2124
+ "mediaTreatment": {
2125
+ "type": "string",
2126
+ "title": "Background contrast treatment",
2127
+ "description": "subtle preserves a visual hero; cinematic adds balanced contrast; text-safe adds a stronger wash for copy-heavy scenes.",
2128
+ "enum": [
2129
+ "subtle",
2130
+ "cinematic",
2131
+ "text-safe"
2132
+ ],
2133
+ "default": "cinematic"
2134
+ }
2135
+ },
2136
+ "required": [
2137
+ "review1Title",
2138
+ "review2Title",
2139
+ "review3Title"
2140
+ ],
2141
+ "additionalProperties": false,
2142
+ "x-vanillasky": {
2143
+ "allowsStockMedia": true
2144
+ }
2145
+ },
2146
+ "minDuration": 4,
2147
+ "preferredDuration": 6,
2148
+ "timing": {
2149
+ "contentFields": [
2150
+ "review1Title",
2151
+ "review1Body",
2152
+ "review2Title",
2153
+ "review2Body",
2154
+ "review3Title",
2155
+ "review3Body"
2156
+ ],
2157
+ "contentUnit": "words"
2158
+ }
2159
+ },
2160
+ {
2161
+ "id": "testimonial",
2162
+ "label": "Testimonial",
2163
+ "description": "A single customer quote card with author name, role, and avatar.",
2164
+ "family": "Social & messaging",
2165
+ "jobs": [
2166
+ "proof"
2167
+ ],
2168
+ "register": "card-led",
2169
+ "useWhen": "One exact attributed testimonial, review, or named endorsement is the strongest proof point.",
2170
+ "avoidWhen": "The quote or attribution is not present verbatim in the source, or several reviews matter equally.",
2171
+ "usesGlobalTextEffect": false,
2172
+ "usesGlobalTransition": false,
2173
+ "usesGlobalBackgroundEffect": false,
2174
+ "textCanvas": "open",
2175
+ "schema": {
2176
+ "type": "object",
2177
+ "properties": {
2178
+ "quote": {
2179
+ "type": "string",
2180
+ "title": "Quote",
2181
+ "description": "Quote text",
2182
+ "format": "grounded-quote",
2183
+ "default": "Best decision I made this year. Our conversion rate doubled in the first week."
2184
+ },
2185
+ "authorName": {
2186
+ "type": "string",
2187
+ "title": "Author name",
2188
+ "description": "Name of the person being quoted",
2189
+ "default": "Jessica Torres"
2190
+ },
2191
+ "authorRole": {
2192
+ "type": "string",
2193
+ "title": "Role / title",
2194
+ "description": "Job title or description below the name",
2195
+ "examples": [
2196
+ "VP of Marketing, Acme Inc"
2197
+ ],
2198
+ "default": ""
2199
+ },
2200
+ "mediaUrl": {
2201
+ "type": "string",
2202
+ "title": "Background media",
2203
+ "description": "Optional photo or video URL behind this scene. When set, replaces the brand gradient.",
2204
+ "format": "uri",
2205
+ "default": ""
2206
+ },
2207
+ "mediaKeyword": {
2208
+ "type": "string",
2209
+ "title": "Background search keyword",
2210
+ "description": "2-4 word English term for Pexels stock-footage search (auto-fills mediaUrl).",
2211
+ "format": "stock-media-keyword",
2212
+ "default": ""
2213
+ },
2214
+ "mediaType": {
2215
+ "type": "string",
2216
+ "title": "Background media type",
2217
+ "description": "auto detects photo/video from URL. 'gradient' is a deliberate mode \u2014 atmospheric brand-color scene with no stock footage.",
2218
+ "enum": [
2219
+ "auto",
2220
+ "photo",
2221
+ "video",
2222
+ "gradient"
2223
+ ],
2224
+ "default": "auto"
2225
+ },
2226
+ "mediaPoster": {
2227
+ "type": "string",
2228
+ "title": "Background poster image",
2229
+ "description": "Still image URL shown while a video backdrop is decoding its first frame. Auto-filled from Pexels' thumbnail when fillPexelsUrls sets a video mediaUrl. Hides the gradient flash that would otherwise appear in the ~50\u2013400ms gap between a <video> mounting and decoding its first frame.",
2230
+ "format": "uri",
2231
+ "default": ""
2232
+ },
2233
+ "mediaPosition": {
2234
+ "type": "string",
2235
+ "title": "Background focal position",
2236
+ "description": "Controls which part of a photo or video stays visible when cover-cropped. Pick the subject's side or vertical anchor after inspecting the frame.",
2237
+ "enum": [
2238
+ "center",
2239
+ "top",
2240
+ "bottom",
2241
+ "left",
2242
+ "right"
2243
+ ],
2244
+ "default": "center"
2245
+ },
2246
+ "mediaTreatment": {
2247
+ "type": "string",
2248
+ "title": "Background contrast treatment",
2249
+ "description": "subtle preserves a visual hero; cinematic adds balanced contrast; text-safe adds a stronger wash for copy-heavy scenes.",
2250
+ "enum": [
2251
+ "subtle",
2252
+ "cinematic",
2253
+ "text-safe"
2254
+ ],
2255
+ "default": "cinematic"
2256
+ }
2257
+ },
2258
+ "required": [
2259
+ "quote",
2260
+ "authorName"
2261
+ ],
2262
+ "additionalProperties": false,
2263
+ "x-vanillasky": {
2264
+ "allowsStockMedia": true
2265
+ }
2266
+ },
2267
+ "minDuration": 2.5,
2268
+ "preferredDuration": 4,
2269
+ "timing": {
2270
+ "contentFields": [
2271
+ "quote",
2272
+ "authorName",
2273
+ "authorRole"
2274
+ ],
2275
+ "contentUnit": "words"
2276
+ }
2277
+ },
2278
+ {
2279
+ "id": "incomingCall",
2280
+ "label": "Incoming call",
2281
+ "description": "A full-screen phone-call interface presents the caller as a dramatic interruption.",
2282
+ "family": "Social & messaging",
2283
+ "jobs": [
2284
+ "setup",
2285
+ "punctuation"
2286
+ ],
2287
+ "register": "card-led",
2288
+ "useWhen": "An urgent, interruptive, comeback, or dramatic moment can be personified as a call.",
2289
+ "avoidWhen": "The story is calm, formal, or better served by a literal notification or direct message.",
2290
+ "usesGlobalTextEffect": false,
2291
+ "usesGlobalTransition": false,
2292
+ "usesGlobalBackgroundEffect": true,
2293
+ "textCanvas": "open",
2294
+ "schema": {
2295
+ "type": "object",
2296
+ "properties": {
2297
+ "callerName": {
2298
+ "type": "string",
2299
+ "title": "Caller name",
2300
+ "description": "Brand or product shown as the caller. One to three words reads best; 40 characters maximum.",
2301
+ "minLength": 1,
2302
+ "maxLength": 40,
2303
+ "default": "Your brand"
2304
+ },
2305
+ "subtitle": {
2306
+ "type": "string",
2307
+ "title": "Subtitle",
2308
+ "description": "Smaller single-line text below the caller name (28 characters maximum).",
2309
+ "maxLength": 28,
2310
+ "default": "is calling...."
2311
+ },
2312
+ "declineLabel": {
2313
+ "type": "string",
2314
+ "title": "Decline label",
2315
+ "description": "Short label below the red button (12 characters maximum).",
2316
+ "maxLength": 12,
2317
+ "default": "Decline"
2318
+ },
2319
+ "acceptLabel": {
2320
+ "type": "string",
2321
+ "title": "Accept label",
2322
+ "description": "Short label below the green button (12 characters maximum).",
2323
+ "maxLength": 12,
2324
+ "default": "Accept"
2325
+ },
2326
+ "mediaUrl": {
2327
+ "type": "string",
2328
+ "title": "Background media",
2329
+ "description": "Optional photo or video URL behind this scene. When set, replaces the brand gradient.",
2330
+ "format": "uri",
2331
+ "default": ""
2332
+ },
2333
+ "mediaKeyword": {
2334
+ "type": "string",
2335
+ "title": "Background search keyword",
2336
+ "description": "2-4 word English term for Pexels stock-footage search (auto-fills mediaUrl).",
2337
+ "format": "stock-media-keyword",
2338
+ "default": ""
2339
+ },
2340
+ "mediaType": {
2341
+ "type": "string",
2342
+ "title": "Background media type",
2343
+ "description": "auto detects photo/video from URL. 'gradient' is a deliberate mode \u2014 atmospheric brand-color scene with no stock footage.",
2344
+ "enum": [
2345
+ "auto",
2346
+ "photo",
2347
+ "video",
2348
+ "gradient"
2349
+ ],
2350
+ "default": "auto"
2351
+ },
2352
+ "mediaPoster": {
2353
+ "type": "string",
2354
+ "title": "Background poster image",
2355
+ "description": "Still image URL shown while a video backdrop is decoding its first frame. Auto-filled from Pexels' thumbnail when fillPexelsUrls sets a video mediaUrl. Hides the gradient flash that would otherwise appear in the ~50\u2013400ms gap between a <video> mounting and decoding its first frame.",
2356
+ "format": "uri",
2357
+ "default": ""
2358
+ },
2359
+ "mediaPosition": {
2360
+ "type": "string",
2361
+ "title": "Background focal position",
2362
+ "description": "Controls which part of a photo or video stays visible when cover-cropped. Pick the subject's side or vertical anchor after inspecting the frame.",
2363
+ "enum": [
2364
+ "center",
2365
+ "top",
2366
+ "bottom",
2367
+ "left",
2368
+ "right"
2369
+ ],
2370
+ "default": "center"
2371
+ },
2372
+ "mediaTreatment": {
2373
+ "type": "string",
2374
+ "title": "Background contrast treatment",
2375
+ "description": "subtle preserves a visual hero; cinematic adds balanced contrast; text-safe adds a stronger wash for copy-heavy scenes.",
2376
+ "enum": [
2377
+ "subtle",
2378
+ "cinematic",
2379
+ "text-safe"
2380
+ ],
2381
+ "default": "cinematic"
2382
+ }
2383
+ },
2384
+ "required": [
2385
+ "callerName"
2386
+ ],
2387
+ "additionalProperties": false,
2388
+ "x-vanillasky": {
2389
+ "allowsStockMedia": true
2390
+ }
2391
+ },
2392
+ "minDuration": 2.5,
2393
+ "preferredDuration": 4,
2394
+ "timing": {
2395
+ "contentFields": [
2396
+ "callerName",
2397
+ "subtitle"
2398
+ ],
2399
+ "contentUnit": "words"
2400
+ }
2401
+ },
2402
+ {
2403
+ "id": "brandMessage",
2404
+ "label": "Brand message",
2405
+ "description": "A single iMessage-style outgoing bubble floats over media or a brand gradient.",
2406
+ "family": "Social & messaging",
2407
+ "jobs": [
2408
+ "claim",
2409
+ "payoff"
2410
+ ],
2411
+ "register": "card-led",
2412
+ "useWhen": "A direct one-to-one thank-you, invitation, promise, or concise launch note is the point.",
2413
+ "avoidWhen": "The scene needs explanation, structured proof, a conversation, or a generic filler line.",
2414
+ "usesGlobalTextEffect": false,
2415
+ "usesGlobalTransition": false,
2416
+ "usesGlobalBackgroundEffect": true,
2417
+ "textCanvas": "open",
2418
+ "schema": {
2419
+ "type": "object",
2420
+ "properties": {
2421
+ "message": {
2422
+ "type": "string",
2423
+ "title": "Message",
2424
+ "description": "The brand-voiced message rendered as a single iMessage-style outgoing bubble (right side, cream) over the media background. 4-12 words reads best \u2014 short messages produce a tight bubble; longer ones wrap inside the 75% width cap. Examples: 'We built this for you.', 'Thanks for being here.', 'See what's new this week.'",
2425
+ "default": "We built this for you."
2426
+ },
2427
+ "mediaUrl": {
2428
+ "type": "string",
2429
+ "title": "Background media",
2430
+ "description": "Optional photo or video URL behind this scene. When set, replaces the brand gradient.",
2431
+ "format": "uri",
2432
+ "default": ""
2433
+ },
2434
+ "mediaKeyword": {
2435
+ "type": "string",
2436
+ "title": "Background search keyword",
2437
+ "description": "2-4 word English term for Pexels stock-footage search (auto-fills mediaUrl).",
2438
+ "format": "stock-media-keyword",
2439
+ "default": ""
2440
+ },
2441
+ "mediaType": {
2442
+ "type": "string",
2443
+ "title": "Background media type",
2444
+ "description": "auto detects photo/video from URL. 'gradient' is a deliberate mode \u2014 atmospheric brand-color scene with no stock footage.",
2445
+ "enum": [
2446
+ "auto",
2447
+ "photo",
2448
+ "video",
2449
+ "gradient"
2450
+ ],
2451
+ "default": "auto"
2452
+ },
2453
+ "mediaPoster": {
2454
+ "type": "string",
2455
+ "title": "Background poster image",
2456
+ "description": "Still image URL shown while a video backdrop is decoding its first frame. Auto-filled from Pexels' thumbnail when fillPexelsUrls sets a video mediaUrl. Hides the gradient flash that would otherwise appear in the ~50\u2013400ms gap between a <video> mounting and decoding its first frame.",
2457
+ "format": "uri",
2458
+ "default": ""
2459
+ },
2460
+ "mediaPosition": {
2461
+ "type": "string",
2462
+ "title": "Background focal position",
2463
+ "description": "Controls which part of a photo or video stays visible when cover-cropped. Pick the subject's side or vertical anchor after inspecting the frame.",
2464
+ "enum": [
2465
+ "center",
2466
+ "top",
2467
+ "bottom",
2468
+ "left",
2469
+ "right"
2470
+ ],
2471
+ "default": "center"
2472
+ },
2473
+ "mediaTreatment": {
2474
+ "type": "string",
2475
+ "title": "Background contrast treatment",
2476
+ "description": "subtle preserves a visual hero; cinematic adds balanced contrast; text-safe adds a stronger wash for copy-heavy scenes.",
2477
+ "enum": [
2478
+ "subtle",
2479
+ "cinematic",
2480
+ "text-safe"
2481
+ ],
2482
+ "default": "cinematic"
2483
+ }
2484
+ },
2485
+ "required": [
2486
+ "message"
2487
+ ],
2488
+ "additionalProperties": false,
2489
+ "x-vanillasky": {
2490
+ "allowsStockMedia": true
2491
+ }
2492
+ },
2493
+ "minDuration": 2.5,
2494
+ "preferredDuration": 4,
2495
+ "timing": {
2496
+ "contentFields": [
2497
+ "message"
2498
+ ],
2499
+ "contentUnit": "words"
2500
+ }
2501
+ },
2502
+ {
2503
+ "id": "promptInput",
2504
+ "label": "Prompt input",
2505
+ "description": "An AI prompt pill expands and types a user instruction over a branded background.",
2506
+ "family": "Product showcase",
2507
+ "jobs": [
2508
+ "setup",
2509
+ "proof"
2510
+ ],
2511
+ "register": "card-led",
2512
+ "useWhen": "Prompting, asking an AI, or describing an intended result is central to the product workflow.",
2513
+ "avoidWhen": "The product is not prompt-driven or the source provides no meaningful prompt to demonstrate.",
2514
+ "usesGlobalTextEffect": false,
2515
+ "usesGlobalTransition": false,
2516
+ "usesGlobalBackgroundEffect": true,
2517
+ "textCanvas": "open",
2518
+ "schema": {
2519
+ "type": "object",
2520
+ "properties": {
2521
+ "promptText": {
2522
+ "type": "string",
2523
+ "title": "Prompt text",
2524
+ "description": "Example prompt that types into the AI prompt bar. Use 3-7 words and at most 40 characters so the full prompt stays legible in both orientations (e.g. 'Make a launch video for our app').",
2525
+ "minLength": 1,
2526
+ "maxLength": 40,
2527
+ "default": "Make a launch video for our app"
2528
+ },
2529
+ "mediaUrl": {
2530
+ "type": "string",
2531
+ "title": "Background media",
2532
+ "description": "Optional photo or video URL behind this scene. When set, replaces the brand gradient.",
2533
+ "format": "uri",
2534
+ "default": ""
2535
+ },
2536
+ "mediaKeyword": {
2537
+ "type": "string",
2538
+ "title": "Background search keyword",
2539
+ "description": "2-4 word English term for Pexels stock-footage search (auto-fills mediaUrl).",
2540
+ "format": "stock-media-keyword",
2541
+ "default": ""
2542
+ },
2543
+ "mediaType": {
2544
+ "type": "string",
2545
+ "title": "Background media type",
2546
+ "description": "auto detects photo/video from URL. 'gradient' is a deliberate mode \u2014 atmospheric brand-color scene with no stock footage.",
2547
+ "enum": [
2548
+ "auto",
2549
+ "photo",
2550
+ "video",
2551
+ "gradient"
2552
+ ],
2553
+ "default": "auto"
2554
+ },
2555
+ "mediaPoster": {
2556
+ "type": "string",
2557
+ "title": "Background poster image",
2558
+ "description": "Still image URL shown while a video backdrop is decoding its first frame. Auto-filled from Pexels' thumbnail when fillPexelsUrls sets a video mediaUrl. Hides the gradient flash that would otherwise appear in the ~50\u2013400ms gap between a <video> mounting and decoding its first frame.",
2559
+ "format": "uri",
2560
+ "default": ""
2561
+ },
2562
+ "mediaPosition": {
2563
+ "type": "string",
2564
+ "title": "Background focal position",
2565
+ "description": "Controls which part of a photo or video stays visible when cover-cropped. Pick the subject's side or vertical anchor after inspecting the frame.",
2566
+ "enum": [
2567
+ "center",
2568
+ "top",
2569
+ "bottom",
2570
+ "left",
2571
+ "right"
2572
+ ],
2573
+ "default": "center"
2574
+ },
2575
+ "mediaTreatment": {
2576
+ "type": "string",
2577
+ "title": "Background contrast treatment",
2578
+ "description": "subtle preserves a visual hero; cinematic adds balanced contrast; text-safe adds a stronger wash for copy-heavy scenes.",
2579
+ "enum": [
2580
+ "subtle",
2581
+ "cinematic",
2582
+ "text-safe"
2583
+ ],
2584
+ "default": "cinematic"
2585
+ }
2586
+ },
2587
+ "required": [
2588
+ "promptText"
2589
+ ],
2590
+ "additionalProperties": false,
2591
+ "x-vanillasky": {
2592
+ "allowsStockMedia": true
2593
+ }
2594
+ },
2595
+ "minDuration": 2.5,
2596
+ "preferredDuration": 4,
2597
+ "timing": {
2598
+ "contentFields": [
2599
+ "promptText"
2600
+ ],
2601
+ "contentUnit": "characters"
2602
+ }
2603
+ },
2604
+ {
2605
+ "id": "beforeAfter",
2606
+ "label": "Before and after",
2607
+ "description": "Two contrasting states trade places in a split transformation layout.",
2608
+ "family": "Explainers",
2609
+ "jobs": [
2610
+ "setup",
2611
+ "proof"
2612
+ ],
2613
+ "register": "motion-led",
2614
+ "useWhen": "A grounded transformation contrasts an old and new state, manual and automated, or chaos and order.",
2615
+ "avoidWhen": "The source lacks two distinct states or the comparison depends on exact labeled quantities.",
2616
+ "usesGlobalTextEffect": true,
2617
+ "usesGlobalTransition": false,
2618
+ "usesGlobalBackgroundEffect": false,
2619
+ "textCanvas": "open",
2620
+ "schema": {
2621
+ "type": "object",
2622
+ "properties": {
2623
+ "problemLabel": {
2624
+ "type": "string",
2625
+ "title": "Before label",
2626
+ "description": "Short uppercase pill label shown above the before headline (12 characters maximum).",
2627
+ "maxLength": 12,
2628
+ "default": "BEFORE"
2629
+ },
2630
+ "problemHeadline": {
2631
+ "type": "string",
2632
+ "title": "Before headline",
2633
+ "description": "Headline shown during the before phase. Use 2-5 words and at most 48 characters. Sits centered beneath the BEFORE pill.",
2634
+ "minLength": 1,
2635
+ "maxLength": 48,
2636
+ "default": "Your calendar today."
2637
+ },
2638
+ "solutionLabel": {
2639
+ "type": "string",
2640
+ "title": "After label",
2641
+ "description": "Short uppercase pill label shown above the after headline (12 characters maximum).",
2642
+ "maxLength": 12,
2643
+ "default": "AFTER"
2644
+ },
2645
+ "solutionHeadline": {
2646
+ "type": "string",
2647
+ "title": "After headline",
2648
+ "description": "Headline shown during the after phase. Use 2-5 words and at most 48 characters. Sits centered beneath the AFTER pill.",
2649
+ "minLength": 1,
2650
+ "maxLength": 48,
2651
+ "default": "Calmly organized."
2652
+ },
2653
+ "problemEmojis": {
2654
+ "type": "array",
2655
+ "title": "Before emojis",
2656
+ "description": 'JSON array of emojis for the chaos / before state (5-8 types, cycled to fill 16 slots distributed around the central text). Example: ["\u{1F4C5}", "\u{1F630}", "\u{1F4BC}", "\u{1F4CA}", "\u23F0", "\u{1F4AC}", "\u{1F4DE}", "\u{1F514}"]',
2657
+ "items": {
2658
+ "type": "string",
2659
+ "format": "emoji",
2660
+ "minLength": 1,
2661
+ "maxLength": 16
2662
+ },
2663
+ "minItems": 5,
2664
+ "maxItems": 8,
2665
+ "default": [
2666
+ "\u{1F4C5}",
2667
+ "\u{1F630}",
2668
+ "\u{1F4BC}",
2669
+ "\u{1F4CA}",
2670
+ "\u23F0",
2671
+ "\u{1F4AC}",
2672
+ "\u{1F4DE}",
2673
+ "\u{1F514}"
2674
+ ]
2675
+ },
2676
+ "solutionEmojis": {
2677
+ "type": "array",
2678
+ "title": "After emojis",
2679
+ "description": 'JSON array of emojis for the calm / after state (3-5 types, cycled to fill 16 slots distributed around the central text). Example: ["\u2728", "\u{1F4CB}", "\u2705", "\u{1F3AF}"]',
2680
+ "items": {
2681
+ "type": "string",
2682
+ "format": "emoji",
2683
+ "minLength": 1,
2684
+ "maxLength": 16
2685
+ },
2686
+ "minItems": 3,
2687
+ "maxItems": 5,
2688
+ "default": [
2689
+ "\u2728",
2690
+ "\u{1F4CB}",
2691
+ "\u2705",
2692
+ "\u{1F3AF}"
2693
+ ]
2694
+ },
2695
+ "showEmojis": {
2696
+ "type": "boolean",
2697
+ "title": "Show decorative emojis",
2698
+ "description": "Show the animated emoji scatter. Disable for sober financial or editorial comparisons.",
2699
+ "default": true
2700
+ }
2701
+ },
2702
+ "required": [
2703
+ "problemHeadline",
2704
+ "solutionHeadline",
2705
+ "problemEmojis",
2706
+ "solutionEmojis"
2707
+ ],
2708
+ "additionalProperties": false
2709
+ },
2710
+ "minDuration": 3,
2711
+ "preferredDuration": 4.5,
2712
+ "timing": {
2713
+ "contentFields": [
2714
+ "problemHeadline",
2715
+ "solutionHeadline"
2716
+ ],
2717
+ "contentUnit": "words"
2718
+ }
2719
+ },
2720
+ {
2721
+ "id": "tripleStats",
2722
+ "label": "Triple stats",
2723
+ "description": "Exactly three peer values and labels appear as a balanced metric row.",
2724
+ "family": "Data & metrics",
2725
+ "jobs": [
2726
+ "claim",
2727
+ "proof"
2728
+ ],
2729
+ "register": "typography-led",
2730
+ "useWhen": "Exactly three grounded peer metrics or compact specs carry comparable importance.",
2731
+ "avoidWhen": "One value is the hero, the values are not comparable, or fewer than three grounded facts exist.",
2732
+ "usesGlobalTextEffect": false,
2733
+ "usesGlobalTransition": true,
2734
+ "transitionTiming": {
2735
+ "entryReadyProgress": 0.2,
2736
+ "holdProgress": 0.7
2737
+ },
2738
+ "usesGlobalBackgroundEffect": true,
2739
+ "textCanvas": "tight",
2740
+ "schema": {
2741
+ "type": "object",
2742
+ "properties": {
2743
+ "texts": {
2744
+ "type": "string",
2745
+ "title": "Text",
2746
+ "description": "Concise title above the three metrics (48 characters maximum).",
2747
+ "minLength": 1,
2748
+ "maxLength": 48,
2749
+ "default": "By the numbers."
2750
+ },
2751
+ "stat1Value": {
2752
+ "type": "string",
2753
+ "title": "Stat 1 value",
2754
+ "description": "First compact stat value (e.g. 10K, 99.9%, $2M; 12 characters maximum).",
2755
+ "minLength": 1,
2756
+ "maxLength": 12,
2757
+ "default": "10K"
2758
+ },
2759
+ "stat1Label": {
2760
+ "type": "string",
2761
+ "title": "Stat 1 label",
2762
+ "description": "Short label below the first number (24 characters maximum).",
2763
+ "minLength": 1,
2764
+ "maxLength": 24,
2765
+ "default": "Users"
2766
+ },
2767
+ "stat2Value": {
2768
+ "type": "string",
2769
+ "title": "Stat 2 value",
2770
+ "description": "Second compact stat value (12 characters maximum).",
2771
+ "minLength": 1,
2772
+ "maxLength": 12,
2773
+ "default": "99.9%"
2774
+ },
2775
+ "stat2Label": {
2776
+ "type": "string",
2777
+ "title": "Stat 2 label",
2778
+ "description": "Short label below the second number (24 characters maximum).",
2779
+ "minLength": 1,
2780
+ "maxLength": 24,
2781
+ "default": "Uptime"
2782
+ },
2783
+ "stat3Value": {
2784
+ "type": "string",
2785
+ "title": "Stat 3 value",
2786
+ "description": "Third compact stat value (12 characters maximum).",
2787
+ "minLength": 1,
2788
+ "maxLength": 12,
2789
+ "default": "<50ms"
2790
+ },
2791
+ "stat3Label": {
2792
+ "type": "string",
2793
+ "title": "Stat 3 label",
2794
+ "description": "Short label below the third number (24 characters maximum).",
2795
+ "minLength": 1,
2796
+ "maxLength": 24,
2797
+ "default": "Latency"
2798
+ },
2799
+ "mediaUrl": {
2800
+ "type": "string",
2801
+ "title": "Background media",
2802
+ "description": "Optional photo or video URL behind this scene. When set, replaces the brand gradient.",
2803
+ "format": "uri",
2804
+ "default": ""
2805
+ },
2806
+ "mediaKeyword": {
2807
+ "type": "string",
2808
+ "title": "Background search keyword",
2809
+ "description": "2-4 word English term for Pexels stock-footage search (auto-fills mediaUrl).",
2810
+ "format": "stock-media-keyword",
2811
+ "default": ""
2812
+ },
2813
+ "mediaType": {
2814
+ "type": "string",
2815
+ "title": "Background media type",
2816
+ "description": "auto detects photo/video from URL. 'gradient' is a deliberate mode \u2014 atmospheric brand-color scene with no stock footage.",
2817
+ "enum": [
2818
+ "auto",
2819
+ "photo",
2820
+ "video",
2821
+ "gradient"
2822
+ ],
2823
+ "default": "auto"
2824
+ },
2825
+ "mediaPoster": {
2826
+ "type": "string",
2827
+ "title": "Background poster image",
2828
+ "description": "Still image URL shown while a video backdrop is decoding its first frame. Auto-filled from Pexels' thumbnail when fillPexelsUrls sets a video mediaUrl. Hides the gradient flash that would otherwise appear in the ~50\u2013400ms gap between a <video> mounting and decoding its first frame.",
2829
+ "format": "uri",
2830
+ "default": ""
2831
+ },
2832
+ "mediaPosition": {
2833
+ "type": "string",
2834
+ "title": "Background focal position",
2835
+ "description": "Controls which part of a photo or video stays visible when cover-cropped. Pick the subject's side or vertical anchor after inspecting the frame.",
2836
+ "enum": [
2837
+ "center",
2838
+ "top",
2839
+ "bottom",
2840
+ "left",
2841
+ "right"
2842
+ ],
2843
+ "default": "center"
2844
+ },
2845
+ "mediaTreatment": {
2846
+ "type": "string",
2847
+ "title": "Background contrast treatment",
2848
+ "description": "subtle preserves a visual hero; cinematic adds balanced contrast; text-safe adds a stronger wash for copy-heavy scenes.",
2849
+ "enum": [
2850
+ "subtle",
2851
+ "cinematic",
2852
+ "text-safe"
2853
+ ],
2854
+ "default": "cinematic"
2855
+ }
2856
+ },
2857
+ "required": [
2858
+ "texts",
2859
+ "stat1Value",
2860
+ "stat1Label",
2861
+ "stat2Value",
2862
+ "stat2Label",
2863
+ "stat3Value",
2864
+ "stat3Label"
2865
+ ],
2866
+ "additionalProperties": false,
2867
+ "x-vanillasky": {
2868
+ "requiresStat": true,
2869
+ "allowsStockMedia": true
2870
+ }
2871
+ },
2872
+ "minDuration": 2,
2873
+ "preferredDuration": 3,
2874
+ "timing": {
2875
+ "contentFields": [
2876
+ "texts",
2877
+ "stat1Label",
2878
+ "stat2Label",
2879
+ "stat3Label"
2880
+ ],
2881
+ "contentUnit": "words"
2882
+ }
2883
+ },
2884
+ {
2885
+ "id": "problemSolution",
2886
+ "label": "Problem and solution",
2887
+ "description": "A problem card transitions into a paired solution card.",
2888
+ "family": "Explainers",
2889
+ "jobs": [
2890
+ "setup",
2891
+ "proof"
2892
+ ],
2893
+ "register": "card-led",
2894
+ "useWhen": "The story contains one crisp, literal pain statement and one corresponding solution statement.",
2895
+ "avoidWhen": "The source is a broad announcement or lacks a specific pain-and-fix pair.",
2896
+ "usesGlobalTextEffect": false,
2897
+ "usesGlobalTransition": false,
2898
+ "usesGlobalBackgroundEffect": true,
2899
+ "textCanvas": "open",
2900
+ "schema": {
2901
+ "type": "object",
2902
+ "properties": {
2903
+ "problemLabel": {
2904
+ "type": "string",
2905
+ "title": "Problem label",
2906
+ "description": "Short uppercase label shown above the problem text (16 characters maximum).",
2907
+ "maxLength": 16,
2908
+ "default": "THE PROBLEM"
2909
+ },
2910
+ "problemText": {
2911
+ "type": "string",
2912
+ "title": "Problem",
2913
+ "description": "The problem statement \u2014 keep it punchy, 1 sentence",
2914
+ "default": "Teams waste 40% of time in meetings"
2915
+ },
2916
+ "solutionLabel": {
2917
+ "type": "string",
2918
+ "title": "Solution label",
2919
+ "description": "Short uppercase label shown above the solution text (16 characters maximum).",
2920
+ "maxLength": 16,
2921
+ "default": "THE SOLUTION"
2922
+ },
2923
+ "solutionText": {
2924
+ "type": "string",
2925
+ "title": "Solution",
2926
+ "description": "The solution statement \u2014 your value prop in 1 sentence",
2927
+ "default": "AI that summarizes in seconds"
2928
+ },
2929
+ "mediaUrl": {
2930
+ "type": "string",
2931
+ "title": "Background media",
2932
+ "description": "Optional photo or video URL behind this scene. When set, replaces the brand gradient.",
2933
+ "format": "uri",
2934
+ "default": ""
2935
+ },
2936
+ "mediaKeyword": {
2937
+ "type": "string",
2938
+ "title": "Background search keyword",
2939
+ "description": "2-4 word English term for Pexels stock-footage search (auto-fills mediaUrl).",
2940
+ "format": "stock-media-keyword",
2941
+ "default": ""
2942
+ },
2943
+ "mediaType": {
2944
+ "type": "string",
2945
+ "title": "Background media type",
2946
+ "description": "auto detects photo/video from URL. 'gradient' is a deliberate mode \u2014 atmospheric brand-color scene with no stock footage.",
2947
+ "enum": [
2948
+ "auto",
2949
+ "photo",
2950
+ "video",
2951
+ "gradient"
2952
+ ],
2953
+ "default": "auto"
2954
+ },
2955
+ "mediaPoster": {
2956
+ "type": "string",
2957
+ "title": "Background poster image",
2958
+ "description": "Still image URL shown while a video backdrop is decoding its first frame. Auto-filled from Pexels' thumbnail when fillPexelsUrls sets a video mediaUrl. Hides the gradient flash that would otherwise appear in the ~50\u2013400ms gap between a <video> mounting and decoding its first frame.",
2959
+ "format": "uri",
2960
+ "default": ""
2961
+ },
2962
+ "mediaPosition": {
2963
+ "type": "string",
2964
+ "title": "Background focal position",
2965
+ "description": "Controls which part of a photo or video stays visible when cover-cropped. Pick the subject's side or vertical anchor after inspecting the frame.",
2966
+ "enum": [
2967
+ "center",
2968
+ "top",
2969
+ "bottom",
2970
+ "left",
2971
+ "right"
2972
+ ],
2973
+ "default": "center"
2974
+ },
2975
+ "mediaTreatment": {
2976
+ "type": "string",
2977
+ "title": "Background contrast treatment",
2978
+ "description": "subtle preserves a visual hero; cinematic adds balanced contrast; text-safe adds a stronger wash for copy-heavy scenes.",
2979
+ "enum": [
2980
+ "subtle",
2981
+ "cinematic",
2982
+ "text-safe"
2983
+ ],
2984
+ "default": "cinematic"
2985
+ }
2986
+ },
2987
+ "required": [
2988
+ "problemText",
2989
+ "solutionText"
2990
+ ],
2991
+ "additionalProperties": false,
2992
+ "x-vanillasky": {
2993
+ "allowsStockMedia": true
2994
+ }
2995
+ },
2996
+ "minDuration": 3,
2997
+ "preferredDuration": 5.5,
2998
+ "timing": {
2999
+ "contentFields": [
3000
+ "problemText",
3001
+ "solutionText"
3002
+ ],
3003
+ "contentUnit": "words"
3004
+ }
3005
+ },
3006
+ {
3007
+ "id": "cardList",
3008
+ "label": "Card list",
3009
+ "description": "Two or three parallel facts appear as stacked icon cards beneath a headline.",
3010
+ "family": "Explainers",
3011
+ "jobs": [
3012
+ "proof"
3013
+ ],
3014
+ "register": "card-led",
3015
+ "useWhen": "Two or three grounded features, benefits, use cases, integrations, or parallel facts need structure.",
3016
+ "avoidWhen": "The facts are sequential, fewer than two exist, or one idea deserves the whole scene.",
3017
+ "usesGlobalTextEffect": false,
3018
+ "usesGlobalTransition": true,
3019
+ "transitionTiming": {
3020
+ "entryReadyProgress": 0.2,
3021
+ "holdProgress": 0.7
3022
+ },
3023
+ "usesGlobalBackgroundEffect": true,
3024
+ "textCanvas": "tight",
3025
+ "schema": {
3026
+ "type": "object",
3027
+ "properties": {
3028
+ "texts": {
3029
+ "type": "string",
3030
+ "title": "Text",
3031
+ "description": "Title text",
3032
+ "default": "What you get."
3033
+ },
3034
+ "items": {
3035
+ "type": "array",
3036
+ "title": "Features",
3037
+ "description": "JSON array of 2 or 3 feature descriptions. Send only the facts the evidence supports \u2014 never pad the list to reach three. Items often contain commas, so send an array rather than comma-joined text.",
3038
+ "items": {
3039
+ "type": "string"
3040
+ },
3041
+ "minItems": 2,
3042
+ "maxItems": 3,
3043
+ "default": [
3044
+ "Automate your savings on energy bills",
3045
+ "Cleaner energy without raising your bill",
3046
+ "Same account and service guaranteed"
3047
+ ]
3048
+ },
3049
+ "itemEmojis": {
3050
+ "type": "array",
3051
+ "title": "Feature emojis",
3052
+ "description": "JSON array of emojis, one per feature row (optional). Rows without an emoji fall back to \u2726.",
3053
+ "examples": [
3054
+ [
3055
+ "\u{1F4B0}",
3056
+ "\u26A1",
3057
+ "\u{1F512}"
3058
+ ]
3059
+ ],
3060
+ "items": {
3061
+ "type": "string"
3062
+ },
3063
+ "maxItems": 3,
3064
+ "default": []
3065
+ },
3066
+ "mediaUrl": {
3067
+ "type": "string",
3068
+ "title": "Background media",
3069
+ "description": "Optional photo or video URL behind this scene. When set, replaces the brand gradient.",
3070
+ "format": "uri",
3071
+ "default": ""
3072
+ },
3073
+ "mediaKeyword": {
3074
+ "type": "string",
3075
+ "title": "Background search keyword",
3076
+ "description": "2-4 word English term for Pexels stock-footage search (auto-fills mediaUrl).",
3077
+ "format": "stock-media-keyword",
3078
+ "default": ""
3079
+ },
3080
+ "mediaType": {
3081
+ "type": "string",
3082
+ "title": "Background media type",
3083
+ "description": "auto detects photo/video from URL. 'gradient' is a deliberate mode \u2014 atmospheric brand-color scene with no stock footage.",
3084
+ "enum": [
3085
+ "auto",
3086
+ "photo",
3087
+ "video",
3088
+ "gradient"
3089
+ ],
3090
+ "default": "auto"
3091
+ },
3092
+ "mediaPoster": {
3093
+ "type": "string",
3094
+ "title": "Background poster image",
3095
+ "description": "Still image URL shown while a video backdrop is decoding its first frame. Auto-filled from Pexels' thumbnail when fillPexelsUrls sets a video mediaUrl. Hides the gradient flash that would otherwise appear in the ~50\u2013400ms gap between a <video> mounting and decoding its first frame.",
3096
+ "format": "uri",
3097
+ "default": ""
3098
+ },
3099
+ "mediaPosition": {
3100
+ "type": "string",
3101
+ "title": "Background focal position",
3102
+ "description": "Controls which part of a photo or video stays visible when cover-cropped. Pick the subject's side or vertical anchor after inspecting the frame.",
3103
+ "enum": [
3104
+ "center",
3105
+ "top",
3106
+ "bottom",
3107
+ "left",
3108
+ "right"
3109
+ ],
3110
+ "default": "center"
3111
+ },
3112
+ "mediaTreatment": {
3113
+ "type": "string",
3114
+ "title": "Background contrast treatment",
3115
+ "description": "subtle preserves a visual hero; cinematic adds balanced contrast; text-safe adds a stronger wash for copy-heavy scenes.",
3116
+ "enum": [
3117
+ "subtle",
3118
+ "cinematic",
3119
+ "text-safe"
3120
+ ],
3121
+ "default": "cinematic"
3122
+ }
3123
+ },
3124
+ "required": [
3125
+ "texts",
3126
+ "items"
3127
+ ],
3128
+ "additionalProperties": false,
3129
+ "x-vanillasky": {
3130
+ "allowsStockMedia": true
3131
+ }
3132
+ },
3133
+ "minDuration": 2,
3134
+ "preferredDuration": 3.5,
3135
+ "timing": {
3136
+ "contentFields": [
3137
+ "items"
3138
+ ],
3139
+ "contentUnit": "items"
3140
+ }
3141
+ },
3142
+ {
3143
+ "id": "steps",
3144
+ "label": "Steps",
3145
+ "description": "Two or three ordered workflow steps reveal as a vertical sequence.",
3146
+ "family": "Explainers",
3147
+ "jobs": [
3148
+ "setup",
3149
+ "proof"
3150
+ ],
3151
+ "register": "card-led",
3152
+ "useWhen": "Two or three grounded actions form a real sequence, workflow, or how-it-works explanation.",
3153
+ "avoidWhen": "Order does not matter, fewer than two steps exist, or the content is a parallel feature list.",
3154
+ "usesGlobalTextEffect": false,
3155
+ "usesGlobalTransition": true,
3156
+ "transitionTiming": {
3157
+ "entryReadyProgress": 0.2,
3158
+ "holdProgress": 0.7
3159
+ },
3160
+ "usesGlobalBackgroundEffect": true,
3161
+ "textCanvas": "tight",
3162
+ "schema": {
3163
+ "type": "object",
3164
+ "properties": {
3165
+ "texts": {
3166
+ "type": "string",
3167
+ "title": "Text",
3168
+ "description": "Title text",
3169
+ "default": "How it works."
3170
+ },
3171
+ "steps": {
3172
+ "type": "array",
3173
+ "title": "Steps",
3174
+ "description": 'JSON array of 2 or 3 step labels. Send only the steps the evidence supports \u2014 never pad the sequence to reach three. Keep each label to 1-2 words and at most 18 characters. Send as an array so labels with internal punctuation render correctly. Example: ["Capture", "Detect", "Notify"]',
3175
+ "items": {
3176
+ "type": "string"
3177
+ },
3178
+ "minItems": 2,
3179
+ "maxItems": 3,
3180
+ "default": [
3181
+ "Describe",
3182
+ "Preview",
3183
+ "Export"
3184
+ ]
3185
+ },
3186
+ "stepEmojis": {
3187
+ "type": "array",
3188
+ "title": "Step emojis",
3189
+ "description": 'JSON array of emojis, one per step (optional). Leave empty for numbered circles. Example: ["\u270D\uFE0F", "\u{1F440}", "\u{1F680}"]',
3190
+ "examples": [
3191
+ [
3192
+ "\u270D\uFE0F",
3193
+ "\u{1F440}",
3194
+ "\u{1F680}"
3195
+ ]
3196
+ ],
3197
+ "items": {
3198
+ "type": "string"
3199
+ },
3200
+ "maxItems": 3,
3201
+ "default": []
3202
+ },
3203
+ "mediaUrl": {
3204
+ "type": "string",
3205
+ "title": "Background media",
3206
+ "description": "Optional photo or video URL behind this scene. When set, replaces the brand gradient.",
3207
+ "format": "uri",
3208
+ "default": ""
3209
+ },
3210
+ "mediaKeyword": {
3211
+ "type": "string",
3212
+ "title": "Background search keyword",
3213
+ "description": "2-4 word English term for Pexels stock-footage search (auto-fills mediaUrl).",
3214
+ "format": "stock-media-keyword",
3215
+ "default": ""
3216
+ },
3217
+ "mediaType": {
3218
+ "type": "string",
3219
+ "title": "Background media type",
3220
+ "description": "auto detects photo/video from URL. 'gradient' is a deliberate mode \u2014 atmospheric brand-color scene with no stock footage.",
3221
+ "enum": [
3222
+ "auto",
3223
+ "photo",
3224
+ "video",
3225
+ "gradient"
3226
+ ],
3227
+ "default": "auto"
3228
+ },
3229
+ "mediaPoster": {
3230
+ "type": "string",
3231
+ "title": "Background poster image",
3232
+ "description": "Still image URL shown while a video backdrop is decoding its first frame. Auto-filled from Pexels' thumbnail when fillPexelsUrls sets a video mediaUrl. Hides the gradient flash that would otherwise appear in the ~50\u2013400ms gap between a <video> mounting and decoding its first frame.",
3233
+ "format": "uri",
3234
+ "default": ""
3235
+ },
3236
+ "mediaPosition": {
3237
+ "type": "string",
3238
+ "title": "Background focal position",
3239
+ "description": "Controls which part of a photo or video stays visible when cover-cropped. Pick the subject's side or vertical anchor after inspecting the frame.",
3240
+ "enum": [
3241
+ "center",
3242
+ "top",
3243
+ "bottom",
3244
+ "left",
3245
+ "right"
3246
+ ],
3247
+ "default": "center"
3248
+ },
3249
+ "mediaTreatment": {
3250
+ "type": "string",
3251
+ "title": "Background contrast treatment",
3252
+ "description": "subtle preserves a visual hero; cinematic adds balanced contrast; text-safe adds a stronger wash for copy-heavy scenes.",
3253
+ "enum": [
3254
+ "subtle",
3255
+ "cinematic",
3256
+ "text-safe"
3257
+ ],
3258
+ "default": "cinematic"
3259
+ }
3260
+ },
3261
+ "required": [
3262
+ "texts",
3263
+ "steps"
3264
+ ],
3265
+ "additionalProperties": false,
3266
+ "x-vanillasky": {
3267
+ "allowsStockMedia": true
3268
+ }
3269
+ },
3270
+ "minDuration": 2,
3271
+ "preferredDuration": 3.5,
3272
+ "timing": {
3273
+ "contentFields": [
3274
+ "steps"
3275
+ ],
3276
+ "contentUnit": "items"
3277
+ }
3278
+ },
3279
+ {
3280
+ "id": "ctaLogo",
3281
+ "label": "Logo close",
3282
+ "description": "A minimal brand name, URL, and call to action close the video.",
3283
+ "family": "Calls to action",
3284
+ "jobs": [
3285
+ "ask"
3286
+ ],
3287
+ "register": "typography-led",
3288
+ "useWhen": "The video already carries enough visual energy and needs a restrained branded final action.",
3289
+ "avoidWhen": "No brand or next action is grounded, or the close needs a cinematic media-backed final beat.",
3290
+ "usesGlobalTextEffect": false,
3291
+ "usesGlobalTransition": true,
3292
+ "transitionTiming": {
3293
+ "entryReadyProgress": 0.2,
3294
+ "holdProgress": 0.7
3295
+ },
3296
+ "usesGlobalBackgroundEffect": false,
3297
+ "textCanvas": "open",
3298
+ "schema": {
3299
+ "type": "object",
3300
+ "properties": {
3301
+ "url": {
3302
+ "type": "string",
3303
+ "title": "URL",
3304
+ "description": "Optional address shown below the CTA.",
3305
+ "examples": [
3306
+ "yoursite.com"
3307
+ ],
3308
+ "default": ""
3309
+ },
3310
+ "cta": {
3311
+ "type": "string",
3312
+ "title": "CTA",
3313
+ "description": "Optional action line shown above the URL. Keep it to four words.",
3314
+ "default": ""
3315
+ },
3316
+ "mediaUrl": {
3317
+ "type": "string",
3318
+ "title": "Background media",
3319
+ "description": "Optional photo or video URL behind this scene. When set, replaces the brand gradient.",
3320
+ "format": "uri",
3321
+ "default": ""
3322
+ },
3323
+ "mediaKeyword": {
3324
+ "type": "string",
3325
+ "title": "Background search keyword",
3326
+ "description": "2-4 word English term for Pexels stock-footage search (auto-fills mediaUrl).",
3327
+ "default": ""
3328
+ },
3329
+ "mediaType": {
3330
+ "type": "string",
3331
+ "title": "Background media type",
3332
+ "description": "auto detects photo/video from URL. 'gradient' is a deliberate mode \u2014 atmospheric brand-color scene with no stock footage.",
3333
+ "enum": [
3334
+ "auto",
3335
+ "photo",
3336
+ "video",
3337
+ "gradient"
3338
+ ],
3339
+ "default": "auto"
3340
+ },
3341
+ "mediaPoster": {
3342
+ "type": "string",
3343
+ "title": "Background poster image",
3344
+ "description": "Still image URL shown while a video backdrop is decoding its first frame. Auto-filled from Pexels' thumbnail when fillPexelsUrls sets a video mediaUrl. Hides the gradient flash that would otherwise appear in the ~50\u2013400ms gap between a <video> mounting and decoding its first frame.",
3345
+ "format": "uri",
3346
+ "default": ""
3347
+ },
3348
+ "mediaPosition": {
3349
+ "type": "string",
3350
+ "title": "Background focal position",
3351
+ "description": "Controls which part of a photo or video stays visible when cover-cropped. Pick the subject's side or vertical anchor after inspecting the frame.",
3352
+ "enum": [
3353
+ "center",
3354
+ "top",
3355
+ "bottom",
3356
+ "left",
3357
+ "right"
3358
+ ],
3359
+ "default": "center"
3360
+ },
3361
+ "mediaTreatment": {
3362
+ "type": "string",
3363
+ "title": "Background contrast treatment",
3364
+ "description": "subtle preserves a visual hero; cinematic adds balanced contrast; text-safe adds a stronger wash for copy-heavy scenes.",
3365
+ "enum": [
3366
+ "subtle",
3367
+ "cinematic",
3368
+ "text-safe"
3369
+ ],
3370
+ "default": "cinematic"
3371
+ }
3372
+ },
3373
+ "additionalProperties": false,
3374
+ "x-vanillasky": {
3375
+ "requiredAnyOf": [
3376
+ [
3377
+ "cta",
3378
+ "url"
3379
+ ]
3380
+ ]
3381
+ }
3382
+ },
3383
+ "minDuration": 3,
3384
+ "preferredDuration": 3.5,
3385
+ "timing": {
3386
+ "contentFields": [
3387
+ "url",
3388
+ "cta"
3389
+ ],
3390
+ "contentUnit": "words"
3391
+ }
3392
+ },
3393
+ {
3394
+ "id": "ctaMedia",
3395
+ "label": "Media close",
3396
+ "description": "A cinematic media-backed headline, URL, and call to action close the video.",
3397
+ "family": "Calls to action",
3398
+ "jobs": [
3399
+ "ask"
3400
+ ],
3401
+ "register": "typography-led",
3402
+ "useWhen": "A launch or reveal benefits from one final visual beat behind a grounded next action.",
3403
+ "avoidWhen": "No media can be resolved, no next action is grounded, or a restrained logo close is enough.",
3404
+ "usesGlobalTextEffect": true,
3405
+ "usesGlobalTransition": true,
3406
+ "transitionTiming": {
3407
+ "entryReadyProgress": 0.2,
3408
+ "holdProgress": 0.7
3409
+ },
3410
+ "usesGlobalBackgroundEffect": false,
3411
+ "textCanvas": "open",
3412
+ "schema": {
3413
+ "type": "object",
3414
+ "properties": {
3415
+ "headline": {
3416
+ "type": "string",
3417
+ "title": "Headline",
3418
+ "description": "The big closing message. Two to seven words reads best.",
3419
+ "default": "Make every moment count"
3420
+ },
3421
+ "url": {
3422
+ "type": "string",
3423
+ "title": "URL",
3424
+ "description": "Optional address shown below the CTA.",
3425
+ "examples": [
3426
+ "www.yoursite.com"
3427
+ ],
3428
+ "default": ""
3429
+ },
3430
+ "cta": {
3431
+ "type": "string",
3432
+ "title": "CTA",
3433
+ "description": "Optional action line shown above the URL. Keep it to four words.",
3434
+ "default": ""
3435
+ },
3436
+ "mediaUrl": {
3437
+ "type": "string",
3438
+ "title": "Background media",
3439
+ "description": "Optional photo or video URL behind this scene. When set, replaces the brand gradient.",
3440
+ "format": "uri",
3441
+ "default": ""
3442
+ },
3443
+ "mediaKeyword": {
3444
+ "type": "string",
3445
+ "title": "Background search keyword",
3446
+ "description": "2-4 word English term for Pexels stock-footage search (auto-fills mediaUrl).",
3447
+ "format": "stock-media-keyword",
3448
+ "default": ""
3449
+ },
3450
+ "mediaType": {
3451
+ "type": "string",
3452
+ "title": "Background media type",
3453
+ "description": "auto detects photo/video from URL. 'gradient' is a deliberate mode \u2014 atmospheric brand-color scene with no stock footage.",
3454
+ "enum": [
3455
+ "auto",
3456
+ "photo",
3457
+ "video",
3458
+ "gradient"
3459
+ ],
3460
+ "default": "auto"
3461
+ },
3462
+ "mediaPoster": {
3463
+ "type": "string",
3464
+ "title": "Background poster image",
3465
+ "description": "Still image URL shown while a video backdrop is decoding its first frame. Auto-filled from Pexels' thumbnail when fillPexelsUrls sets a video mediaUrl. Hides the gradient flash that would otherwise appear in the ~50\u2013400ms gap between a <video> mounting and decoding its first frame.",
3466
+ "format": "uri",
3467
+ "default": ""
3468
+ },
3469
+ "mediaPosition": {
3470
+ "type": "string",
3471
+ "title": "Background focal position",
3472
+ "description": "Controls which part of a photo or video stays visible when cover-cropped. Pick the subject's side or vertical anchor after inspecting the frame.",
3473
+ "enum": [
3474
+ "center",
3475
+ "top",
3476
+ "bottom",
3477
+ "left",
3478
+ "right"
3479
+ ],
3480
+ "default": "center"
3481
+ },
3482
+ "mediaTreatment": {
3483
+ "type": "string",
3484
+ "title": "Background contrast treatment",
3485
+ "description": "subtle preserves a visual hero; cinematic adds balanced contrast; text-safe adds a stronger wash for copy-heavy scenes.",
3486
+ "enum": [
3487
+ "subtle",
3488
+ "cinematic",
3489
+ "text-safe"
3490
+ ],
3491
+ "default": "cinematic"
3492
+ }
3493
+ },
3494
+ "required": [
3495
+ "headline"
3496
+ ],
3497
+ "additionalProperties": false,
3498
+ "x-vanillasky": {
3499
+ "allowsStockMedia": true,
3500
+ "requiredAnyOf": [
3501
+ [
3502
+ "cta",
3503
+ "url"
3504
+ ],
3505
+ [
3506
+ "mediaUrl"
3507
+ ]
3508
+ ]
3509
+ }
3510
+ },
3511
+ "minDuration": 3,
3512
+ "preferredDuration": 4,
3513
+ "timing": {
3514
+ "contentFields": [
3515
+ "headline",
3516
+ "url",
3517
+ "cta"
3518
+ ],
3519
+ "contentUnit": "words"
3520
+ }
3521
+ }
3522
+ ];
3523
+
3524
+ // src/visual-system/catalog/builtin-metadata.ts
3525
+ function freezeValue(value) {
3526
+ if (value && typeof value === "object" && !Object.isFrozen(value)) {
3527
+ for (const nested of Object.values(value)) freezeValue(nested);
3528
+ Object.freeze(value);
3529
+ }
3530
+ return value;
3531
+ }
3532
+ var BUILTIN_TEMPLATE_CATALOG = freezeValue(
3533
+ [...GENERATED_BUILTIN_TEMPLATE_CATALOG]
3534
+ );
3535
+ var metadataById = new Map(BUILTIN_TEMPLATE_CATALOG.map((template) => [template.id, template]));
3536
+ var BUILTIN_TEMPLATE_CAPABILITIES = freezeValue({
3537
+ templates: BUILTIN_TEMPLATE_CATALOG.map(({ id }) => id).sort()
3538
+ });
3539
+ function listBuiltinTemplateMetadata() {
3540
+ return BUILTIN_TEMPLATE_CATALOG;
3541
+ }
3542
+ function getBuiltinTemplateMetadata(id) {
3543
+ return metadataById.get(id);
3544
+ }
3545
+
3546
+ export {
3547
+ BUILTIN_TEMPLATE_CATALOG,
3548
+ BUILTIN_TEMPLATE_CAPABILITIES,
3549
+ listBuiltinTemplateMetadata,
3550
+ getBuiltinTemplateMetadata
3551
+ };