stillmade-sdk 0.1.3 → 0.1.5

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 (104) hide show
  1. package/.claude/settings.json +15 -0
  2. package/.cursor/hooks.json +11 -0
  3. package/.gitignore +2 -0
  4. package/.stillmade/sdk-baseline.json +65 -0
  5. package/AGENTS.md +29 -7
  6. package/AGENT_CONTROL.md +2 -1
  7. package/BROWSER_APPLICATIONS.md +128 -3
  8. package/GITHUB_APPLICATIONS.md +184 -1
  9. package/NATIVE_APPLICATION_ADAPTATION.md +565 -0
  10. package/NATIVE_MCP.md +3 -2
  11. package/PASTE_TO_LLM.md +29 -10
  12. package/PORTABLE.md +5 -4
  13. package/REMIX.md +16 -10
  14. package/SDK.md +511 -46
  15. package/UI_AESTHETIC.md +37 -6
  16. package/api-index.json +90 -0
  17. package/blocks/connected-app/versions/{1.2.0 → 1.5.0}/task-selection.js +5 -3
  18. package/blocks/connected-app/versions/{1.4.0 → 1.5.0}/view.js +4 -2
  19. package/blocks/native/sm.ai-decide/versions/1.0.0/package.js +149 -0
  20. package/blocks/native/sm.ai-write/versions/1.0.0/package.js +147 -0
  21. package/blocks/native/sm.connect-app/versions/1.0.0/package.js +55 -0
  22. package/blocks/native/sm.contact-fields/versions/1.0.0/package.js +197 -0
  23. package/blocks/native/sm.continue-if/versions/1.0.0/package.js +168 -0
  24. package/blocks/native/sm.generate-image/versions/2.1.0/package.js +101 -0
  25. package/blocks/native/sm.meta-creative/versions/1.0.0/package.js +206 -0
  26. package/blocks/native/sm.new-items/versions/1.0.0/package.js +191 -0
  27. package/blocks/native/sm.voiceover/versions/2.1.5/package.js +179 -0
  28. package/blocks/native/sm.voiceover/versions/2.1.6/package.js +179 -0
  29. package/blocks/native/sm.when/versions/1.0.0/package.js +129 -0
  30. package/blocks/native/sm.write-summary/versions/1.0.0/package.js +133 -0
  31. package/blocks/occasion-story/package.js +1 -1
  32. package/blocks/occasion-story/versions/1.0.2/package.js +1682 -0
  33. package/blocks/photo-reel-composer/versions/1.0.1/package.js +3026 -0
  34. package/blocks/release-visuals/package.js +1 -1
  35. package/blocks/release-visuals/versions/1.0.2/package.js +1500 -0
  36. package/blocks/short-form-finish/package.js +1 -1
  37. package/blocks/short-form-finish/versions/1.0.3/package.js +1026 -0
  38. package/examples/original-photo-editor.stillmade-block +0 -0
  39. package/examples/portable-regexp.stillmade-block +0 -0
  40. package/package.json +1 -1
  41. package/packages/block-cli/cli.js +16 -1
  42. package/packages/block-cli/format.js +2 -0
  43. package/packages/block-cli/ready-gate.js +164 -0
  44. package/packages/block-platform/block-readiness.js +3 -2
  45. package/packages/block-platform/canvas.js +1 -1
  46. package/packages/block-platform/catalog.js +6 -4
  47. package/packages/block-platform/chat-requirements.js +8 -3
  48. package/packages/block-platform/collaboration.js +5 -5
  49. package/packages/block-platform/first-revenue-project-types.js +2 -2
  50. package/packages/block-platform/flow-inspection.js +2 -2
  51. package/packages/block-platform/native-application-validation-context.js +20 -0
  52. package/packages/block-platform/plain-descriptions.js +67 -0
  53. package/packages/block-platform/product-ad-variants-project-type.js +6 -3
  54. package/packages/block-platform/project-context.js +4 -4
  55. package/packages/block-platform/project-type-composer.js +21 -12
  56. package/packages/block-platform/project-type-draft.js +2 -2
  57. package/packages/block-platform/project-type-recommendations.js +1 -1
  58. package/packages/block-platform/project-types.js +9 -4
  59. package/packages/block-platform/revenue-review-project-types.js +6 -5
  60. package/packages/block-platform/segment-runner.js +35 -11
  61. package/packages/block-platform/step-groups.js +78 -0
  62. package/packages/block-platform/steps.js +12 -8
  63. package/packages/block-platform/type-workspace.js +4 -4
  64. package/packages/block-platform/workflow-loop.js +4 -0
  65. package/packages/block-platform/workspace-input-readset.js +18 -4
  66. package/packages/block-sdk/admission.js +5 -17
  67. package/packages/block-sdk/agent-inputs.js +2 -0
  68. package/packages/block-sdk/agent-inspection.js +4 -2
  69. package/packages/block-sdk/browser-application-artifacts.js +1 -1
  70. package/packages/block-sdk/browser-application-execution.d.ts +2 -2
  71. package/packages/block-sdk/browser-application-execution.js +30 -12
  72. package/packages/block-sdk/browser-application-guest.d.ts +2 -0
  73. package/packages/block-sdk/browser-application-guest.js +16 -9
  74. package/packages/block-sdk/browser-application.d.ts +12 -4
  75. package/packages/block-sdk/browser-application.js +27 -13
  76. package/packages/block-sdk/builder-contract.js +25 -17
  77. package/packages/block-sdk/capabilities.js +38 -19
  78. package/packages/block-sdk/connected-app.js +26 -6
  79. package/packages/block-sdk/connection-adapters.js +19 -2
  80. package/packages/block-sdk/contracts-core.js +8 -0
  81. package/packages/block-sdk/contracts.js +9 -5
  82. package/packages/block-sdk/document-drafts.js +7 -1
  83. package/packages/block-sdk/document-store-binding.js +2 -1
  84. package/packages/block-sdk/file-bundle.js +4 -1
  85. package/packages/block-sdk/first-party-packages.js +18 -2
  86. package/packages/block-sdk/generation-catalog.js +3 -2
  87. package/packages/block-sdk/index.d.ts +20 -1
  88. package/packages/block-sdk/index.js +1 -0
  89. package/packages/block-sdk/native-application-package.d.ts +22 -0
  90. package/packages/block-sdk/native-application-package.js +77 -0
  91. package/packages/block-sdk/native_adapter.py +76 -0
  92. package/packages/block-sdk/package.json +1 -0
  93. package/packages/block-sdk/pipeline-runtime-host.d.ts +2 -0
  94. package/packages/block-sdk/project-flow-contract.js +2 -0
  95. package/packages/block-sdk/remix-policy.js +3 -1
  96. package/packages/block-sdk/resolver-admission.js +17 -0
  97. package/packages/block-sdk/runtime.js +4 -1
  98. package/packages/block-sdk/schema-fields.js +34 -2
  99. package/packages/block-sdk/semantic-relationships.js +14 -0
  100. package/packages/block-sdk/shared-controls.js +3 -1
  101. package/packages/block-sdk/shared-view.js +6 -1
  102. package/packages/block-sdk/source-rights.js +6 -5
  103. package/packages/block-sdk/testing.js +3 -0
  104. package/packages/design-system/theme.css +12 -3
@@ -0,0 +1,3026 @@
1
+ // Generated from the owned 1.0.1 manifest, runtime and fixtures.
2
+ export default {
3
+ "manifest": {
4
+ "schemaVersion": 1,
5
+ "sdkVersion": "0.1.0",
6
+ "id": "stillmade.photo-reel-composer",
7
+ "version": "1.0.1",
8
+ "name": "Photo Reel Composer",
9
+ "kind": "task",
10
+ "runtime": "javascript",
11
+ "entry": "src/run.js",
12
+ "license": "MIT",
13
+ "description": "Arrange original property or product photos and approved copy into a restrained, editable 20–30 second reel. No generated imagery or model calls.",
14
+ "inputs": {
15
+ "assets": {
16
+ "type": "asset[]",
17
+ "primary": true,
18
+ "default": [],
19
+ "description": "Ordered original photo references. Property: 6–8 photos. Product: 3–10. Exact versions and measured dimensions are required.",
20
+ "semantic": "legacy.asset-list.assets",
21
+ "required": true,
22
+ "sources": [
23
+ "upstream",
24
+ "user"
25
+ ]
26
+ },
27
+ "purpose": {
28
+ "type": "text",
29
+ "default": "property",
30
+ "description": "Property or product photo reel.",
31
+ "semantic": "stillmade.photo_reel.purpose",
32
+ "required": false,
33
+ "sources": [
34
+ "user",
35
+ "default"
36
+ ]
37
+ },
38
+ "title": {
39
+ "type": "text",
40
+ "default": "",
41
+ "description": "Approved title, up to 64 characters. Use only facts supplied and reviewed by the customer.",
42
+ "semantic": "stillmade.photo_reel.title",
43
+ "required": true,
44
+ "sources": [
45
+ "user",
46
+ "default"
47
+ ]
48
+ },
49
+ "captions": {
50
+ "type": "text",
51
+ "default": "",
52
+ "description": "One approved short caption per photo, one line each, up to 64 characters per line. Leave all empty for an uncaptioned sequence.",
53
+ "semantic": "stillmade.photo_reel.captions",
54
+ "required": false,
55
+ "sources": [
56
+ "user",
57
+ "default"
58
+ ]
59
+ },
60
+ "cta": {
61
+ "type": "text",
62
+ "default": "",
63
+ "description": "Approved call to action, up to 56 characters.",
64
+ "semantic": "stillmade.photo_reel.cta",
65
+ "required": true,
66
+ "sources": [
67
+ "user",
68
+ "default"
69
+ ]
70
+ },
71
+ "contact": {
72
+ "type": "text",
73
+ "default": "",
74
+ "description": "Exact contact or short destination to display, up to 64 characters.",
75
+ "semantic": "stillmade.photo_reel.contact",
76
+ "required": false,
77
+ "sources": [
78
+ "user",
79
+ "default"
80
+ ]
81
+ },
82
+ "demoDisclosure": {
83
+ "type": "text",
84
+ "default": "",
85
+ "description": "Optional exact disclosure shown throughout, such as Stock-photo demonstration · not a live listing.",
86
+ "semantic": "stillmade.photo_reel.demodisclosure",
87
+ "required": false,
88
+ "sources": [
89
+ "user",
90
+ "default"
91
+ ]
92
+ },
93
+ "duration": {
94
+ "type": "number",
95
+ "default": 24,
96
+ "min": 20,
97
+ "max": 30,
98
+ "description": "Total reel duration in seconds, including the four-second closing card.",
99
+ "semantic": "stillmade.photo_reel.duration",
100
+ "required": false,
101
+ "sources": [
102
+ "user",
103
+ "default"
104
+ ]
105
+ },
106
+ "aspect": {
107
+ "type": "text",
108
+ "default": "9:16",
109
+ "description": "Output frame shape; photos always retain their full original rectangle.",
110
+ "semantic": "stillmade.photo_reel.aspect",
111
+ "required": false,
112
+ "sources": [
113
+ "user",
114
+ "default"
115
+ ]
116
+ },
117
+ "logo": {
118
+ "type": "asset[]",
119
+ "default": [],
120
+ "description": "Optional single original logo image with exact version and measured dimensions.",
121
+ "semantic": "stillmade.photo_reel.logo",
122
+ "required": false,
123
+ "sources": [
124
+ "user",
125
+ "default"
126
+ ]
127
+ },
128
+ "music": {
129
+ "type": "asset[]",
130
+ "default": [],
131
+ "description": "Optional single licensed audio track with measured duration covering the reel. Otherwise output is silent.",
132
+ "semantic": "stillmade.photo_reel.music",
133
+ "required": false,
134
+ "sources": [
135
+ "user",
136
+ "default"
137
+ ]
138
+ }
139
+ },
140
+ "outputs": {
141
+ "timeline": {
142
+ "type": "timeline",
143
+ "primary": true,
144
+ "semantic": "legacy.timeline.updatededitor",
145
+ "description": "Editable complete Editor timeline containing original photo clips, approved text, optional music and logo."
146
+ },
147
+ "review": {
148
+ "type": "json",
149
+ "description": "Structural checks and remaining human review. Does not authorize export or certify claims or rights.",
150
+ "semantic": "stillmade.photo_reel.review",
151
+ "schema": {
152
+ "schemaVersion": "integer",
153
+ "state": "string",
154
+ "durationSec": "number",
155
+ "aspect": "string",
156
+ "photoCount": "integer",
157
+ "checks": [
158
+ {
159
+ "id": "string",
160
+ "passed": "boolean",
161
+ "note": "string"
162
+ }
163
+ ],
164
+ "warnings": "string[]",
165
+ "humanApproved": "boolean",
166
+ "exportAuthorized": "boolean",
167
+ "cost": {
168
+ "providerCalls": "integer",
169
+ "generationCredits": "integer",
170
+ "note": "string"
171
+ }
172
+ }
173
+ },
174
+ "provenance": {
175
+ "type": "json",
176
+ "description": "Versioned source references, supplied copy and bounded transformation record.",
177
+ "semantic": "stillmade.photo_reel.provenance",
178
+ "schema": {
179
+ "schemaVersion": "integer",
180
+ "kind": "string",
181
+ "block": {
182
+ "id": "string",
183
+ "version": "string"
184
+ },
185
+ "purpose": "string",
186
+ "sourcePhotos": [
187
+ {
188
+ "index": "integer",
189
+ "assetId": "string",
190
+ "versionId": "string",
191
+ "width": "number",
192
+ "height": "number"
193
+ }
194
+ ],
195
+ "logo": "json",
196
+ "music": "json",
197
+ "approvedCopy": {
198
+ "title": "string",
199
+ "captions": "string[]",
200
+ "cta": "string",
201
+ "contact": "string",
202
+ "demoDisclosure": "string"
203
+ },
204
+ "rights": {
205
+ "userConfirmed": "boolean",
206
+ "independentlyVerified": "boolean"
207
+ },
208
+ "transformations": "string[]",
209
+ "generativeMedia": "boolean",
210
+ "providerCalls": "integer"
211
+ }
212
+ }
213
+ },
214
+ "permissions": {
215
+ "project": [
216
+ "asset.read"
217
+ ],
218
+ "network": [],
219
+ "filesystem": [],
220
+ "secrets": []
221
+ },
222
+ "ui": [
223
+ {
224
+ "port": "purpose",
225
+ "control": "select",
226
+ "options": [
227
+ "property",
228
+ "product"
229
+ ]
230
+ },
231
+ {
232
+ "port": "aspect",
233
+ "control": "select",
234
+ "options": [
235
+ "9:16",
236
+ "16:9",
237
+ "1:1"
238
+ ]
239
+ },
240
+ {
241
+ "port": "duration",
242
+ "control": "slider",
243
+ "min": 20,
244
+ "max": 30,
245
+ "step": 1
246
+ }
247
+ ],
248
+ "platforms": {
249
+ "phone": {
250
+ "supported": true
251
+ },
252
+ "browser": {
253
+ "supported": true
254
+ },
255
+ "desktop": {
256
+ "supported": true
257
+ }
258
+ },
259
+ "provenance": {
260
+ "notice": "Original source (authorship declared by the packager)\nLicense: MIT\nChanges: Original implementation declared by the packager; no reused repository or dependency code.\nFiles: LICENSE, src/run.js\n\nMIT License\n\nCopyright (c) <year> <copyright holders>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and \nassociated documentation files (the \"Software\"), to deal in the Software without restriction, including \nwithout limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell \ncopies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the \nfollowing conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial \nportions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT \nLIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO \nEVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER \nIN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE \nUSE OR OTHER DEALINGS IN THE SOFTWARE.\n",
261
+ "portableSources": [
262
+ {
263
+ "kind": "original",
264
+ "license": "MIT",
265
+ "paths": [
266
+ "LICENSE",
267
+ "src/run.js"
268
+ ],
269
+ "evidence": [
270
+ "upstream/0/LICENSE",
271
+ "upstream/0/src/run.js"
272
+ ],
273
+ "changes": "Original implementation declared by the packager; no reused repository or dependency code."
274
+ }
275
+ ]
276
+ },
277
+ "semantics": [
278
+ {
279
+ "name": "stillmade.photo_reel.purpose",
280
+ "description": "Property or product photo reel.",
281
+ "parents": []
282
+ },
283
+ {
284
+ "name": "stillmade.photo_reel.title",
285
+ "description": "Approved title, up to 64 characters. Use only facts supplied and reviewed by the customer.",
286
+ "parents": []
287
+ },
288
+ {
289
+ "name": "stillmade.photo_reel.captions",
290
+ "description": "One approved short caption per photo, one line each, up to 64 characters per line. Leave all empty for an uncaptioned sequence.",
291
+ "parents": []
292
+ },
293
+ {
294
+ "name": "stillmade.photo_reel.cta",
295
+ "description": "Approved call to action, up to 56 characters.",
296
+ "parents": []
297
+ },
298
+ {
299
+ "name": "stillmade.photo_reel.contact",
300
+ "description": "Exact contact or short destination to display, up to 64 characters.",
301
+ "parents": []
302
+ },
303
+ {
304
+ "name": "stillmade.photo_reel.demodisclosure",
305
+ "description": "Optional exact disclosure shown throughout, such as Stock-photo demonstration · not a live listing.",
306
+ "parents": []
307
+ },
308
+ {
309
+ "name": "stillmade.photo_reel.duration",
310
+ "description": "Total reel duration in seconds, including the four-second closing card.",
311
+ "parents": []
312
+ },
313
+ {
314
+ "name": "stillmade.photo_reel.aspect",
315
+ "description": "Output frame shape; photos always retain their full original rectangle.",
316
+ "parents": []
317
+ },
318
+ {
319
+ "name": "stillmade.photo_reel.logo",
320
+ "description": "Optional single original logo image with exact version and measured dimensions.",
321
+ "parents": []
322
+ },
323
+ {
324
+ "name": "stillmade.photo_reel.music",
325
+ "description": "Optional single licensed audio track with measured duration covering the reel. Otherwise output is silent.",
326
+ "parents": []
327
+ },
328
+ {
329
+ "name": "stillmade.photo_reel.review",
330
+ "description": "Structural checks and remaining human review. Does not authorize export or certify claims or rights.",
331
+ "parents": []
332
+ },
333
+ {
334
+ "name": "stillmade.photo_reel.provenance",
335
+ "description": "Versioned source references, supplied copy and bounded transformation record.",
336
+ "parents": []
337
+ }
338
+ ]
339
+ },
340
+ "code": "// Photo Reel Composer 1.0.1. Runs only inside the isolated SDK JavaScript runtime.\n// Rights to supplied media are confirmed in the Content & IP Policy at sign-up.\n// References and supplied copy are data; this Block never reads media bytes,\n// calls a model, changes a photo, authorizes an export, or verifies legal rights.\nconst clone = value => JSON.parse(JSON.stringify(value));\nconst fail = (ok, message) => { if (!ok) throw new Error(message); };\nconst clean = (value, label, maximum, required = false) => {\n fail(value === undefined || typeof value === 'string', label + ' must be text.');\n const text = (value || '').trim();\n fail((!required || text.length > 0) && text.length <= maximum, label + ' must contain ' + (required ? '1' : '0') + ' to ' + maximum + ' characters.');\n fail(!/[\\u0000-\\u0008\\u000b\\u000c\\u000e-\\u001f]/.test(text), label + ' contains unsupported control characters.');\n fail(text.split(/\\s+/).every(word => word.length <= 28), label + ' contains a word or link too long to fit. Use a shorter display label.');\n return text;\n};\nconst purpose = input.purpose || 'property';\nfail(['property', 'product'].includes(purpose), 'Choose property or product.');\nconst assets = input.assets;\nconst minimum = purpose === 'property' ? 6 : 3;\nconst maximum = purpose === 'property' ? 8 : 10;\nfail(Array.isArray(assets) && assets.length >= minimum && assets.length <= maximum, 'Choose ' + minimum + ' to ' + maximum + ' original photos, in the order they should appear.');\nconst validMedia = (asset, kind, label) => {\n fail(asset && typeof asset === 'object' && !Array.isArray(asset) && asset.kind === kind, label + ' must be a ' + kind + ' reference.');\n fail(typeof asset.assetId === 'string' && asset.assetId.length > 0 && asset.assetId.length <= 240 && typeof asset.versionId === 'string' && asset.versionId.length > 0 && asset.versionId.length <= 240, label + ' needs its exact asset and version identities.');\n const mediaUrl = asset.url;\n const supportedUrl = typeof mediaUrl === 'string' && mediaUrl.length > 0 && mediaUrl.length <= 200000 && (\n /^\\/api\\/media\\/[A-Za-z0-9_-]+\\/[A-Za-z0-9_-][A-Za-z0-9._-]*$/.test(mediaUrl) ||\n /^(https:\\/\\/|http:\\/\\/(127\\.0\\.0\\.1|localhost)(:\\d+)?\\/|data:(image\\/(png|jpeg|webp)|audio\\/[a-z0-9.+-]+);base64,|blob:|sm-media:)/i.test(mediaUrl)\n ) && !/^https?:\\/\\/[^/?#]*@/i.test(mediaUrl);\n fail(supportedUrl, label + ' needs an authorized media reference.');\n if (kind === 'image') fail(Number.isFinite(asset.width) && asset.width > 0 && asset.width <= 40000 && Number.isFinite(asset.height) && asset.height > 0 && asset.height <= 40000, label + ' needs its measured original width and height. Upload this original photo again to record its dimensions.');\n return clone(asset);\n};\nconst photos = assets.map((asset, index) => validMedia(asset, 'image', 'Photo ' + (index + 1)));\nfail(new Set(photos.map(asset => asset.assetId + '\\n' + asset.versionId)).size === photos.length, 'Remove duplicate photo versions before composing.');\nconst title = clean(input.title, 'Approved title', 64, true);\nconst cta = clean(input.cta, 'Approved call to action', 56, true);\nconst contact = clean(input.contact, 'Contact or destination', 64);\nconst demoDisclosure = clean(input.demoDisclosure, 'Demo disclosure', 72);\nconst captionsText = input.captions || '';\nfail(typeof captionsText === 'string' && captionsText.length <= 1600, 'Use one short approved caption per photo.');\nconst captions = captionsText.trim() ? captionsText.replace(/\\r/g, '').split('\\n').map((line, index) => clean(line, 'Photo caption ' + (index + 1), 64)) : [];\nfail(captions.length === 0 || captions.length === photos.length, 'Supply exactly one caption line per photo, or leave captions empty. Blank lines keep a photo uncaptioned.');\n\nconst duration = input.duration === undefined ? 24 : input.duration;\nfail(Number.isFinite(duration) && duration >= 20 && duration <= 30, 'Choose a duration from 20 to 30 seconds.');\nconst aspect = input.aspect || '9:16';\nfail(['9:16', '16:9', '1:1'].includes(aspect), 'Choose 9:16, 16:9 or 1:1.');\nconst extra = (value, kind, label) => {\n fail(value === undefined || Array.isArray(value) && value.length <= 1, 'Choose at most one ' + label + '.');\n return value && value.length ? validMedia(value[0], kind, label) : null;\n};\nconst logo = extra(input.logo, 'image', 'logo');\nconst music = extra(input.music, 'audio', 'music track');\nif (music) fail(Number.isFinite(music.duration) && music.duration >= duration, 'The music track must have a measured duration covering the complete reel. Upload a supported MP3 or WAV again to record its duration, choose a longer track, or omit music.');\nconst [aw, ah] = aspect.split(':').map(Number);\nconst frameWidth = aw / ah * 369;\nconst portrait = aspect === '9:16';\nconst square = aspect === '1:1';\nconst backgroundColor = '#101714';\nconst foreground = '#F7F0DB';\nconst accent = '#CEB88B';\nconst overlays = [];\nconst clips = [];\nconst stickers = [];\nconst round = value => Math.round(value * 1000000) / 1000000;\nconst text = (id, value, start, end, y, size, color = foreground, width = .82) => {\n if (!value) return;\n // The shared renderer wraps between words only. Conservatively shrink a\n // single unusually long unbroken word; ordinary short-copy layouts stay put.\n const longest = Math.max(...value.split(/\\s+/).map(word => word.length));\n if (longest > 18) size = Math.min(size, (frameWidth * width - 12) / longest);\n overlays.push({ id, trackId: 'text', type: 'text', text: value, font: 'Hanken Grotesk', size, color, background: 'none', backgroundPadding: 0, width, x: 50, y, position: 'center', start: round(start), end: round(end), duration: round(end - start), animation: 'fade', animIn: .25, animOut: .2, rotation: 0 });\n};\nconst introEnd = 3.5;\nconst cardStart = duration - 4;\nconst sceneDuration = (cardStart - introEnd) / (photos.length - 1);\n// Fit the FULL measured photo inside a reserved rectangle, then add at most 3%\n// optical zoom. No crop, stretching, retouching, or synthetic pixels are requested.\nconst photoClip = (asset, index, start, length, endCard = false) => {\n const rawFit = Math.min(frameWidth / asset.width, 369 / asset.height);\n const fittedWidth = asset.width * rawFit;\n const fittedHeight = asset.height * rawFit;\n const regionWidth = frameWidth * (endCard ? .64 : .9);\n const regionHeight = 369 * (endCard ? .31 : portrait ? .48 : square ? .48 : .49);\n const scale = Math.min(regionWidth / fittedWidth, regionHeight / fittedHeight) / 1.03;\n const center = endCard ? .34 : .5;\n return {\n id: (endCard ? 'end-card-photo-' : 'photo-') + (index + 1), kind: 'image', trackId: 'video',\n asset: clone(asset), assetId: asset.assetId, versionId: asset.versionId, url: asset.url,\n title: asset.name || 'Photo ' + (index + 1), start: round(start), duration: round(length), trimStart: 0, speed: 1, volume: 0,\n transform: { fit: 'contain', scale, x: 0, y: (center - .5) * 369, rotation: 0, opacity: 1 },\n motion: { type: endCard ? 'none' : 'zoom', intensity: 100, zoomAmount: 1.03, zoomDirection: index % 2 ? 'out' : 'in', zoomAnchor: 'center', easing: 'ease-in-out', loop: false, ['window']: [0, 100] },\n transition: { type: endCard ? 'Cut' : 'Cross dissolve', duration: .3 },\n };\n};\nphotos.forEach((asset, index) => {\n const start = index === 0 ? 0 : introEnd + (index - 1) * sceneDuration;\n const end = index === 0 ? introEnd : index === photos.length - 1 ? cardStart : introEnd + index * sceneDuration;\n clips.push(photoClip(asset, index, start, end - start));\n text('photo-caption-' + (index + 1), captions[index], start + .2, end - .1, 79, portrait ? 11 : 14);\n text('photo-number-' + (index + 1), String(index + 1).padStart(2, '0') + ' / ' + String(photos.length).padStart(2, '0'), start, end - .01, 88, portrait ? 6.5 : 8, accent);\n});\nclips.push(photoClip(photos.at(-1), photos.length - 1, cardStart, 4, true));\n// The opening and closing compositions share an editorial rhythm while the\n// photographs remain full, unaltered, independently editable timeline items.\ntext('opening-title', title, 0, introEnd - .1, 18, portrait ? 16 : 22);\ntext('sequence-title', title, introEnd, cardStart, 16, portrait ? 10 : 14, foreground);\ntext('closing-title', title, cardStart, duration, 13, portrait ? 10 : 14, accent);\ntext('closing-cta', cta, cardStart + .1, duration, 65, portrait ? 17 : 23);\ntext('closing-contact', contact, cardStart + .25, duration, 79, portrait ? 9 : 12);\ntext('demo-disclosure', demoDisclosure, 0, duration, 94, portrait ? 6.5 : 8, '#D4D9D0', .84);\nstickers.push({ id: 'editorial-rule', trackId: 'overlay', shape: 'underline', color: accent, x: 50, y: 25, scale: .2, stretchX: 1.1, stretchY: .24, opacity: .85, rotation: 0, start: 0, end: cardStart });\nstickers.push({ id: 'closing-rule', trackId: 'overlay', shape: 'underline', color: accent, x: 50, y: 55, scale: .2, stretchX: 1.1, stretchY: .24, opacity: .85, rotation: 0, start: cardStart, end: duration });\nif (logo) {\n // Use an ordinary image clip on its own upper track. This avoids the GIF\n // sticker renderer's minimum-height distortion for unusually wide wordmarks.\n const fitted = Math.min(frameWidth / logo.width, 369 / logo.height);\n const scale = Math.min(frameWidth * .2 / (logo.width * fitted), 369 * .06 / (logo.height * fitted));\n clips.push({ id: 'brand-logo', kind: 'image', trackId: 'logo', asset: clone(logo), assetId: logo.assetId, versionId: logo.versionId, url: logo.url, start: cardStart, duration: 4, trimStart: 0, speed: 1, volume: 0, transform: { fit: 'contain', scale, x: 0, y: 369 * .39, rotation: 0, opacity: 1 }, motion: { type: 'none' }, transition: { type: 'Cut', duration: 0 } });\n}\nconst audioItems = music ? [{ id: 'music', trackId: 'audio', asset: clone(music), assetId: music.assetId, versionId: music.versionId, url: music.url, start: 0, duration, trimStart: 0, sourceOffset: 0, speed: 1, volume: 35, fadeIn: .5, fadeOut: .8 }] : [];\nconst provenance = {\n schemaVersion: 1, kind: 'photo-reel-source-record', block: { id: 'stillmade.photo-reel-composer', version: '1.0.0' }, purpose,\n sourcePhotos: photos.map((asset, index) => ({ index, assetId: asset.assetId, versionId: asset.versionId, width: asset.width, height: asset.height })),\n logo: logo ? { assetId: logo.assetId, versionId: logo.versionId } : null,\n music: music ? { assetId: music.assetId, versionId: music.versionId } : null,\n approvedCopy: { title, captions, cta, contact, demoDisclosure },\n rights: { userConfirmed: true, independentlyVerified: false },\n transformations: ['contain fit', 'uniform scale', 'at most three percent optical zoom'],\n generativeMedia: false, providerCalls: 0,\n};\nconst timeline = {\n schemaVersion: 1, activeTimelineId: 'photo-reel', name: title, aspect, aspectRatio: aspect, previewH: 369, backgroundColor,\n tracks: [{ id: 'video', type: 'video', label: 'Original photos' }, ...(logo ? [{ id: 'logo', type: 'video', label: 'Original logo' }] : []), { id: 'text', type: 'text', label: 'Approved copy' }, { id: 'overlay', type: 'overlay', label: 'Graphic details' }, { id: 'audio', type: 'audio', label: 'Music' }],\n trackOrder: ['video', 'logo', 'text', 'overlay', 'audio'], clips, audioItems, textOverlays: overlays, captions: [], stickers, effects: [],\n // The device renderer consumes these legacy aliases; Editor keeps the normal\n // editable lanes above. Both refer to the same content at creation time.\n audio: clone(audioItems), overlays: clone(overlays), targetDuration: duration,\n exportSettings: { aspect, fps: 30, resolution: '1080p', quality: 'high' },\n photoReel: clone(provenance),\n};\nconst lowResolution = photos.filter(asset => asset.width < 1080 || asset.height < 720).map(asset => asset.assetId);\nreturn {\n timeline,\n review: {\n schemaVersion: 1, state: 'needs-human-review', durationSec: duration, aspect, photoCount: photos.length,\n checks: [\n { id: 'original-source-versions', passed: true, note: 'Every photo retains its supplied asset/version and full source rectangle.' },\n { id: 'approved-copy-only', passed: true, note: 'No property features, prices, availability or product claims are generated.' },\n { id: 'bounded-local-composition', passed: true, note: 'No provider call or credit spend occurs in this Block.' },\n { id: 'complete-timing', passed: true, note: 'All source photos and a four-second closing card fit the requested duration.' },\n ],\n warnings: [\n 'Review the full export for photo quality, exact facts, readable copy, licensing and the displayed contact before delivering.',\n ...(!music ? ['Silent reel. Add rights-cleared music in this Block or the Editor if wanted.'] : []),\n ...(lowResolution.length ? ['Small source images may look soft in a 1080p export: ' + lowResolution.join(', ')] : []),\n ...(demoDisclosure ? ['This is labeled demonstration material and is not evidence of a real property or product offer.'] : []),\n ],\n humanApproved: false, exportAuthorized: false, cost: { providerCalls: 0, generationCredits: 0, note: 'Host storage, export access and any separately added services use their own policy.' },\n },\n provenance,\n};\n",
341
+ "tests": [
342
+ {
343
+ "name": "Compose exact original property photos and approved copy",
344
+ "input": {
345
+ "assets": [
346
+ {
347
+ "kind": "image",
348
+ "assetId": "photo-1",
349
+ "versionId": "original-v1",
350
+ "url": "https://media.example/photo-1.jpg",
351
+ "width": 1600,
352
+ "height": 1067,
353
+ "name": "Approved photo 1"
354
+ },
355
+ {
356
+ "kind": "image",
357
+ "assetId": "photo-2",
358
+ "versionId": "original-v1",
359
+ "url": "https://media.example/photo-2.jpg",
360
+ "width": 1600,
361
+ "height": 1067,
362
+ "name": "Approved photo 2"
363
+ },
364
+ {
365
+ "kind": "image",
366
+ "assetId": "photo-3",
367
+ "versionId": "original-v1",
368
+ "url": "https://media.example/photo-3.jpg",
369
+ "width": 1600,
370
+ "height": 1067,
371
+ "name": "Approved photo 3"
372
+ },
373
+ {
374
+ "kind": "image",
375
+ "assetId": "photo-4",
376
+ "versionId": "original-v1",
377
+ "url": "https://media.example/photo-4.jpg",
378
+ "width": 1600,
379
+ "height": 1067,
380
+ "name": "Approved photo 4"
381
+ },
382
+ {
383
+ "kind": "image",
384
+ "assetId": "photo-5",
385
+ "versionId": "original-v1",
386
+ "url": "https://media.example/photo-5.jpg",
387
+ "width": 1600,
388
+ "height": 1067,
389
+ "name": "Approved photo 5"
390
+ },
391
+ {
392
+ "kind": "image",
393
+ "assetId": "photo-6",
394
+ "versionId": "original-v1",
395
+ "url": "https://media.example/photo-6.jpg",
396
+ "width": 1600,
397
+ "height": 1067,
398
+ "name": "Approved photo 6"
399
+ }
400
+ ],
401
+ "purpose": "property",
402
+ "title": "An interior photo collection",
403
+ "captions": "Entry\nLiving area\nDining area\nKitchen\nBedroom\nDetail",
404
+ "cta": "Ask for the details",
405
+ "contact": "Example contact",
406
+ "demoDisclosure": "Sample only · not a live listing",
407
+ "duration": 24,
408
+ "aspect": "9:16",
409
+ "logo": [],
410
+ "music": []
411
+ },
412
+ "expected": {
413
+ "timeline": {
414
+ "schemaVersion": 1,
415
+ "activeTimelineId": "photo-reel",
416
+ "name": "An interior photo collection",
417
+ "aspect": "9:16",
418
+ "aspectRatio": "9:16",
419
+ "previewH": 369,
420
+ "backgroundColor": "#101714",
421
+ "tracks": [
422
+ {
423
+ "id": "video",
424
+ "type": "video",
425
+ "label": "Original photos"
426
+ },
427
+ {
428
+ "id": "text",
429
+ "type": "text",
430
+ "label": "Approved copy"
431
+ },
432
+ {
433
+ "id": "overlay",
434
+ "type": "overlay",
435
+ "label": "Graphic details"
436
+ },
437
+ {
438
+ "id": "audio",
439
+ "type": "audio",
440
+ "label": "Music"
441
+ }
442
+ ],
443
+ "trackOrder": [
444
+ "video",
445
+ "logo",
446
+ "text",
447
+ "overlay",
448
+ "audio"
449
+ ],
450
+ "clips": [
451
+ {
452
+ "id": "photo-1",
453
+ "kind": "image",
454
+ "trackId": "video",
455
+ "asset": {
456
+ "kind": "image",
457
+ "assetId": "photo-1",
458
+ "versionId": "original-v1",
459
+ "url": "https://media.example/photo-1.jpg",
460
+ "width": 1600,
461
+ "height": 1067,
462
+ "name": "Approved photo 1"
463
+ },
464
+ "assetId": "photo-1",
465
+ "versionId": "original-v1",
466
+ "url": "https://media.example/photo-1.jpg",
467
+ "title": "Approved photo 1",
468
+ "start": 0,
469
+ "duration": 3.5,
470
+ "trimStart": 0,
471
+ "speed": 1,
472
+ "volume": 0,
473
+ "transform": {
474
+ "fit": "contain",
475
+ "scale": 0.8737864077669902,
476
+ "x": 0,
477
+ "y": 0,
478
+ "rotation": 0,
479
+ "opacity": 1
480
+ },
481
+ "motion": {
482
+ "type": "zoom",
483
+ "intensity": 100,
484
+ "zoomAmount": 1.03,
485
+ "zoomDirection": "in",
486
+ "zoomAnchor": "center",
487
+ "easing": "ease-in-out",
488
+ "loop": false,
489
+ "window": [
490
+ 0,
491
+ 100
492
+ ]
493
+ },
494
+ "transition": {
495
+ "type": "Cross dissolve",
496
+ "duration": 0.3
497
+ }
498
+ },
499
+ {
500
+ "id": "photo-2",
501
+ "kind": "image",
502
+ "trackId": "video",
503
+ "asset": {
504
+ "kind": "image",
505
+ "assetId": "photo-2",
506
+ "versionId": "original-v1",
507
+ "url": "https://media.example/photo-2.jpg",
508
+ "width": 1600,
509
+ "height": 1067,
510
+ "name": "Approved photo 2"
511
+ },
512
+ "assetId": "photo-2",
513
+ "versionId": "original-v1",
514
+ "url": "https://media.example/photo-2.jpg",
515
+ "title": "Approved photo 2",
516
+ "start": 3.5,
517
+ "duration": 3.3,
518
+ "trimStart": 0,
519
+ "speed": 1,
520
+ "volume": 0,
521
+ "transform": {
522
+ "fit": "contain",
523
+ "scale": 0.8737864077669902,
524
+ "x": 0,
525
+ "y": 0,
526
+ "rotation": 0,
527
+ "opacity": 1
528
+ },
529
+ "motion": {
530
+ "type": "zoom",
531
+ "intensity": 100,
532
+ "zoomAmount": 1.03,
533
+ "zoomDirection": "out",
534
+ "zoomAnchor": "center",
535
+ "easing": "ease-in-out",
536
+ "loop": false,
537
+ "window": [
538
+ 0,
539
+ 100
540
+ ]
541
+ },
542
+ "transition": {
543
+ "type": "Cross dissolve",
544
+ "duration": 0.3
545
+ }
546
+ },
547
+ {
548
+ "id": "photo-3",
549
+ "kind": "image",
550
+ "trackId": "video",
551
+ "asset": {
552
+ "kind": "image",
553
+ "assetId": "photo-3",
554
+ "versionId": "original-v1",
555
+ "url": "https://media.example/photo-3.jpg",
556
+ "width": 1600,
557
+ "height": 1067,
558
+ "name": "Approved photo 3"
559
+ },
560
+ "assetId": "photo-3",
561
+ "versionId": "original-v1",
562
+ "url": "https://media.example/photo-3.jpg",
563
+ "title": "Approved photo 3",
564
+ "start": 6.8,
565
+ "duration": 3.3,
566
+ "trimStart": 0,
567
+ "speed": 1,
568
+ "volume": 0,
569
+ "transform": {
570
+ "fit": "contain",
571
+ "scale": 0.8737864077669902,
572
+ "x": 0,
573
+ "y": 0,
574
+ "rotation": 0,
575
+ "opacity": 1
576
+ },
577
+ "motion": {
578
+ "type": "zoom",
579
+ "intensity": 100,
580
+ "zoomAmount": 1.03,
581
+ "zoomDirection": "in",
582
+ "zoomAnchor": "center",
583
+ "easing": "ease-in-out",
584
+ "loop": false,
585
+ "window": [
586
+ 0,
587
+ 100
588
+ ]
589
+ },
590
+ "transition": {
591
+ "type": "Cross dissolve",
592
+ "duration": 0.3
593
+ }
594
+ },
595
+ {
596
+ "id": "photo-4",
597
+ "kind": "image",
598
+ "trackId": "video",
599
+ "asset": {
600
+ "kind": "image",
601
+ "assetId": "photo-4",
602
+ "versionId": "original-v1",
603
+ "url": "https://media.example/photo-4.jpg",
604
+ "width": 1600,
605
+ "height": 1067,
606
+ "name": "Approved photo 4"
607
+ },
608
+ "assetId": "photo-4",
609
+ "versionId": "original-v1",
610
+ "url": "https://media.example/photo-4.jpg",
611
+ "title": "Approved photo 4",
612
+ "start": 10.1,
613
+ "duration": 3.3,
614
+ "trimStart": 0,
615
+ "speed": 1,
616
+ "volume": 0,
617
+ "transform": {
618
+ "fit": "contain",
619
+ "scale": 0.8737864077669902,
620
+ "x": 0,
621
+ "y": 0,
622
+ "rotation": 0,
623
+ "opacity": 1
624
+ },
625
+ "motion": {
626
+ "type": "zoom",
627
+ "intensity": 100,
628
+ "zoomAmount": 1.03,
629
+ "zoomDirection": "out",
630
+ "zoomAnchor": "center",
631
+ "easing": "ease-in-out",
632
+ "loop": false,
633
+ "window": [
634
+ 0,
635
+ 100
636
+ ]
637
+ },
638
+ "transition": {
639
+ "type": "Cross dissolve",
640
+ "duration": 0.3
641
+ }
642
+ },
643
+ {
644
+ "id": "photo-5",
645
+ "kind": "image",
646
+ "trackId": "video",
647
+ "asset": {
648
+ "kind": "image",
649
+ "assetId": "photo-5",
650
+ "versionId": "original-v1",
651
+ "url": "https://media.example/photo-5.jpg",
652
+ "width": 1600,
653
+ "height": 1067,
654
+ "name": "Approved photo 5"
655
+ },
656
+ "assetId": "photo-5",
657
+ "versionId": "original-v1",
658
+ "url": "https://media.example/photo-5.jpg",
659
+ "title": "Approved photo 5",
660
+ "start": 13.4,
661
+ "duration": 3.3,
662
+ "trimStart": 0,
663
+ "speed": 1,
664
+ "volume": 0,
665
+ "transform": {
666
+ "fit": "contain",
667
+ "scale": 0.8737864077669902,
668
+ "x": 0,
669
+ "y": 0,
670
+ "rotation": 0,
671
+ "opacity": 1
672
+ },
673
+ "motion": {
674
+ "type": "zoom",
675
+ "intensity": 100,
676
+ "zoomAmount": 1.03,
677
+ "zoomDirection": "in",
678
+ "zoomAnchor": "center",
679
+ "easing": "ease-in-out",
680
+ "loop": false,
681
+ "window": [
682
+ 0,
683
+ 100
684
+ ]
685
+ },
686
+ "transition": {
687
+ "type": "Cross dissolve",
688
+ "duration": 0.3
689
+ }
690
+ },
691
+ {
692
+ "id": "photo-6",
693
+ "kind": "image",
694
+ "trackId": "video",
695
+ "asset": {
696
+ "kind": "image",
697
+ "assetId": "photo-6",
698
+ "versionId": "original-v1",
699
+ "url": "https://media.example/photo-6.jpg",
700
+ "width": 1600,
701
+ "height": 1067,
702
+ "name": "Approved photo 6"
703
+ },
704
+ "assetId": "photo-6",
705
+ "versionId": "original-v1",
706
+ "url": "https://media.example/photo-6.jpg",
707
+ "title": "Approved photo 6",
708
+ "start": 16.7,
709
+ "duration": 3.3,
710
+ "trimStart": 0,
711
+ "speed": 1,
712
+ "volume": 0,
713
+ "transform": {
714
+ "fit": "contain",
715
+ "scale": 0.8737864077669902,
716
+ "x": 0,
717
+ "y": 0,
718
+ "rotation": 0,
719
+ "opacity": 1
720
+ },
721
+ "motion": {
722
+ "type": "zoom",
723
+ "intensity": 100,
724
+ "zoomAmount": 1.03,
725
+ "zoomDirection": "out",
726
+ "zoomAnchor": "center",
727
+ "easing": "ease-in-out",
728
+ "loop": false,
729
+ "window": [
730
+ 0,
731
+ 100
732
+ ]
733
+ },
734
+ "transition": {
735
+ "type": "Cross dissolve",
736
+ "duration": 0.3
737
+ }
738
+ },
739
+ {
740
+ "id": "end-card-photo-6",
741
+ "kind": "image",
742
+ "trackId": "video",
743
+ "asset": {
744
+ "kind": "image",
745
+ "assetId": "photo-6",
746
+ "versionId": "original-v1",
747
+ "url": "https://media.example/photo-6.jpg",
748
+ "width": 1600,
749
+ "height": 1067,
750
+ "name": "Approved photo 6"
751
+ },
752
+ "assetId": "photo-6",
753
+ "versionId": "original-v1",
754
+ "url": "https://media.example/photo-6.jpg",
755
+ "title": "Approved photo 6",
756
+ "start": 20,
757
+ "duration": 4,
758
+ "trimStart": 0,
759
+ "speed": 1,
760
+ "volume": 0,
761
+ "transform": {
762
+ "fit": "contain",
763
+ "scale": 0.6213592233009708,
764
+ "x": 0,
765
+ "y": -59.03999999999999,
766
+ "rotation": 0,
767
+ "opacity": 1
768
+ },
769
+ "motion": {
770
+ "type": "none",
771
+ "intensity": 100,
772
+ "zoomAmount": 1.03,
773
+ "zoomDirection": "out",
774
+ "zoomAnchor": "center",
775
+ "easing": "ease-in-out",
776
+ "loop": false,
777
+ "window": [
778
+ 0,
779
+ 100
780
+ ]
781
+ },
782
+ "transition": {
783
+ "type": "Cut",
784
+ "duration": 0.3
785
+ }
786
+ }
787
+ ],
788
+ "audioItems": [],
789
+ "textOverlays": [
790
+ {
791
+ "id": "photo-caption-1",
792
+ "trackId": "text",
793
+ "type": "text",
794
+ "text": "Entry",
795
+ "font": "Hanken Grotesk",
796
+ "size": 11,
797
+ "color": "#F7F0DB",
798
+ "background": "none",
799
+ "backgroundPadding": 0,
800
+ "width": 0.82,
801
+ "x": 50,
802
+ "y": 79,
803
+ "position": "center",
804
+ "start": 0.2,
805
+ "end": 3.4,
806
+ "duration": 3.2,
807
+ "animation": "fade",
808
+ "animIn": 0.25,
809
+ "animOut": 0.2,
810
+ "rotation": 0
811
+ },
812
+ {
813
+ "id": "photo-number-1",
814
+ "trackId": "text",
815
+ "type": "text",
816
+ "text": "01 / 06",
817
+ "font": "Hanken Grotesk",
818
+ "size": 6.5,
819
+ "color": "#CEB88B",
820
+ "background": "none",
821
+ "backgroundPadding": 0,
822
+ "width": 0.82,
823
+ "x": 50,
824
+ "y": 88,
825
+ "position": "center",
826
+ "start": 0,
827
+ "end": 3.49,
828
+ "duration": 3.49,
829
+ "animation": "fade",
830
+ "animIn": 0.25,
831
+ "animOut": 0.2,
832
+ "rotation": 0
833
+ },
834
+ {
835
+ "id": "photo-caption-2",
836
+ "trackId": "text",
837
+ "type": "text",
838
+ "text": "Living area",
839
+ "font": "Hanken Grotesk",
840
+ "size": 11,
841
+ "color": "#F7F0DB",
842
+ "background": "none",
843
+ "backgroundPadding": 0,
844
+ "width": 0.82,
845
+ "x": 50,
846
+ "y": 79,
847
+ "position": "center",
848
+ "start": 3.7,
849
+ "end": 6.7,
850
+ "duration": 3,
851
+ "animation": "fade",
852
+ "animIn": 0.25,
853
+ "animOut": 0.2,
854
+ "rotation": 0
855
+ },
856
+ {
857
+ "id": "photo-number-2",
858
+ "trackId": "text",
859
+ "type": "text",
860
+ "text": "02 / 06",
861
+ "font": "Hanken Grotesk",
862
+ "size": 6.5,
863
+ "color": "#CEB88B",
864
+ "background": "none",
865
+ "backgroundPadding": 0,
866
+ "width": 0.82,
867
+ "x": 50,
868
+ "y": 88,
869
+ "position": "center",
870
+ "start": 3.5,
871
+ "end": 6.79,
872
+ "duration": 3.29,
873
+ "animation": "fade",
874
+ "animIn": 0.25,
875
+ "animOut": 0.2,
876
+ "rotation": 0
877
+ },
878
+ {
879
+ "id": "photo-caption-3",
880
+ "trackId": "text",
881
+ "type": "text",
882
+ "text": "Dining area",
883
+ "font": "Hanken Grotesk",
884
+ "size": 11,
885
+ "color": "#F7F0DB",
886
+ "background": "none",
887
+ "backgroundPadding": 0,
888
+ "width": 0.82,
889
+ "x": 50,
890
+ "y": 79,
891
+ "position": "center",
892
+ "start": 7,
893
+ "end": 10,
894
+ "duration": 3,
895
+ "animation": "fade",
896
+ "animIn": 0.25,
897
+ "animOut": 0.2,
898
+ "rotation": 0
899
+ },
900
+ {
901
+ "id": "photo-number-3",
902
+ "trackId": "text",
903
+ "type": "text",
904
+ "text": "03 / 06",
905
+ "font": "Hanken Grotesk",
906
+ "size": 6.5,
907
+ "color": "#CEB88B",
908
+ "background": "none",
909
+ "backgroundPadding": 0,
910
+ "width": 0.82,
911
+ "x": 50,
912
+ "y": 88,
913
+ "position": "center",
914
+ "start": 6.8,
915
+ "end": 10.09,
916
+ "duration": 3.29,
917
+ "animation": "fade",
918
+ "animIn": 0.25,
919
+ "animOut": 0.2,
920
+ "rotation": 0
921
+ },
922
+ {
923
+ "id": "photo-caption-4",
924
+ "trackId": "text",
925
+ "type": "text",
926
+ "text": "Kitchen",
927
+ "font": "Hanken Grotesk",
928
+ "size": 11,
929
+ "color": "#F7F0DB",
930
+ "background": "none",
931
+ "backgroundPadding": 0,
932
+ "width": 0.82,
933
+ "x": 50,
934
+ "y": 79,
935
+ "position": "center",
936
+ "start": 10.3,
937
+ "end": 13.3,
938
+ "duration": 3,
939
+ "animation": "fade",
940
+ "animIn": 0.25,
941
+ "animOut": 0.2,
942
+ "rotation": 0
943
+ },
944
+ {
945
+ "id": "photo-number-4",
946
+ "trackId": "text",
947
+ "type": "text",
948
+ "text": "04 / 06",
949
+ "font": "Hanken Grotesk",
950
+ "size": 6.5,
951
+ "color": "#CEB88B",
952
+ "background": "none",
953
+ "backgroundPadding": 0,
954
+ "width": 0.82,
955
+ "x": 50,
956
+ "y": 88,
957
+ "position": "center",
958
+ "start": 10.1,
959
+ "end": 13.39,
960
+ "duration": 3.29,
961
+ "animation": "fade",
962
+ "animIn": 0.25,
963
+ "animOut": 0.2,
964
+ "rotation": 0
965
+ },
966
+ {
967
+ "id": "photo-caption-5",
968
+ "trackId": "text",
969
+ "type": "text",
970
+ "text": "Bedroom",
971
+ "font": "Hanken Grotesk",
972
+ "size": 11,
973
+ "color": "#F7F0DB",
974
+ "background": "none",
975
+ "backgroundPadding": 0,
976
+ "width": 0.82,
977
+ "x": 50,
978
+ "y": 79,
979
+ "position": "center",
980
+ "start": 13.6,
981
+ "end": 16.6,
982
+ "duration": 3,
983
+ "animation": "fade",
984
+ "animIn": 0.25,
985
+ "animOut": 0.2,
986
+ "rotation": 0
987
+ },
988
+ {
989
+ "id": "photo-number-5",
990
+ "trackId": "text",
991
+ "type": "text",
992
+ "text": "05 / 06",
993
+ "font": "Hanken Grotesk",
994
+ "size": 6.5,
995
+ "color": "#CEB88B",
996
+ "background": "none",
997
+ "backgroundPadding": 0,
998
+ "width": 0.82,
999
+ "x": 50,
1000
+ "y": 88,
1001
+ "position": "center",
1002
+ "start": 13.4,
1003
+ "end": 16.69,
1004
+ "duration": 3.29,
1005
+ "animation": "fade",
1006
+ "animIn": 0.25,
1007
+ "animOut": 0.2,
1008
+ "rotation": 0
1009
+ },
1010
+ {
1011
+ "id": "photo-caption-6",
1012
+ "trackId": "text",
1013
+ "type": "text",
1014
+ "text": "Detail",
1015
+ "font": "Hanken Grotesk",
1016
+ "size": 11,
1017
+ "color": "#F7F0DB",
1018
+ "background": "none",
1019
+ "backgroundPadding": 0,
1020
+ "width": 0.82,
1021
+ "x": 50,
1022
+ "y": 79,
1023
+ "position": "center",
1024
+ "start": 16.9,
1025
+ "end": 19.9,
1026
+ "duration": 3,
1027
+ "animation": "fade",
1028
+ "animIn": 0.25,
1029
+ "animOut": 0.2,
1030
+ "rotation": 0
1031
+ },
1032
+ {
1033
+ "id": "photo-number-6",
1034
+ "trackId": "text",
1035
+ "type": "text",
1036
+ "text": "06 / 06",
1037
+ "font": "Hanken Grotesk",
1038
+ "size": 6.5,
1039
+ "color": "#CEB88B",
1040
+ "background": "none",
1041
+ "backgroundPadding": 0,
1042
+ "width": 0.82,
1043
+ "x": 50,
1044
+ "y": 88,
1045
+ "position": "center",
1046
+ "start": 16.7,
1047
+ "end": 19.99,
1048
+ "duration": 3.29,
1049
+ "animation": "fade",
1050
+ "animIn": 0.25,
1051
+ "animOut": 0.2,
1052
+ "rotation": 0
1053
+ },
1054
+ {
1055
+ "id": "opening-title",
1056
+ "trackId": "text",
1057
+ "type": "text",
1058
+ "text": "An interior photo collection",
1059
+ "font": "Hanken Grotesk",
1060
+ "size": 16,
1061
+ "color": "#F7F0DB",
1062
+ "background": "none",
1063
+ "backgroundPadding": 0,
1064
+ "width": 0.82,
1065
+ "x": 50,
1066
+ "y": 18,
1067
+ "position": "center",
1068
+ "start": 0,
1069
+ "end": 3.4,
1070
+ "duration": 3.4,
1071
+ "animation": "fade",
1072
+ "animIn": 0.25,
1073
+ "animOut": 0.2,
1074
+ "rotation": 0
1075
+ },
1076
+ {
1077
+ "id": "sequence-title",
1078
+ "trackId": "text",
1079
+ "type": "text",
1080
+ "text": "An interior photo collection",
1081
+ "font": "Hanken Grotesk",
1082
+ "size": 10,
1083
+ "color": "#F7F0DB",
1084
+ "background": "none",
1085
+ "backgroundPadding": 0,
1086
+ "width": 0.82,
1087
+ "x": 50,
1088
+ "y": 16,
1089
+ "position": "center",
1090
+ "start": 3.5,
1091
+ "end": 20,
1092
+ "duration": 16.5,
1093
+ "animation": "fade",
1094
+ "animIn": 0.25,
1095
+ "animOut": 0.2,
1096
+ "rotation": 0
1097
+ },
1098
+ {
1099
+ "id": "closing-title",
1100
+ "trackId": "text",
1101
+ "type": "text",
1102
+ "text": "An interior photo collection",
1103
+ "font": "Hanken Grotesk",
1104
+ "size": 10,
1105
+ "color": "#CEB88B",
1106
+ "background": "none",
1107
+ "backgroundPadding": 0,
1108
+ "width": 0.82,
1109
+ "x": 50,
1110
+ "y": 13,
1111
+ "position": "center",
1112
+ "start": 20,
1113
+ "end": 24,
1114
+ "duration": 4,
1115
+ "animation": "fade",
1116
+ "animIn": 0.25,
1117
+ "animOut": 0.2,
1118
+ "rotation": 0
1119
+ },
1120
+ {
1121
+ "id": "closing-cta",
1122
+ "trackId": "text",
1123
+ "type": "text",
1124
+ "text": "Ask for the details",
1125
+ "font": "Hanken Grotesk",
1126
+ "size": 17,
1127
+ "color": "#F7F0DB",
1128
+ "background": "none",
1129
+ "backgroundPadding": 0,
1130
+ "width": 0.82,
1131
+ "x": 50,
1132
+ "y": 65,
1133
+ "position": "center",
1134
+ "start": 20.1,
1135
+ "end": 24,
1136
+ "duration": 3.9,
1137
+ "animation": "fade",
1138
+ "animIn": 0.25,
1139
+ "animOut": 0.2,
1140
+ "rotation": 0
1141
+ },
1142
+ {
1143
+ "id": "closing-contact",
1144
+ "trackId": "text",
1145
+ "type": "text",
1146
+ "text": "Example contact",
1147
+ "font": "Hanken Grotesk",
1148
+ "size": 9,
1149
+ "color": "#F7F0DB",
1150
+ "background": "none",
1151
+ "backgroundPadding": 0,
1152
+ "width": 0.82,
1153
+ "x": 50,
1154
+ "y": 79,
1155
+ "position": "center",
1156
+ "start": 20.25,
1157
+ "end": 24,
1158
+ "duration": 3.75,
1159
+ "animation": "fade",
1160
+ "animIn": 0.25,
1161
+ "animOut": 0.2,
1162
+ "rotation": 0
1163
+ },
1164
+ {
1165
+ "id": "demo-disclosure",
1166
+ "trackId": "text",
1167
+ "type": "text",
1168
+ "text": "Sample only · not a live listing",
1169
+ "font": "Hanken Grotesk",
1170
+ "size": 6.5,
1171
+ "color": "#D4D9D0",
1172
+ "background": "none",
1173
+ "backgroundPadding": 0,
1174
+ "width": 0.84,
1175
+ "x": 50,
1176
+ "y": 94,
1177
+ "position": "center",
1178
+ "start": 0,
1179
+ "end": 24,
1180
+ "duration": 24,
1181
+ "animation": "fade",
1182
+ "animIn": 0.25,
1183
+ "animOut": 0.2,
1184
+ "rotation": 0
1185
+ }
1186
+ ],
1187
+ "captions": [],
1188
+ "stickers": [
1189
+ {
1190
+ "id": "editorial-rule",
1191
+ "trackId": "overlay",
1192
+ "shape": "underline",
1193
+ "color": "#CEB88B",
1194
+ "x": 50,
1195
+ "y": 25,
1196
+ "scale": 0.2,
1197
+ "stretchX": 1.1,
1198
+ "stretchY": 0.24,
1199
+ "opacity": 0.85,
1200
+ "rotation": 0,
1201
+ "start": 0,
1202
+ "end": 20
1203
+ },
1204
+ {
1205
+ "id": "closing-rule",
1206
+ "trackId": "overlay",
1207
+ "shape": "underline",
1208
+ "color": "#CEB88B",
1209
+ "x": 50,
1210
+ "y": 55,
1211
+ "scale": 0.2,
1212
+ "stretchX": 1.1,
1213
+ "stretchY": 0.24,
1214
+ "opacity": 0.85,
1215
+ "rotation": 0,
1216
+ "start": 20,
1217
+ "end": 24
1218
+ }
1219
+ ],
1220
+ "effects": [],
1221
+ "audio": [],
1222
+ "overlays": [
1223
+ {
1224
+ "id": "photo-caption-1",
1225
+ "trackId": "text",
1226
+ "type": "text",
1227
+ "text": "Entry",
1228
+ "font": "Hanken Grotesk",
1229
+ "size": 11,
1230
+ "color": "#F7F0DB",
1231
+ "background": "none",
1232
+ "backgroundPadding": 0,
1233
+ "width": 0.82,
1234
+ "x": 50,
1235
+ "y": 79,
1236
+ "position": "center",
1237
+ "start": 0.2,
1238
+ "end": 3.4,
1239
+ "duration": 3.2,
1240
+ "animation": "fade",
1241
+ "animIn": 0.25,
1242
+ "animOut": 0.2,
1243
+ "rotation": 0
1244
+ },
1245
+ {
1246
+ "id": "photo-number-1",
1247
+ "trackId": "text",
1248
+ "type": "text",
1249
+ "text": "01 / 06",
1250
+ "font": "Hanken Grotesk",
1251
+ "size": 6.5,
1252
+ "color": "#CEB88B",
1253
+ "background": "none",
1254
+ "backgroundPadding": 0,
1255
+ "width": 0.82,
1256
+ "x": 50,
1257
+ "y": 88,
1258
+ "position": "center",
1259
+ "start": 0,
1260
+ "end": 3.49,
1261
+ "duration": 3.49,
1262
+ "animation": "fade",
1263
+ "animIn": 0.25,
1264
+ "animOut": 0.2,
1265
+ "rotation": 0
1266
+ },
1267
+ {
1268
+ "id": "photo-caption-2",
1269
+ "trackId": "text",
1270
+ "type": "text",
1271
+ "text": "Living area",
1272
+ "font": "Hanken Grotesk",
1273
+ "size": 11,
1274
+ "color": "#F7F0DB",
1275
+ "background": "none",
1276
+ "backgroundPadding": 0,
1277
+ "width": 0.82,
1278
+ "x": 50,
1279
+ "y": 79,
1280
+ "position": "center",
1281
+ "start": 3.7,
1282
+ "end": 6.7,
1283
+ "duration": 3,
1284
+ "animation": "fade",
1285
+ "animIn": 0.25,
1286
+ "animOut": 0.2,
1287
+ "rotation": 0
1288
+ },
1289
+ {
1290
+ "id": "photo-number-2",
1291
+ "trackId": "text",
1292
+ "type": "text",
1293
+ "text": "02 / 06",
1294
+ "font": "Hanken Grotesk",
1295
+ "size": 6.5,
1296
+ "color": "#CEB88B",
1297
+ "background": "none",
1298
+ "backgroundPadding": 0,
1299
+ "width": 0.82,
1300
+ "x": 50,
1301
+ "y": 88,
1302
+ "position": "center",
1303
+ "start": 3.5,
1304
+ "end": 6.79,
1305
+ "duration": 3.29,
1306
+ "animation": "fade",
1307
+ "animIn": 0.25,
1308
+ "animOut": 0.2,
1309
+ "rotation": 0
1310
+ },
1311
+ {
1312
+ "id": "photo-caption-3",
1313
+ "trackId": "text",
1314
+ "type": "text",
1315
+ "text": "Dining area",
1316
+ "font": "Hanken Grotesk",
1317
+ "size": 11,
1318
+ "color": "#F7F0DB",
1319
+ "background": "none",
1320
+ "backgroundPadding": 0,
1321
+ "width": 0.82,
1322
+ "x": 50,
1323
+ "y": 79,
1324
+ "position": "center",
1325
+ "start": 7,
1326
+ "end": 10,
1327
+ "duration": 3,
1328
+ "animation": "fade",
1329
+ "animIn": 0.25,
1330
+ "animOut": 0.2,
1331
+ "rotation": 0
1332
+ },
1333
+ {
1334
+ "id": "photo-number-3",
1335
+ "trackId": "text",
1336
+ "type": "text",
1337
+ "text": "03 / 06",
1338
+ "font": "Hanken Grotesk",
1339
+ "size": 6.5,
1340
+ "color": "#CEB88B",
1341
+ "background": "none",
1342
+ "backgroundPadding": 0,
1343
+ "width": 0.82,
1344
+ "x": 50,
1345
+ "y": 88,
1346
+ "position": "center",
1347
+ "start": 6.8,
1348
+ "end": 10.09,
1349
+ "duration": 3.29,
1350
+ "animation": "fade",
1351
+ "animIn": 0.25,
1352
+ "animOut": 0.2,
1353
+ "rotation": 0
1354
+ },
1355
+ {
1356
+ "id": "photo-caption-4",
1357
+ "trackId": "text",
1358
+ "type": "text",
1359
+ "text": "Kitchen",
1360
+ "font": "Hanken Grotesk",
1361
+ "size": 11,
1362
+ "color": "#F7F0DB",
1363
+ "background": "none",
1364
+ "backgroundPadding": 0,
1365
+ "width": 0.82,
1366
+ "x": 50,
1367
+ "y": 79,
1368
+ "position": "center",
1369
+ "start": 10.3,
1370
+ "end": 13.3,
1371
+ "duration": 3,
1372
+ "animation": "fade",
1373
+ "animIn": 0.25,
1374
+ "animOut": 0.2,
1375
+ "rotation": 0
1376
+ },
1377
+ {
1378
+ "id": "photo-number-4",
1379
+ "trackId": "text",
1380
+ "type": "text",
1381
+ "text": "04 / 06",
1382
+ "font": "Hanken Grotesk",
1383
+ "size": 6.5,
1384
+ "color": "#CEB88B",
1385
+ "background": "none",
1386
+ "backgroundPadding": 0,
1387
+ "width": 0.82,
1388
+ "x": 50,
1389
+ "y": 88,
1390
+ "position": "center",
1391
+ "start": 10.1,
1392
+ "end": 13.39,
1393
+ "duration": 3.29,
1394
+ "animation": "fade",
1395
+ "animIn": 0.25,
1396
+ "animOut": 0.2,
1397
+ "rotation": 0
1398
+ },
1399
+ {
1400
+ "id": "photo-caption-5",
1401
+ "trackId": "text",
1402
+ "type": "text",
1403
+ "text": "Bedroom",
1404
+ "font": "Hanken Grotesk",
1405
+ "size": 11,
1406
+ "color": "#F7F0DB",
1407
+ "background": "none",
1408
+ "backgroundPadding": 0,
1409
+ "width": 0.82,
1410
+ "x": 50,
1411
+ "y": 79,
1412
+ "position": "center",
1413
+ "start": 13.6,
1414
+ "end": 16.6,
1415
+ "duration": 3,
1416
+ "animation": "fade",
1417
+ "animIn": 0.25,
1418
+ "animOut": 0.2,
1419
+ "rotation": 0
1420
+ },
1421
+ {
1422
+ "id": "photo-number-5",
1423
+ "trackId": "text",
1424
+ "type": "text",
1425
+ "text": "05 / 06",
1426
+ "font": "Hanken Grotesk",
1427
+ "size": 6.5,
1428
+ "color": "#CEB88B",
1429
+ "background": "none",
1430
+ "backgroundPadding": 0,
1431
+ "width": 0.82,
1432
+ "x": 50,
1433
+ "y": 88,
1434
+ "position": "center",
1435
+ "start": 13.4,
1436
+ "end": 16.69,
1437
+ "duration": 3.29,
1438
+ "animation": "fade",
1439
+ "animIn": 0.25,
1440
+ "animOut": 0.2,
1441
+ "rotation": 0
1442
+ },
1443
+ {
1444
+ "id": "photo-caption-6",
1445
+ "trackId": "text",
1446
+ "type": "text",
1447
+ "text": "Detail",
1448
+ "font": "Hanken Grotesk",
1449
+ "size": 11,
1450
+ "color": "#F7F0DB",
1451
+ "background": "none",
1452
+ "backgroundPadding": 0,
1453
+ "width": 0.82,
1454
+ "x": 50,
1455
+ "y": 79,
1456
+ "position": "center",
1457
+ "start": 16.9,
1458
+ "end": 19.9,
1459
+ "duration": 3,
1460
+ "animation": "fade",
1461
+ "animIn": 0.25,
1462
+ "animOut": 0.2,
1463
+ "rotation": 0
1464
+ },
1465
+ {
1466
+ "id": "photo-number-6",
1467
+ "trackId": "text",
1468
+ "type": "text",
1469
+ "text": "06 / 06",
1470
+ "font": "Hanken Grotesk",
1471
+ "size": 6.5,
1472
+ "color": "#CEB88B",
1473
+ "background": "none",
1474
+ "backgroundPadding": 0,
1475
+ "width": 0.82,
1476
+ "x": 50,
1477
+ "y": 88,
1478
+ "position": "center",
1479
+ "start": 16.7,
1480
+ "end": 19.99,
1481
+ "duration": 3.29,
1482
+ "animation": "fade",
1483
+ "animIn": 0.25,
1484
+ "animOut": 0.2,
1485
+ "rotation": 0
1486
+ },
1487
+ {
1488
+ "id": "opening-title",
1489
+ "trackId": "text",
1490
+ "type": "text",
1491
+ "text": "An interior photo collection",
1492
+ "font": "Hanken Grotesk",
1493
+ "size": 16,
1494
+ "color": "#F7F0DB",
1495
+ "background": "none",
1496
+ "backgroundPadding": 0,
1497
+ "width": 0.82,
1498
+ "x": 50,
1499
+ "y": 18,
1500
+ "position": "center",
1501
+ "start": 0,
1502
+ "end": 3.4,
1503
+ "duration": 3.4,
1504
+ "animation": "fade",
1505
+ "animIn": 0.25,
1506
+ "animOut": 0.2,
1507
+ "rotation": 0
1508
+ },
1509
+ {
1510
+ "id": "sequence-title",
1511
+ "trackId": "text",
1512
+ "type": "text",
1513
+ "text": "An interior photo collection",
1514
+ "font": "Hanken Grotesk",
1515
+ "size": 10,
1516
+ "color": "#F7F0DB",
1517
+ "background": "none",
1518
+ "backgroundPadding": 0,
1519
+ "width": 0.82,
1520
+ "x": 50,
1521
+ "y": 16,
1522
+ "position": "center",
1523
+ "start": 3.5,
1524
+ "end": 20,
1525
+ "duration": 16.5,
1526
+ "animation": "fade",
1527
+ "animIn": 0.25,
1528
+ "animOut": 0.2,
1529
+ "rotation": 0
1530
+ },
1531
+ {
1532
+ "id": "closing-title",
1533
+ "trackId": "text",
1534
+ "type": "text",
1535
+ "text": "An interior photo collection",
1536
+ "font": "Hanken Grotesk",
1537
+ "size": 10,
1538
+ "color": "#CEB88B",
1539
+ "background": "none",
1540
+ "backgroundPadding": 0,
1541
+ "width": 0.82,
1542
+ "x": 50,
1543
+ "y": 13,
1544
+ "position": "center",
1545
+ "start": 20,
1546
+ "end": 24,
1547
+ "duration": 4,
1548
+ "animation": "fade",
1549
+ "animIn": 0.25,
1550
+ "animOut": 0.2,
1551
+ "rotation": 0
1552
+ },
1553
+ {
1554
+ "id": "closing-cta",
1555
+ "trackId": "text",
1556
+ "type": "text",
1557
+ "text": "Ask for the details",
1558
+ "font": "Hanken Grotesk",
1559
+ "size": 17,
1560
+ "color": "#F7F0DB",
1561
+ "background": "none",
1562
+ "backgroundPadding": 0,
1563
+ "width": 0.82,
1564
+ "x": 50,
1565
+ "y": 65,
1566
+ "position": "center",
1567
+ "start": 20.1,
1568
+ "end": 24,
1569
+ "duration": 3.9,
1570
+ "animation": "fade",
1571
+ "animIn": 0.25,
1572
+ "animOut": 0.2,
1573
+ "rotation": 0
1574
+ },
1575
+ {
1576
+ "id": "closing-contact",
1577
+ "trackId": "text",
1578
+ "type": "text",
1579
+ "text": "Example contact",
1580
+ "font": "Hanken Grotesk",
1581
+ "size": 9,
1582
+ "color": "#F7F0DB",
1583
+ "background": "none",
1584
+ "backgroundPadding": 0,
1585
+ "width": 0.82,
1586
+ "x": 50,
1587
+ "y": 79,
1588
+ "position": "center",
1589
+ "start": 20.25,
1590
+ "end": 24,
1591
+ "duration": 3.75,
1592
+ "animation": "fade",
1593
+ "animIn": 0.25,
1594
+ "animOut": 0.2,
1595
+ "rotation": 0
1596
+ },
1597
+ {
1598
+ "id": "demo-disclosure",
1599
+ "trackId": "text",
1600
+ "type": "text",
1601
+ "text": "Sample only · not a live listing",
1602
+ "font": "Hanken Grotesk",
1603
+ "size": 6.5,
1604
+ "color": "#D4D9D0",
1605
+ "background": "none",
1606
+ "backgroundPadding": 0,
1607
+ "width": 0.84,
1608
+ "x": 50,
1609
+ "y": 94,
1610
+ "position": "center",
1611
+ "start": 0,
1612
+ "end": 24,
1613
+ "duration": 24,
1614
+ "animation": "fade",
1615
+ "animIn": 0.25,
1616
+ "animOut": 0.2,
1617
+ "rotation": 0
1618
+ }
1619
+ ],
1620
+ "targetDuration": 24,
1621
+ "exportSettings": {
1622
+ "aspect": "9:16",
1623
+ "fps": 30,
1624
+ "resolution": "1080p",
1625
+ "quality": "high"
1626
+ },
1627
+ "photoReel": {
1628
+ "schemaVersion": 1,
1629
+ "kind": "photo-reel-source-record",
1630
+ "block": {
1631
+ "id": "stillmade.photo-reel-composer",
1632
+ "version": "1.0.0"
1633
+ },
1634
+ "purpose": "property",
1635
+ "sourcePhotos": [
1636
+ {
1637
+ "index": 0,
1638
+ "assetId": "photo-1",
1639
+ "versionId": "original-v1",
1640
+ "width": 1600,
1641
+ "height": 1067
1642
+ },
1643
+ {
1644
+ "index": 1,
1645
+ "assetId": "photo-2",
1646
+ "versionId": "original-v1",
1647
+ "width": 1600,
1648
+ "height": 1067
1649
+ },
1650
+ {
1651
+ "index": 2,
1652
+ "assetId": "photo-3",
1653
+ "versionId": "original-v1",
1654
+ "width": 1600,
1655
+ "height": 1067
1656
+ },
1657
+ {
1658
+ "index": 3,
1659
+ "assetId": "photo-4",
1660
+ "versionId": "original-v1",
1661
+ "width": 1600,
1662
+ "height": 1067
1663
+ },
1664
+ {
1665
+ "index": 4,
1666
+ "assetId": "photo-5",
1667
+ "versionId": "original-v1",
1668
+ "width": 1600,
1669
+ "height": 1067
1670
+ },
1671
+ {
1672
+ "index": 5,
1673
+ "assetId": "photo-6",
1674
+ "versionId": "original-v1",
1675
+ "width": 1600,
1676
+ "height": 1067
1677
+ }
1678
+ ],
1679
+ "logo": null,
1680
+ "music": null,
1681
+ "approvedCopy": {
1682
+ "title": "An interior photo collection",
1683
+ "captions": [
1684
+ "Entry",
1685
+ "Living area",
1686
+ "Dining area",
1687
+ "Kitchen",
1688
+ "Bedroom",
1689
+ "Detail"
1690
+ ],
1691
+ "cta": "Ask for the details",
1692
+ "contact": "Example contact",
1693
+ "demoDisclosure": "Sample only · not a live listing"
1694
+ },
1695
+ "rights": {
1696
+ "userConfirmed": true,
1697
+ "independentlyVerified": false
1698
+ },
1699
+ "transformations": [
1700
+ "contain fit",
1701
+ "uniform scale",
1702
+ "at most three percent optical zoom"
1703
+ ],
1704
+ "generativeMedia": false,
1705
+ "providerCalls": 0
1706
+ }
1707
+ },
1708
+ "review": {
1709
+ "schemaVersion": 1,
1710
+ "state": "needs-human-review",
1711
+ "durationSec": 24,
1712
+ "aspect": "9:16",
1713
+ "photoCount": 6,
1714
+ "checks": [
1715
+ {
1716
+ "id": "original-source-versions",
1717
+ "passed": true,
1718
+ "note": "Every photo retains its supplied asset/version and full source rectangle."
1719
+ },
1720
+ {
1721
+ "id": "approved-copy-only",
1722
+ "passed": true,
1723
+ "note": "No property features, prices, availability or product claims are generated."
1724
+ },
1725
+ {
1726
+ "id": "bounded-local-composition",
1727
+ "passed": true,
1728
+ "note": "No provider call or credit spend occurs in this Block."
1729
+ },
1730
+ {
1731
+ "id": "complete-timing",
1732
+ "passed": true,
1733
+ "note": "All source photos and a four-second closing card fit the requested duration."
1734
+ }
1735
+ ],
1736
+ "warnings": [
1737
+ "Review the full export for photo quality, exact facts, readable copy, licensing and the displayed contact before delivering.",
1738
+ "Silent reel. Add rights-cleared music in this Block or the Editor if wanted.",
1739
+ "This is labeled demonstration material and is not evidence of a real property or product offer."
1740
+ ],
1741
+ "humanApproved": false,
1742
+ "exportAuthorized": false,
1743
+ "cost": {
1744
+ "providerCalls": 0,
1745
+ "generationCredits": 0,
1746
+ "note": "Host storage, export access and any separately added services use their own policy."
1747
+ }
1748
+ },
1749
+ "provenance": {
1750
+ "schemaVersion": 1,
1751
+ "kind": "photo-reel-source-record",
1752
+ "block": {
1753
+ "id": "stillmade.photo-reel-composer",
1754
+ "version": "1.0.0"
1755
+ },
1756
+ "purpose": "property",
1757
+ "sourcePhotos": [
1758
+ {
1759
+ "index": 0,
1760
+ "assetId": "photo-1",
1761
+ "versionId": "original-v1",
1762
+ "width": 1600,
1763
+ "height": 1067
1764
+ },
1765
+ {
1766
+ "index": 1,
1767
+ "assetId": "photo-2",
1768
+ "versionId": "original-v1",
1769
+ "width": 1600,
1770
+ "height": 1067
1771
+ },
1772
+ {
1773
+ "index": 2,
1774
+ "assetId": "photo-3",
1775
+ "versionId": "original-v1",
1776
+ "width": 1600,
1777
+ "height": 1067
1778
+ },
1779
+ {
1780
+ "index": 3,
1781
+ "assetId": "photo-4",
1782
+ "versionId": "original-v1",
1783
+ "width": 1600,
1784
+ "height": 1067
1785
+ },
1786
+ {
1787
+ "index": 4,
1788
+ "assetId": "photo-5",
1789
+ "versionId": "original-v1",
1790
+ "width": 1600,
1791
+ "height": 1067
1792
+ },
1793
+ {
1794
+ "index": 5,
1795
+ "assetId": "photo-6",
1796
+ "versionId": "original-v1",
1797
+ "width": 1600,
1798
+ "height": 1067
1799
+ }
1800
+ ],
1801
+ "logo": null,
1802
+ "music": null,
1803
+ "approvedCopy": {
1804
+ "title": "An interior photo collection",
1805
+ "captions": [
1806
+ "Entry",
1807
+ "Living area",
1808
+ "Dining area",
1809
+ "Kitchen",
1810
+ "Bedroom",
1811
+ "Detail"
1812
+ ],
1813
+ "cta": "Ask for the details",
1814
+ "contact": "Example contact",
1815
+ "demoDisclosure": "Sample only · not a live listing"
1816
+ },
1817
+ "rights": {
1818
+ "userConfirmed": true,
1819
+ "independentlyVerified": false
1820
+ },
1821
+ "transformations": [
1822
+ "contain fit",
1823
+ "uniform scale",
1824
+ "at most three percent optical zoom"
1825
+ ],
1826
+ "generativeMedia": false,
1827
+ "providerCalls": 0
1828
+ }
1829
+ }
1830
+ },
1831
+ {
1832
+ "name": "Compose a product reel without inventing product claims",
1833
+ "input": {
1834
+ "assets": [
1835
+ {
1836
+ "kind": "image",
1837
+ "assetId": "photo-1",
1838
+ "versionId": "original-v1",
1839
+ "url": "https://media.example/photo-1.jpg",
1840
+ "width": 1600,
1841
+ "height": 1067,
1842
+ "name": "Approved photo 1"
1843
+ },
1844
+ {
1845
+ "kind": "image",
1846
+ "assetId": "photo-2",
1847
+ "versionId": "original-v1",
1848
+ "url": "https://media.example/photo-2.jpg",
1849
+ "width": 1600,
1850
+ "height": 1067,
1851
+ "name": "Approved photo 2"
1852
+ },
1853
+ {
1854
+ "kind": "image",
1855
+ "assetId": "photo-3",
1856
+ "versionId": "original-v1",
1857
+ "url": "https://media.example/photo-3.jpg",
1858
+ "width": 1600,
1859
+ "height": 1067,
1860
+ "name": "Approved photo 3"
1861
+ },
1862
+ {
1863
+ "kind": "image",
1864
+ "assetId": "photo-4",
1865
+ "versionId": "original-v1",
1866
+ "url": "https://media.example/photo-4.jpg",
1867
+ "width": 1600,
1868
+ "height": 1067,
1869
+ "name": "Approved photo 4"
1870
+ }
1871
+ ],
1872
+ "purpose": "product",
1873
+ "title": "An object in four views",
1874
+ "captions": "Front\nSide\nTexture\nDetail",
1875
+ "cta": "Explore the collection",
1876
+ "contact": "Example contact",
1877
+ "demoDisclosure": "Sample only · not a product offer",
1878
+ "duration": 24,
1879
+ "aspect": "9:16",
1880
+ "logo": [],
1881
+ "music": []
1882
+ },
1883
+ "expected": {
1884
+ "timeline": {
1885
+ "schemaVersion": 1,
1886
+ "activeTimelineId": "photo-reel",
1887
+ "name": "An object in four views",
1888
+ "aspect": "9:16",
1889
+ "aspectRatio": "9:16",
1890
+ "previewH": 369,
1891
+ "backgroundColor": "#101714",
1892
+ "tracks": [
1893
+ {
1894
+ "id": "video",
1895
+ "type": "video",
1896
+ "label": "Original photos"
1897
+ },
1898
+ {
1899
+ "id": "text",
1900
+ "type": "text",
1901
+ "label": "Approved copy"
1902
+ },
1903
+ {
1904
+ "id": "overlay",
1905
+ "type": "overlay",
1906
+ "label": "Graphic details"
1907
+ },
1908
+ {
1909
+ "id": "audio",
1910
+ "type": "audio",
1911
+ "label": "Music"
1912
+ }
1913
+ ],
1914
+ "trackOrder": [
1915
+ "video",
1916
+ "logo",
1917
+ "text",
1918
+ "overlay",
1919
+ "audio"
1920
+ ],
1921
+ "clips": [
1922
+ {
1923
+ "id": "photo-1",
1924
+ "kind": "image",
1925
+ "trackId": "video",
1926
+ "asset": {
1927
+ "kind": "image",
1928
+ "assetId": "photo-1",
1929
+ "versionId": "original-v1",
1930
+ "url": "https://media.example/photo-1.jpg",
1931
+ "width": 1600,
1932
+ "height": 1067,
1933
+ "name": "Approved photo 1"
1934
+ },
1935
+ "assetId": "photo-1",
1936
+ "versionId": "original-v1",
1937
+ "url": "https://media.example/photo-1.jpg",
1938
+ "title": "Approved photo 1",
1939
+ "start": 0,
1940
+ "duration": 3.5,
1941
+ "trimStart": 0,
1942
+ "speed": 1,
1943
+ "volume": 0,
1944
+ "transform": {
1945
+ "fit": "contain",
1946
+ "scale": 0.8737864077669902,
1947
+ "x": 0,
1948
+ "y": 0,
1949
+ "rotation": 0,
1950
+ "opacity": 1
1951
+ },
1952
+ "motion": {
1953
+ "type": "zoom",
1954
+ "intensity": 100,
1955
+ "zoomAmount": 1.03,
1956
+ "zoomDirection": "in",
1957
+ "zoomAnchor": "center",
1958
+ "easing": "ease-in-out",
1959
+ "loop": false,
1960
+ "window": [
1961
+ 0,
1962
+ 100
1963
+ ]
1964
+ },
1965
+ "transition": {
1966
+ "type": "Cross dissolve",
1967
+ "duration": 0.3
1968
+ }
1969
+ },
1970
+ {
1971
+ "id": "photo-2",
1972
+ "kind": "image",
1973
+ "trackId": "video",
1974
+ "asset": {
1975
+ "kind": "image",
1976
+ "assetId": "photo-2",
1977
+ "versionId": "original-v1",
1978
+ "url": "https://media.example/photo-2.jpg",
1979
+ "width": 1600,
1980
+ "height": 1067,
1981
+ "name": "Approved photo 2"
1982
+ },
1983
+ "assetId": "photo-2",
1984
+ "versionId": "original-v1",
1985
+ "url": "https://media.example/photo-2.jpg",
1986
+ "title": "Approved photo 2",
1987
+ "start": 3.5,
1988
+ "duration": 5.5,
1989
+ "trimStart": 0,
1990
+ "speed": 1,
1991
+ "volume": 0,
1992
+ "transform": {
1993
+ "fit": "contain",
1994
+ "scale": 0.8737864077669902,
1995
+ "x": 0,
1996
+ "y": 0,
1997
+ "rotation": 0,
1998
+ "opacity": 1
1999
+ },
2000
+ "motion": {
2001
+ "type": "zoom",
2002
+ "intensity": 100,
2003
+ "zoomAmount": 1.03,
2004
+ "zoomDirection": "out",
2005
+ "zoomAnchor": "center",
2006
+ "easing": "ease-in-out",
2007
+ "loop": false,
2008
+ "window": [
2009
+ 0,
2010
+ 100
2011
+ ]
2012
+ },
2013
+ "transition": {
2014
+ "type": "Cross dissolve",
2015
+ "duration": 0.3
2016
+ }
2017
+ },
2018
+ {
2019
+ "id": "photo-3",
2020
+ "kind": "image",
2021
+ "trackId": "video",
2022
+ "asset": {
2023
+ "kind": "image",
2024
+ "assetId": "photo-3",
2025
+ "versionId": "original-v1",
2026
+ "url": "https://media.example/photo-3.jpg",
2027
+ "width": 1600,
2028
+ "height": 1067,
2029
+ "name": "Approved photo 3"
2030
+ },
2031
+ "assetId": "photo-3",
2032
+ "versionId": "original-v1",
2033
+ "url": "https://media.example/photo-3.jpg",
2034
+ "title": "Approved photo 3",
2035
+ "start": 9,
2036
+ "duration": 5.5,
2037
+ "trimStart": 0,
2038
+ "speed": 1,
2039
+ "volume": 0,
2040
+ "transform": {
2041
+ "fit": "contain",
2042
+ "scale": 0.8737864077669902,
2043
+ "x": 0,
2044
+ "y": 0,
2045
+ "rotation": 0,
2046
+ "opacity": 1
2047
+ },
2048
+ "motion": {
2049
+ "type": "zoom",
2050
+ "intensity": 100,
2051
+ "zoomAmount": 1.03,
2052
+ "zoomDirection": "in",
2053
+ "zoomAnchor": "center",
2054
+ "easing": "ease-in-out",
2055
+ "loop": false,
2056
+ "window": [
2057
+ 0,
2058
+ 100
2059
+ ]
2060
+ },
2061
+ "transition": {
2062
+ "type": "Cross dissolve",
2063
+ "duration": 0.3
2064
+ }
2065
+ },
2066
+ {
2067
+ "id": "photo-4",
2068
+ "kind": "image",
2069
+ "trackId": "video",
2070
+ "asset": {
2071
+ "kind": "image",
2072
+ "assetId": "photo-4",
2073
+ "versionId": "original-v1",
2074
+ "url": "https://media.example/photo-4.jpg",
2075
+ "width": 1600,
2076
+ "height": 1067,
2077
+ "name": "Approved photo 4"
2078
+ },
2079
+ "assetId": "photo-4",
2080
+ "versionId": "original-v1",
2081
+ "url": "https://media.example/photo-4.jpg",
2082
+ "title": "Approved photo 4",
2083
+ "start": 14.5,
2084
+ "duration": 5.5,
2085
+ "trimStart": 0,
2086
+ "speed": 1,
2087
+ "volume": 0,
2088
+ "transform": {
2089
+ "fit": "contain",
2090
+ "scale": 0.8737864077669902,
2091
+ "x": 0,
2092
+ "y": 0,
2093
+ "rotation": 0,
2094
+ "opacity": 1
2095
+ },
2096
+ "motion": {
2097
+ "type": "zoom",
2098
+ "intensity": 100,
2099
+ "zoomAmount": 1.03,
2100
+ "zoomDirection": "out",
2101
+ "zoomAnchor": "center",
2102
+ "easing": "ease-in-out",
2103
+ "loop": false,
2104
+ "window": [
2105
+ 0,
2106
+ 100
2107
+ ]
2108
+ },
2109
+ "transition": {
2110
+ "type": "Cross dissolve",
2111
+ "duration": 0.3
2112
+ }
2113
+ },
2114
+ {
2115
+ "id": "end-card-photo-4",
2116
+ "kind": "image",
2117
+ "trackId": "video",
2118
+ "asset": {
2119
+ "kind": "image",
2120
+ "assetId": "photo-4",
2121
+ "versionId": "original-v1",
2122
+ "url": "https://media.example/photo-4.jpg",
2123
+ "width": 1600,
2124
+ "height": 1067,
2125
+ "name": "Approved photo 4"
2126
+ },
2127
+ "assetId": "photo-4",
2128
+ "versionId": "original-v1",
2129
+ "url": "https://media.example/photo-4.jpg",
2130
+ "title": "Approved photo 4",
2131
+ "start": 20,
2132
+ "duration": 4,
2133
+ "trimStart": 0,
2134
+ "speed": 1,
2135
+ "volume": 0,
2136
+ "transform": {
2137
+ "fit": "contain",
2138
+ "scale": 0.6213592233009708,
2139
+ "x": 0,
2140
+ "y": -59.03999999999999,
2141
+ "rotation": 0,
2142
+ "opacity": 1
2143
+ },
2144
+ "motion": {
2145
+ "type": "none",
2146
+ "intensity": 100,
2147
+ "zoomAmount": 1.03,
2148
+ "zoomDirection": "out",
2149
+ "zoomAnchor": "center",
2150
+ "easing": "ease-in-out",
2151
+ "loop": false,
2152
+ "window": [
2153
+ 0,
2154
+ 100
2155
+ ]
2156
+ },
2157
+ "transition": {
2158
+ "type": "Cut",
2159
+ "duration": 0.3
2160
+ }
2161
+ }
2162
+ ],
2163
+ "audioItems": [],
2164
+ "textOverlays": [
2165
+ {
2166
+ "id": "photo-caption-1",
2167
+ "trackId": "text",
2168
+ "type": "text",
2169
+ "text": "Front",
2170
+ "font": "Hanken Grotesk",
2171
+ "size": 11,
2172
+ "color": "#F7F0DB",
2173
+ "background": "none",
2174
+ "backgroundPadding": 0,
2175
+ "width": 0.82,
2176
+ "x": 50,
2177
+ "y": 79,
2178
+ "position": "center",
2179
+ "start": 0.2,
2180
+ "end": 3.4,
2181
+ "duration": 3.2,
2182
+ "animation": "fade",
2183
+ "animIn": 0.25,
2184
+ "animOut": 0.2,
2185
+ "rotation": 0
2186
+ },
2187
+ {
2188
+ "id": "photo-number-1",
2189
+ "trackId": "text",
2190
+ "type": "text",
2191
+ "text": "01 / 04",
2192
+ "font": "Hanken Grotesk",
2193
+ "size": 6.5,
2194
+ "color": "#CEB88B",
2195
+ "background": "none",
2196
+ "backgroundPadding": 0,
2197
+ "width": 0.82,
2198
+ "x": 50,
2199
+ "y": 88,
2200
+ "position": "center",
2201
+ "start": 0,
2202
+ "end": 3.49,
2203
+ "duration": 3.49,
2204
+ "animation": "fade",
2205
+ "animIn": 0.25,
2206
+ "animOut": 0.2,
2207
+ "rotation": 0
2208
+ },
2209
+ {
2210
+ "id": "photo-caption-2",
2211
+ "trackId": "text",
2212
+ "type": "text",
2213
+ "text": "Side",
2214
+ "font": "Hanken Grotesk",
2215
+ "size": 11,
2216
+ "color": "#F7F0DB",
2217
+ "background": "none",
2218
+ "backgroundPadding": 0,
2219
+ "width": 0.82,
2220
+ "x": 50,
2221
+ "y": 79,
2222
+ "position": "center",
2223
+ "start": 3.7,
2224
+ "end": 8.9,
2225
+ "duration": 5.2,
2226
+ "animation": "fade",
2227
+ "animIn": 0.25,
2228
+ "animOut": 0.2,
2229
+ "rotation": 0
2230
+ },
2231
+ {
2232
+ "id": "photo-number-2",
2233
+ "trackId": "text",
2234
+ "type": "text",
2235
+ "text": "02 / 04",
2236
+ "font": "Hanken Grotesk",
2237
+ "size": 6.5,
2238
+ "color": "#CEB88B",
2239
+ "background": "none",
2240
+ "backgroundPadding": 0,
2241
+ "width": 0.82,
2242
+ "x": 50,
2243
+ "y": 88,
2244
+ "position": "center",
2245
+ "start": 3.5,
2246
+ "end": 8.99,
2247
+ "duration": 5.49,
2248
+ "animation": "fade",
2249
+ "animIn": 0.25,
2250
+ "animOut": 0.2,
2251
+ "rotation": 0
2252
+ },
2253
+ {
2254
+ "id": "photo-caption-3",
2255
+ "trackId": "text",
2256
+ "type": "text",
2257
+ "text": "Texture",
2258
+ "font": "Hanken Grotesk",
2259
+ "size": 11,
2260
+ "color": "#F7F0DB",
2261
+ "background": "none",
2262
+ "backgroundPadding": 0,
2263
+ "width": 0.82,
2264
+ "x": 50,
2265
+ "y": 79,
2266
+ "position": "center",
2267
+ "start": 9.2,
2268
+ "end": 14.4,
2269
+ "duration": 5.2,
2270
+ "animation": "fade",
2271
+ "animIn": 0.25,
2272
+ "animOut": 0.2,
2273
+ "rotation": 0
2274
+ },
2275
+ {
2276
+ "id": "photo-number-3",
2277
+ "trackId": "text",
2278
+ "type": "text",
2279
+ "text": "03 / 04",
2280
+ "font": "Hanken Grotesk",
2281
+ "size": 6.5,
2282
+ "color": "#CEB88B",
2283
+ "background": "none",
2284
+ "backgroundPadding": 0,
2285
+ "width": 0.82,
2286
+ "x": 50,
2287
+ "y": 88,
2288
+ "position": "center",
2289
+ "start": 9,
2290
+ "end": 14.49,
2291
+ "duration": 5.49,
2292
+ "animation": "fade",
2293
+ "animIn": 0.25,
2294
+ "animOut": 0.2,
2295
+ "rotation": 0
2296
+ },
2297
+ {
2298
+ "id": "photo-caption-4",
2299
+ "trackId": "text",
2300
+ "type": "text",
2301
+ "text": "Detail",
2302
+ "font": "Hanken Grotesk",
2303
+ "size": 11,
2304
+ "color": "#F7F0DB",
2305
+ "background": "none",
2306
+ "backgroundPadding": 0,
2307
+ "width": 0.82,
2308
+ "x": 50,
2309
+ "y": 79,
2310
+ "position": "center",
2311
+ "start": 14.7,
2312
+ "end": 19.9,
2313
+ "duration": 5.2,
2314
+ "animation": "fade",
2315
+ "animIn": 0.25,
2316
+ "animOut": 0.2,
2317
+ "rotation": 0
2318
+ },
2319
+ {
2320
+ "id": "photo-number-4",
2321
+ "trackId": "text",
2322
+ "type": "text",
2323
+ "text": "04 / 04",
2324
+ "font": "Hanken Grotesk",
2325
+ "size": 6.5,
2326
+ "color": "#CEB88B",
2327
+ "background": "none",
2328
+ "backgroundPadding": 0,
2329
+ "width": 0.82,
2330
+ "x": 50,
2331
+ "y": 88,
2332
+ "position": "center",
2333
+ "start": 14.5,
2334
+ "end": 19.99,
2335
+ "duration": 5.49,
2336
+ "animation": "fade",
2337
+ "animIn": 0.25,
2338
+ "animOut": 0.2,
2339
+ "rotation": 0
2340
+ },
2341
+ {
2342
+ "id": "opening-title",
2343
+ "trackId": "text",
2344
+ "type": "text",
2345
+ "text": "An object in four views",
2346
+ "font": "Hanken Grotesk",
2347
+ "size": 16,
2348
+ "color": "#F7F0DB",
2349
+ "background": "none",
2350
+ "backgroundPadding": 0,
2351
+ "width": 0.82,
2352
+ "x": 50,
2353
+ "y": 18,
2354
+ "position": "center",
2355
+ "start": 0,
2356
+ "end": 3.4,
2357
+ "duration": 3.4,
2358
+ "animation": "fade",
2359
+ "animIn": 0.25,
2360
+ "animOut": 0.2,
2361
+ "rotation": 0
2362
+ },
2363
+ {
2364
+ "id": "sequence-title",
2365
+ "trackId": "text",
2366
+ "type": "text",
2367
+ "text": "An object in four views",
2368
+ "font": "Hanken Grotesk",
2369
+ "size": 10,
2370
+ "color": "#F7F0DB",
2371
+ "background": "none",
2372
+ "backgroundPadding": 0,
2373
+ "width": 0.82,
2374
+ "x": 50,
2375
+ "y": 16,
2376
+ "position": "center",
2377
+ "start": 3.5,
2378
+ "end": 20,
2379
+ "duration": 16.5,
2380
+ "animation": "fade",
2381
+ "animIn": 0.25,
2382
+ "animOut": 0.2,
2383
+ "rotation": 0
2384
+ },
2385
+ {
2386
+ "id": "closing-title",
2387
+ "trackId": "text",
2388
+ "type": "text",
2389
+ "text": "An object in four views",
2390
+ "font": "Hanken Grotesk",
2391
+ "size": 10,
2392
+ "color": "#CEB88B",
2393
+ "background": "none",
2394
+ "backgroundPadding": 0,
2395
+ "width": 0.82,
2396
+ "x": 50,
2397
+ "y": 13,
2398
+ "position": "center",
2399
+ "start": 20,
2400
+ "end": 24,
2401
+ "duration": 4,
2402
+ "animation": "fade",
2403
+ "animIn": 0.25,
2404
+ "animOut": 0.2,
2405
+ "rotation": 0
2406
+ },
2407
+ {
2408
+ "id": "closing-cta",
2409
+ "trackId": "text",
2410
+ "type": "text",
2411
+ "text": "Explore the collection",
2412
+ "font": "Hanken Grotesk",
2413
+ "size": 17,
2414
+ "color": "#F7F0DB",
2415
+ "background": "none",
2416
+ "backgroundPadding": 0,
2417
+ "width": 0.82,
2418
+ "x": 50,
2419
+ "y": 65,
2420
+ "position": "center",
2421
+ "start": 20.1,
2422
+ "end": 24,
2423
+ "duration": 3.9,
2424
+ "animation": "fade",
2425
+ "animIn": 0.25,
2426
+ "animOut": 0.2,
2427
+ "rotation": 0
2428
+ },
2429
+ {
2430
+ "id": "closing-contact",
2431
+ "trackId": "text",
2432
+ "type": "text",
2433
+ "text": "Example contact",
2434
+ "font": "Hanken Grotesk",
2435
+ "size": 9,
2436
+ "color": "#F7F0DB",
2437
+ "background": "none",
2438
+ "backgroundPadding": 0,
2439
+ "width": 0.82,
2440
+ "x": 50,
2441
+ "y": 79,
2442
+ "position": "center",
2443
+ "start": 20.25,
2444
+ "end": 24,
2445
+ "duration": 3.75,
2446
+ "animation": "fade",
2447
+ "animIn": 0.25,
2448
+ "animOut": 0.2,
2449
+ "rotation": 0
2450
+ },
2451
+ {
2452
+ "id": "demo-disclosure",
2453
+ "trackId": "text",
2454
+ "type": "text",
2455
+ "text": "Sample only · not a product offer",
2456
+ "font": "Hanken Grotesk",
2457
+ "size": 6.5,
2458
+ "color": "#D4D9D0",
2459
+ "background": "none",
2460
+ "backgroundPadding": 0,
2461
+ "width": 0.84,
2462
+ "x": 50,
2463
+ "y": 94,
2464
+ "position": "center",
2465
+ "start": 0,
2466
+ "end": 24,
2467
+ "duration": 24,
2468
+ "animation": "fade",
2469
+ "animIn": 0.25,
2470
+ "animOut": 0.2,
2471
+ "rotation": 0
2472
+ }
2473
+ ],
2474
+ "captions": [],
2475
+ "stickers": [
2476
+ {
2477
+ "id": "editorial-rule",
2478
+ "trackId": "overlay",
2479
+ "shape": "underline",
2480
+ "color": "#CEB88B",
2481
+ "x": 50,
2482
+ "y": 25,
2483
+ "scale": 0.2,
2484
+ "stretchX": 1.1,
2485
+ "stretchY": 0.24,
2486
+ "opacity": 0.85,
2487
+ "rotation": 0,
2488
+ "start": 0,
2489
+ "end": 20
2490
+ },
2491
+ {
2492
+ "id": "closing-rule",
2493
+ "trackId": "overlay",
2494
+ "shape": "underline",
2495
+ "color": "#CEB88B",
2496
+ "x": 50,
2497
+ "y": 55,
2498
+ "scale": 0.2,
2499
+ "stretchX": 1.1,
2500
+ "stretchY": 0.24,
2501
+ "opacity": 0.85,
2502
+ "rotation": 0,
2503
+ "start": 20,
2504
+ "end": 24
2505
+ }
2506
+ ],
2507
+ "effects": [],
2508
+ "audio": [],
2509
+ "overlays": [
2510
+ {
2511
+ "id": "photo-caption-1",
2512
+ "trackId": "text",
2513
+ "type": "text",
2514
+ "text": "Front",
2515
+ "font": "Hanken Grotesk",
2516
+ "size": 11,
2517
+ "color": "#F7F0DB",
2518
+ "background": "none",
2519
+ "backgroundPadding": 0,
2520
+ "width": 0.82,
2521
+ "x": 50,
2522
+ "y": 79,
2523
+ "position": "center",
2524
+ "start": 0.2,
2525
+ "end": 3.4,
2526
+ "duration": 3.2,
2527
+ "animation": "fade",
2528
+ "animIn": 0.25,
2529
+ "animOut": 0.2,
2530
+ "rotation": 0
2531
+ },
2532
+ {
2533
+ "id": "photo-number-1",
2534
+ "trackId": "text",
2535
+ "type": "text",
2536
+ "text": "01 / 04",
2537
+ "font": "Hanken Grotesk",
2538
+ "size": 6.5,
2539
+ "color": "#CEB88B",
2540
+ "background": "none",
2541
+ "backgroundPadding": 0,
2542
+ "width": 0.82,
2543
+ "x": 50,
2544
+ "y": 88,
2545
+ "position": "center",
2546
+ "start": 0,
2547
+ "end": 3.49,
2548
+ "duration": 3.49,
2549
+ "animation": "fade",
2550
+ "animIn": 0.25,
2551
+ "animOut": 0.2,
2552
+ "rotation": 0
2553
+ },
2554
+ {
2555
+ "id": "photo-caption-2",
2556
+ "trackId": "text",
2557
+ "type": "text",
2558
+ "text": "Side",
2559
+ "font": "Hanken Grotesk",
2560
+ "size": 11,
2561
+ "color": "#F7F0DB",
2562
+ "background": "none",
2563
+ "backgroundPadding": 0,
2564
+ "width": 0.82,
2565
+ "x": 50,
2566
+ "y": 79,
2567
+ "position": "center",
2568
+ "start": 3.7,
2569
+ "end": 8.9,
2570
+ "duration": 5.2,
2571
+ "animation": "fade",
2572
+ "animIn": 0.25,
2573
+ "animOut": 0.2,
2574
+ "rotation": 0
2575
+ },
2576
+ {
2577
+ "id": "photo-number-2",
2578
+ "trackId": "text",
2579
+ "type": "text",
2580
+ "text": "02 / 04",
2581
+ "font": "Hanken Grotesk",
2582
+ "size": 6.5,
2583
+ "color": "#CEB88B",
2584
+ "background": "none",
2585
+ "backgroundPadding": 0,
2586
+ "width": 0.82,
2587
+ "x": 50,
2588
+ "y": 88,
2589
+ "position": "center",
2590
+ "start": 3.5,
2591
+ "end": 8.99,
2592
+ "duration": 5.49,
2593
+ "animation": "fade",
2594
+ "animIn": 0.25,
2595
+ "animOut": 0.2,
2596
+ "rotation": 0
2597
+ },
2598
+ {
2599
+ "id": "photo-caption-3",
2600
+ "trackId": "text",
2601
+ "type": "text",
2602
+ "text": "Texture",
2603
+ "font": "Hanken Grotesk",
2604
+ "size": 11,
2605
+ "color": "#F7F0DB",
2606
+ "background": "none",
2607
+ "backgroundPadding": 0,
2608
+ "width": 0.82,
2609
+ "x": 50,
2610
+ "y": 79,
2611
+ "position": "center",
2612
+ "start": 9.2,
2613
+ "end": 14.4,
2614
+ "duration": 5.2,
2615
+ "animation": "fade",
2616
+ "animIn": 0.25,
2617
+ "animOut": 0.2,
2618
+ "rotation": 0
2619
+ },
2620
+ {
2621
+ "id": "photo-number-3",
2622
+ "trackId": "text",
2623
+ "type": "text",
2624
+ "text": "03 / 04",
2625
+ "font": "Hanken Grotesk",
2626
+ "size": 6.5,
2627
+ "color": "#CEB88B",
2628
+ "background": "none",
2629
+ "backgroundPadding": 0,
2630
+ "width": 0.82,
2631
+ "x": 50,
2632
+ "y": 88,
2633
+ "position": "center",
2634
+ "start": 9,
2635
+ "end": 14.49,
2636
+ "duration": 5.49,
2637
+ "animation": "fade",
2638
+ "animIn": 0.25,
2639
+ "animOut": 0.2,
2640
+ "rotation": 0
2641
+ },
2642
+ {
2643
+ "id": "photo-caption-4",
2644
+ "trackId": "text",
2645
+ "type": "text",
2646
+ "text": "Detail",
2647
+ "font": "Hanken Grotesk",
2648
+ "size": 11,
2649
+ "color": "#F7F0DB",
2650
+ "background": "none",
2651
+ "backgroundPadding": 0,
2652
+ "width": 0.82,
2653
+ "x": 50,
2654
+ "y": 79,
2655
+ "position": "center",
2656
+ "start": 14.7,
2657
+ "end": 19.9,
2658
+ "duration": 5.2,
2659
+ "animation": "fade",
2660
+ "animIn": 0.25,
2661
+ "animOut": 0.2,
2662
+ "rotation": 0
2663
+ },
2664
+ {
2665
+ "id": "photo-number-4",
2666
+ "trackId": "text",
2667
+ "type": "text",
2668
+ "text": "04 / 04",
2669
+ "font": "Hanken Grotesk",
2670
+ "size": 6.5,
2671
+ "color": "#CEB88B",
2672
+ "background": "none",
2673
+ "backgroundPadding": 0,
2674
+ "width": 0.82,
2675
+ "x": 50,
2676
+ "y": 88,
2677
+ "position": "center",
2678
+ "start": 14.5,
2679
+ "end": 19.99,
2680
+ "duration": 5.49,
2681
+ "animation": "fade",
2682
+ "animIn": 0.25,
2683
+ "animOut": 0.2,
2684
+ "rotation": 0
2685
+ },
2686
+ {
2687
+ "id": "opening-title",
2688
+ "trackId": "text",
2689
+ "type": "text",
2690
+ "text": "An object in four views",
2691
+ "font": "Hanken Grotesk",
2692
+ "size": 16,
2693
+ "color": "#F7F0DB",
2694
+ "background": "none",
2695
+ "backgroundPadding": 0,
2696
+ "width": 0.82,
2697
+ "x": 50,
2698
+ "y": 18,
2699
+ "position": "center",
2700
+ "start": 0,
2701
+ "end": 3.4,
2702
+ "duration": 3.4,
2703
+ "animation": "fade",
2704
+ "animIn": 0.25,
2705
+ "animOut": 0.2,
2706
+ "rotation": 0
2707
+ },
2708
+ {
2709
+ "id": "sequence-title",
2710
+ "trackId": "text",
2711
+ "type": "text",
2712
+ "text": "An object in four views",
2713
+ "font": "Hanken Grotesk",
2714
+ "size": 10,
2715
+ "color": "#F7F0DB",
2716
+ "background": "none",
2717
+ "backgroundPadding": 0,
2718
+ "width": 0.82,
2719
+ "x": 50,
2720
+ "y": 16,
2721
+ "position": "center",
2722
+ "start": 3.5,
2723
+ "end": 20,
2724
+ "duration": 16.5,
2725
+ "animation": "fade",
2726
+ "animIn": 0.25,
2727
+ "animOut": 0.2,
2728
+ "rotation": 0
2729
+ },
2730
+ {
2731
+ "id": "closing-title",
2732
+ "trackId": "text",
2733
+ "type": "text",
2734
+ "text": "An object in four views",
2735
+ "font": "Hanken Grotesk",
2736
+ "size": 10,
2737
+ "color": "#CEB88B",
2738
+ "background": "none",
2739
+ "backgroundPadding": 0,
2740
+ "width": 0.82,
2741
+ "x": 50,
2742
+ "y": 13,
2743
+ "position": "center",
2744
+ "start": 20,
2745
+ "end": 24,
2746
+ "duration": 4,
2747
+ "animation": "fade",
2748
+ "animIn": 0.25,
2749
+ "animOut": 0.2,
2750
+ "rotation": 0
2751
+ },
2752
+ {
2753
+ "id": "closing-cta",
2754
+ "trackId": "text",
2755
+ "type": "text",
2756
+ "text": "Explore the collection",
2757
+ "font": "Hanken Grotesk",
2758
+ "size": 17,
2759
+ "color": "#F7F0DB",
2760
+ "background": "none",
2761
+ "backgroundPadding": 0,
2762
+ "width": 0.82,
2763
+ "x": 50,
2764
+ "y": 65,
2765
+ "position": "center",
2766
+ "start": 20.1,
2767
+ "end": 24,
2768
+ "duration": 3.9,
2769
+ "animation": "fade",
2770
+ "animIn": 0.25,
2771
+ "animOut": 0.2,
2772
+ "rotation": 0
2773
+ },
2774
+ {
2775
+ "id": "closing-contact",
2776
+ "trackId": "text",
2777
+ "type": "text",
2778
+ "text": "Example contact",
2779
+ "font": "Hanken Grotesk",
2780
+ "size": 9,
2781
+ "color": "#F7F0DB",
2782
+ "background": "none",
2783
+ "backgroundPadding": 0,
2784
+ "width": 0.82,
2785
+ "x": 50,
2786
+ "y": 79,
2787
+ "position": "center",
2788
+ "start": 20.25,
2789
+ "end": 24,
2790
+ "duration": 3.75,
2791
+ "animation": "fade",
2792
+ "animIn": 0.25,
2793
+ "animOut": 0.2,
2794
+ "rotation": 0
2795
+ },
2796
+ {
2797
+ "id": "demo-disclosure",
2798
+ "trackId": "text",
2799
+ "type": "text",
2800
+ "text": "Sample only · not a product offer",
2801
+ "font": "Hanken Grotesk",
2802
+ "size": 6.5,
2803
+ "color": "#D4D9D0",
2804
+ "background": "none",
2805
+ "backgroundPadding": 0,
2806
+ "width": 0.84,
2807
+ "x": 50,
2808
+ "y": 94,
2809
+ "position": "center",
2810
+ "start": 0,
2811
+ "end": 24,
2812
+ "duration": 24,
2813
+ "animation": "fade",
2814
+ "animIn": 0.25,
2815
+ "animOut": 0.2,
2816
+ "rotation": 0
2817
+ }
2818
+ ],
2819
+ "targetDuration": 24,
2820
+ "exportSettings": {
2821
+ "aspect": "9:16",
2822
+ "fps": 30,
2823
+ "resolution": "1080p",
2824
+ "quality": "high"
2825
+ },
2826
+ "photoReel": {
2827
+ "schemaVersion": 1,
2828
+ "kind": "photo-reel-source-record",
2829
+ "block": {
2830
+ "id": "stillmade.photo-reel-composer",
2831
+ "version": "1.0.0"
2832
+ },
2833
+ "purpose": "product",
2834
+ "sourcePhotos": [
2835
+ {
2836
+ "index": 0,
2837
+ "assetId": "photo-1",
2838
+ "versionId": "original-v1",
2839
+ "width": 1600,
2840
+ "height": 1067
2841
+ },
2842
+ {
2843
+ "index": 1,
2844
+ "assetId": "photo-2",
2845
+ "versionId": "original-v1",
2846
+ "width": 1600,
2847
+ "height": 1067
2848
+ },
2849
+ {
2850
+ "index": 2,
2851
+ "assetId": "photo-3",
2852
+ "versionId": "original-v1",
2853
+ "width": 1600,
2854
+ "height": 1067
2855
+ },
2856
+ {
2857
+ "index": 3,
2858
+ "assetId": "photo-4",
2859
+ "versionId": "original-v1",
2860
+ "width": 1600,
2861
+ "height": 1067
2862
+ }
2863
+ ],
2864
+ "logo": null,
2865
+ "music": null,
2866
+ "approvedCopy": {
2867
+ "title": "An object in four views",
2868
+ "captions": [
2869
+ "Front",
2870
+ "Side",
2871
+ "Texture",
2872
+ "Detail"
2873
+ ],
2874
+ "cta": "Explore the collection",
2875
+ "contact": "Example contact",
2876
+ "demoDisclosure": "Sample only · not a product offer"
2877
+ },
2878
+ "rights": {
2879
+ "userConfirmed": true,
2880
+ "independentlyVerified": false
2881
+ },
2882
+ "transformations": [
2883
+ "contain fit",
2884
+ "uniform scale",
2885
+ "at most three percent optical zoom"
2886
+ ],
2887
+ "generativeMedia": false,
2888
+ "providerCalls": 0
2889
+ }
2890
+ },
2891
+ "review": {
2892
+ "schemaVersion": 1,
2893
+ "state": "needs-human-review",
2894
+ "durationSec": 24,
2895
+ "aspect": "9:16",
2896
+ "photoCount": 4,
2897
+ "checks": [
2898
+ {
2899
+ "id": "original-source-versions",
2900
+ "passed": true,
2901
+ "note": "Every photo retains its supplied asset/version and full source rectangle."
2902
+ },
2903
+ {
2904
+ "id": "approved-copy-only",
2905
+ "passed": true,
2906
+ "note": "No property features, prices, availability or product claims are generated."
2907
+ },
2908
+ {
2909
+ "id": "bounded-local-composition",
2910
+ "passed": true,
2911
+ "note": "No provider call or credit spend occurs in this Block."
2912
+ },
2913
+ {
2914
+ "id": "complete-timing",
2915
+ "passed": true,
2916
+ "note": "All source photos and a four-second closing card fit the requested duration."
2917
+ }
2918
+ ],
2919
+ "warnings": [
2920
+ "Review the full export for photo quality, exact facts, readable copy, licensing and the displayed contact before delivering.",
2921
+ "Silent reel. Add rights-cleared music in this Block or the Editor if wanted.",
2922
+ "This is labeled demonstration material and is not evidence of a real property or product offer."
2923
+ ],
2924
+ "humanApproved": false,
2925
+ "exportAuthorized": false,
2926
+ "cost": {
2927
+ "providerCalls": 0,
2928
+ "generationCredits": 0,
2929
+ "note": "Host storage, export access and any separately added services use their own policy."
2930
+ }
2931
+ },
2932
+ "provenance": {
2933
+ "schemaVersion": 1,
2934
+ "kind": "photo-reel-source-record",
2935
+ "block": {
2936
+ "id": "stillmade.photo-reel-composer",
2937
+ "version": "1.0.0"
2938
+ },
2939
+ "purpose": "product",
2940
+ "sourcePhotos": [
2941
+ {
2942
+ "index": 0,
2943
+ "assetId": "photo-1",
2944
+ "versionId": "original-v1",
2945
+ "width": 1600,
2946
+ "height": 1067
2947
+ },
2948
+ {
2949
+ "index": 1,
2950
+ "assetId": "photo-2",
2951
+ "versionId": "original-v1",
2952
+ "width": 1600,
2953
+ "height": 1067
2954
+ },
2955
+ {
2956
+ "index": 2,
2957
+ "assetId": "photo-3",
2958
+ "versionId": "original-v1",
2959
+ "width": 1600,
2960
+ "height": 1067
2961
+ },
2962
+ {
2963
+ "index": 3,
2964
+ "assetId": "photo-4",
2965
+ "versionId": "original-v1",
2966
+ "width": 1600,
2967
+ "height": 1067
2968
+ }
2969
+ ],
2970
+ "logo": null,
2971
+ "music": null,
2972
+ "approvedCopy": {
2973
+ "title": "An object in four views",
2974
+ "captions": [
2975
+ "Front",
2976
+ "Side",
2977
+ "Texture",
2978
+ "Detail"
2979
+ ],
2980
+ "cta": "Explore the collection",
2981
+ "contact": "Example contact",
2982
+ "demoDisclosure": "Sample only · not a product offer"
2983
+ },
2984
+ "rights": {
2985
+ "userConfirmed": true,
2986
+ "independentlyVerified": false
2987
+ },
2988
+ "transformations": [
2989
+ "contain fit",
2990
+ "uniform scale",
2991
+ "at most three percent optical zoom"
2992
+ ],
2993
+ "generativeMedia": false,
2994
+ "providerCalls": 0
2995
+ }
2996
+ }
2997
+ }
2998
+ ],
2999
+ "portable": {
3000
+ "format": "stillmade-block/1",
3001
+ "dependencies": [],
3002
+ "sources": [
3003
+ {
3004
+ "kind": "original",
3005
+ "license": "MIT",
3006
+ "paths": [
3007
+ "LICENSE",
3008
+ "src/run.js"
3009
+ ],
3010
+ "evidence": [
3011
+ "upstream/0/LICENSE",
3012
+ "upstream/0/src/run.js"
3013
+ ],
3014
+ "changes": "Original implementation declared by the packager; no reused repository or dependency code."
3015
+ }
3016
+ ],
3017
+ "files": {
3018
+ "upstream/0/LICENSE": "MIT License\n\nCopyright (c) <year> <copyright holders>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and \nassociated documentation files (the \"Software\"), to deal in the Software without restriction, including \nwithout limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell \ncopies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the \nfollowing conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial \nportions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT \nLIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO \nEVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER \nIN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE \nUSE OR OTHER DEALINGS IN THE SOFTWARE.\n",
3019
+ "upstream/0/src/run.js": "// Photo Reel Composer 1.0.1. Runs only inside the isolated SDK JavaScript runtime.\n// Rights to supplied media are confirmed in the Content & IP Policy at sign-up.\n// References and supplied copy are data; this Block never reads media bytes,\n// calls a model, changes a photo, authorizes an export, or verifies legal rights.\nconst clone = value => JSON.parse(JSON.stringify(value));\nconst fail = (ok, message) => { if (!ok) throw new Error(message); };\nconst clean = (value, label, maximum, required = false) => {\n fail(value === undefined || typeof value === 'string', label + ' must be text.');\n const text = (value || '').trim();\n fail((!required || text.length > 0) && text.length <= maximum, label + ' must contain ' + (required ? '1' : '0') + ' to ' + maximum + ' characters.');\n fail(!/[\\u0000-\\u0008\\u000b\\u000c\\u000e-\\u001f]/.test(text), label + ' contains unsupported control characters.');\n fail(text.split(/\\s+/).every(word => word.length <= 28), label + ' contains a word or link too long to fit. Use a shorter display label.');\n return text;\n};\nconst purpose = input.purpose || 'property';\nfail(['property', 'product'].includes(purpose), 'Choose property or product.');\nconst assets = input.assets;\nconst minimum = purpose === 'property' ? 6 : 3;\nconst maximum = purpose === 'property' ? 8 : 10;\nfail(Array.isArray(assets) && assets.length >= minimum && assets.length <= maximum, 'Choose ' + minimum + ' to ' + maximum + ' original photos, in the order they should appear.');\nconst validMedia = (asset, kind, label) => {\n fail(asset && typeof asset === 'object' && !Array.isArray(asset) && asset.kind === kind, label + ' must be a ' + kind + ' reference.');\n fail(typeof asset.assetId === 'string' && asset.assetId.length > 0 && asset.assetId.length <= 240 && typeof asset.versionId === 'string' && asset.versionId.length > 0 && asset.versionId.length <= 240, label + ' needs its exact asset and version identities.');\n const mediaUrl = asset.url;\n const supportedUrl = typeof mediaUrl === 'string' && mediaUrl.length > 0 && mediaUrl.length <= 200000 && (\n /^\\/api\\/media\\/[A-Za-z0-9_-]+\\/[A-Za-z0-9_-][A-Za-z0-9._-]*$/.test(mediaUrl) ||\n /^(https:\\/\\/|http:\\/\\/(127\\.0\\.0\\.1|localhost)(:\\d+)?\\/|data:(image\\/(png|jpeg|webp)|audio\\/[a-z0-9.+-]+);base64,|blob:|sm-media:)/i.test(mediaUrl)\n ) && !/^https?:\\/\\/[^/?#]*@/i.test(mediaUrl);\n fail(supportedUrl, label + ' needs an authorized media reference.');\n if (kind === 'image') fail(Number.isFinite(asset.width) && asset.width > 0 && asset.width <= 40000 && Number.isFinite(asset.height) && asset.height > 0 && asset.height <= 40000, label + ' needs its measured original width and height. Upload this original photo again to record its dimensions.');\n return clone(asset);\n};\nconst photos = assets.map((asset, index) => validMedia(asset, 'image', 'Photo ' + (index + 1)));\nfail(new Set(photos.map(asset => asset.assetId + '\\n' + asset.versionId)).size === photos.length, 'Remove duplicate photo versions before composing.');\nconst title = clean(input.title, 'Approved title', 64, true);\nconst cta = clean(input.cta, 'Approved call to action', 56, true);\nconst contact = clean(input.contact, 'Contact or destination', 64);\nconst demoDisclosure = clean(input.demoDisclosure, 'Demo disclosure', 72);\nconst captionsText = input.captions || '';\nfail(typeof captionsText === 'string' && captionsText.length <= 1600, 'Use one short approved caption per photo.');\nconst captions = captionsText.trim() ? captionsText.replace(/\\r/g, '').split('\\n').map((line, index) => clean(line, 'Photo caption ' + (index + 1), 64)) : [];\nfail(captions.length === 0 || captions.length === photos.length, 'Supply exactly one caption line per photo, or leave captions empty. Blank lines keep a photo uncaptioned.');\n\nconst duration = input.duration === undefined ? 24 : input.duration;\nfail(Number.isFinite(duration) && duration >= 20 && duration <= 30, 'Choose a duration from 20 to 30 seconds.');\nconst aspect = input.aspect || '9:16';\nfail(['9:16', '16:9', '1:1'].includes(aspect), 'Choose 9:16, 16:9 or 1:1.');\nconst extra = (value, kind, label) => {\n fail(value === undefined || Array.isArray(value) && value.length <= 1, 'Choose at most one ' + label + '.');\n return value && value.length ? validMedia(value[0], kind, label) : null;\n};\nconst logo = extra(input.logo, 'image', 'logo');\nconst music = extra(input.music, 'audio', 'music track');\nif (music) fail(Number.isFinite(music.duration) && music.duration >= duration, 'The music track must have a measured duration covering the complete reel. Upload a supported MP3 or WAV again to record its duration, choose a longer track, or omit music.');\nconst [aw, ah] = aspect.split(':').map(Number);\nconst frameWidth = aw / ah * 369;\nconst portrait = aspect === '9:16';\nconst square = aspect === '1:1';\nconst backgroundColor = '#101714';\nconst foreground = '#F7F0DB';\nconst accent = '#CEB88B';\nconst overlays = [];\nconst clips = [];\nconst stickers = [];\nconst round = value => Math.round(value * 1000000) / 1000000;\nconst text = (id, value, start, end, y, size, color = foreground, width = .82) => {\n if (!value) return;\n // The shared renderer wraps between words only. Conservatively shrink a\n // single unusually long unbroken word; ordinary short-copy layouts stay put.\n const longest = Math.max(...value.split(/\\s+/).map(word => word.length));\n if (longest > 18) size = Math.min(size, (frameWidth * width - 12) / longest);\n overlays.push({ id, trackId: 'text', type: 'text', text: value, font: 'Hanken Grotesk', size, color, background: 'none', backgroundPadding: 0, width, x: 50, y, position: 'center', start: round(start), end: round(end), duration: round(end - start), animation: 'fade', animIn: .25, animOut: .2, rotation: 0 });\n};\nconst introEnd = 3.5;\nconst cardStart = duration - 4;\nconst sceneDuration = (cardStart - introEnd) / (photos.length - 1);\n// Fit the FULL measured photo inside a reserved rectangle, then add at most 3%\n// optical zoom. No crop, stretching, retouching, or synthetic pixels are requested.\nconst photoClip = (asset, index, start, length, endCard = false) => {\n const rawFit = Math.min(frameWidth / asset.width, 369 / asset.height);\n const fittedWidth = asset.width * rawFit;\n const fittedHeight = asset.height * rawFit;\n const regionWidth = frameWidth * (endCard ? .64 : .9);\n const regionHeight = 369 * (endCard ? .31 : portrait ? .48 : square ? .48 : .49);\n const scale = Math.min(regionWidth / fittedWidth, regionHeight / fittedHeight) / 1.03;\n const center = endCard ? .34 : .5;\n return {\n id: (endCard ? 'end-card-photo-' : 'photo-') + (index + 1), kind: 'image', trackId: 'video',\n asset: clone(asset), assetId: asset.assetId, versionId: asset.versionId, url: asset.url,\n title: asset.name || 'Photo ' + (index + 1), start: round(start), duration: round(length), trimStart: 0, speed: 1, volume: 0,\n transform: { fit: 'contain', scale, x: 0, y: (center - .5) * 369, rotation: 0, opacity: 1 },\n motion: { type: endCard ? 'none' : 'zoom', intensity: 100, zoomAmount: 1.03, zoomDirection: index % 2 ? 'out' : 'in', zoomAnchor: 'center', easing: 'ease-in-out', loop: false, ['window']: [0, 100] },\n transition: { type: endCard ? 'Cut' : 'Cross dissolve', duration: .3 },\n };\n};\nphotos.forEach((asset, index) => {\n const start = index === 0 ? 0 : introEnd + (index - 1) * sceneDuration;\n const end = index === 0 ? introEnd : index === photos.length - 1 ? cardStart : introEnd + index * sceneDuration;\n clips.push(photoClip(asset, index, start, end - start));\n text('photo-caption-' + (index + 1), captions[index], start + .2, end - .1, 79, portrait ? 11 : 14);\n text('photo-number-' + (index + 1), String(index + 1).padStart(2, '0') + ' / ' + String(photos.length).padStart(2, '0'), start, end - .01, 88, portrait ? 6.5 : 8, accent);\n});\nclips.push(photoClip(photos.at(-1), photos.length - 1, cardStart, 4, true));\n// The opening and closing compositions share an editorial rhythm while the\n// photographs remain full, unaltered, independently editable timeline items.\ntext('opening-title', title, 0, introEnd - .1, 18, portrait ? 16 : 22);\ntext('sequence-title', title, introEnd, cardStart, 16, portrait ? 10 : 14, foreground);\ntext('closing-title', title, cardStart, duration, 13, portrait ? 10 : 14, accent);\ntext('closing-cta', cta, cardStart + .1, duration, 65, portrait ? 17 : 23);\ntext('closing-contact', contact, cardStart + .25, duration, 79, portrait ? 9 : 12);\ntext('demo-disclosure', demoDisclosure, 0, duration, 94, portrait ? 6.5 : 8, '#D4D9D0', .84);\nstickers.push({ id: 'editorial-rule', trackId: 'overlay', shape: 'underline', color: accent, x: 50, y: 25, scale: .2, stretchX: 1.1, stretchY: .24, opacity: .85, rotation: 0, start: 0, end: cardStart });\nstickers.push({ id: 'closing-rule', trackId: 'overlay', shape: 'underline', color: accent, x: 50, y: 55, scale: .2, stretchX: 1.1, stretchY: .24, opacity: .85, rotation: 0, start: cardStart, end: duration });\nif (logo) {\n // Use an ordinary image clip on its own upper track. This avoids the GIF\n // sticker renderer's minimum-height distortion for unusually wide wordmarks.\n const fitted = Math.min(frameWidth / logo.width, 369 / logo.height);\n const scale = Math.min(frameWidth * .2 / (logo.width * fitted), 369 * .06 / (logo.height * fitted));\n clips.push({ id: 'brand-logo', kind: 'image', trackId: 'logo', asset: clone(logo), assetId: logo.assetId, versionId: logo.versionId, url: logo.url, start: cardStart, duration: 4, trimStart: 0, speed: 1, volume: 0, transform: { fit: 'contain', scale, x: 0, y: 369 * .39, rotation: 0, opacity: 1 }, motion: { type: 'none' }, transition: { type: 'Cut', duration: 0 } });\n}\nconst audioItems = music ? [{ id: 'music', trackId: 'audio', asset: clone(music), assetId: music.assetId, versionId: music.versionId, url: music.url, start: 0, duration, trimStart: 0, sourceOffset: 0, speed: 1, volume: 35, fadeIn: .5, fadeOut: .8 }] : [];\nconst provenance = {\n schemaVersion: 1, kind: 'photo-reel-source-record', block: { id: 'stillmade.photo-reel-composer', version: '1.0.0' }, purpose,\n sourcePhotos: photos.map((asset, index) => ({ index, assetId: asset.assetId, versionId: asset.versionId, width: asset.width, height: asset.height })),\n logo: logo ? { assetId: logo.assetId, versionId: logo.versionId } : null,\n music: music ? { assetId: music.assetId, versionId: music.versionId } : null,\n approvedCopy: { title, captions, cta, contact, demoDisclosure },\n rights: { userConfirmed: true, independentlyVerified: false },\n transformations: ['contain fit', 'uniform scale', 'at most three percent optical zoom'],\n generativeMedia: false, providerCalls: 0,\n};\nconst timeline = {\n schemaVersion: 1, activeTimelineId: 'photo-reel', name: title, aspect, aspectRatio: aspect, previewH: 369, backgroundColor,\n tracks: [{ id: 'video', type: 'video', label: 'Original photos' }, ...(logo ? [{ id: 'logo', type: 'video', label: 'Original logo' }] : []), { id: 'text', type: 'text', label: 'Approved copy' }, { id: 'overlay', type: 'overlay', label: 'Graphic details' }, { id: 'audio', type: 'audio', label: 'Music' }],\n trackOrder: ['video', 'logo', 'text', 'overlay', 'audio'], clips, audioItems, textOverlays: overlays, captions: [], stickers, effects: [],\n // The device renderer consumes these legacy aliases; Editor keeps the normal\n // editable lanes above. Both refer to the same content at creation time.\n audio: clone(audioItems), overlays: clone(overlays), targetDuration: duration,\n exportSettings: { aspect, fps: 30, resolution: '1080p', quality: 'high' },\n photoReel: clone(provenance),\n};\nconst lowResolution = photos.filter(asset => asset.width < 1080 || asset.height < 720).map(asset => asset.assetId);\nreturn {\n timeline,\n review: {\n schemaVersion: 1, state: 'needs-human-review', durationSec: duration, aspect, photoCount: photos.length,\n checks: [\n { id: 'original-source-versions', passed: true, note: 'Every photo retains its supplied asset/version and full source rectangle.' },\n { id: 'approved-copy-only', passed: true, note: 'No property features, prices, availability or product claims are generated.' },\n { id: 'bounded-local-composition', passed: true, note: 'No provider call or credit spend occurs in this Block.' },\n { id: 'complete-timing', passed: true, note: 'All source photos and a four-second closing card fit the requested duration.' },\n ],\n warnings: [\n 'Review the full export for photo quality, exact facts, readable copy, licensing and the displayed contact before delivering.',\n ...(!music ? ['Silent reel. Add rights-cleared music in this Block or the Editor if wanted.'] : []),\n ...(lowResolution.length ? ['Small source images may look soft in a 1080p export: ' + lowResolution.join(', ')] : []),\n ...(demoDisclosure ? ['This is labeled demonstration material and is not evidence of a real property or product offer.'] : []),\n ],\n humanApproved: false, exportAuthorized: false, cost: { providerCalls: 0, generationCredits: 0, note: 'Host storage, export access and any separately added services use their own policy.' },\n },\n provenance,\n};\n"
3020
+ },
3021
+ "lineage": {
3022
+ "parents": [],
3023
+ "changes": "Packaged original Block source for StillMade."
3024
+ }
3025
+ }
3026
+ };