{"schemaVersion":1,"id":"stillmade.property-listing-reel","version":"1.0.1","name":"Property Listing Reel","description":"Turn 6–8 approved original property photos, exact listing facts and your call to action into an editable 20–30 second vertical reel.","license":"MIT","startingChat":"native","summary":{"text":"Turn 6–8 approved original property photos, exact listing facts and your call to action into an editable 20–30 second vertical reel.","origin":"human","generation":"authored","status":"ready"},"defaults":{"aspectRatio":"9:16","brief":"Import 6–8 approved original property photos in the order you want them shown. In Compose Reel, enter the approved title, one exact factual caption per photo when needed, call to action and contact details, then confirm your rights to the supplied media. Review the editable 24-second portrait reel in Editor before exporting. Preserve the photographed property; do not invent rooms, fixtures, views, listing facts or prices."},"onboarding":[],"packages":[{"manifest":{"schemaVersion":1,"sdkVersion":"0.1.0","id":"sm.import-media","version":"2.0.2","name":"Import media","description":"Upload or choose images, video and audio, arrange the selection, and return original media for the next Block.","kind":"task","runtime":"javascript","entry":"src/run.js","license":"MIT","provenance":{"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","portableSources":[{"kind":"original","license":"MIT","paths":["LICENSE","src/run.js"],"evidence":["upstream/0/LICENSE","upstream/0/src/run.js"],"changes":"Original implementation declared by the packager; no reused repository or dependency code."}]},"inputs":{"files":{"type":"asset[]","primary":true,"default":[],"description":"Upload or select original media, then arrange its order.","semantic":"legacy.asset-list.files","required":false,"sources":["user","default"]},"include":{"type":"text","default":"All","semantic":"legacy.text.include","required":false,"description":"Select which media kind to include.","sources":["user","default"]},"duplicates":{"type":"text","default":"Keep all","semantic":"legacy.text.duplicates","required":false,"description":"Choose whether to remove exact duplicate references.","sources":["user","default"]}},"outputs":{"assets":{"type":"asset[]","primary":true,"semantic":"legacy.asset-list.assets","description":"Original media references in approved order, retaining exact source versions."}},"permissions":{"project":["asset.read"],"network":[],"filesystem":[],"secrets":[]},"ui":[{"port":"include","control":"select","options":["All","Images","Video","Audio"]},{"port":"duplicates","control":"select","options":["Keep all","Remove duplicates"]}]},"code":"function collect(input){\n if(input.files.length>64)throw Error('Import up to 64 files at a time.');\n const kind={All:null,Images:'image',Video:'video',Audio:'audio'}[input.include];\n if(kind===undefined)throw Error('Choose All, Images, Video or Audio.');\n if(!['Keep all','Remove duplicates'].includes(input.duplicates))throw Error('Choose a supported duplicate policy.');\n const seen=new Set(),assets=[];\n for(const file of input.files){\n  if(kind&&file.kind!==kind)continue;\n  const key=JSON.stringify([file.kind,file.assetId,file.versionId,file.url]);\n  if(input.duplicates==='Remove duplicates'&&seen.has(key))continue;\n  seen.add(key);assets.push({...file});\n }\n return {assets};\n}\nreturn collect(input);\n","tests":[{"name":"Keep an ordered original photo collection for connected review","input":{"files":[{"kind":"image","assetId":"photo-1","versionId":"original-v1","url":"https://media.example/photo-1.jpg","width":1600,"height":1067,"name":"Approved photo 1"},{"kind":"image","assetId":"photo-2","versionId":"original-v1","url":"https://media.example/photo-2.jpg","width":1600,"height":1067,"name":"Approved photo 2"},{"kind":"image","assetId":"photo-3","versionId":"original-v1","url":"https://media.example/photo-3.jpg","width":1600,"height":1067,"name":"Approved photo 3"},{"kind":"image","assetId":"photo-4","versionId":"original-v1","url":"https://media.example/photo-4.jpg","width":1600,"height":1067,"name":"Approved photo 4"},{"kind":"image","assetId":"photo-5","versionId":"original-v1","url":"https://media.example/photo-5.jpg","width":1600,"height":1067,"name":"Approved photo 5"},{"kind":"image","assetId":"photo-6","versionId":"original-v1","url":"https://media.example/photo-6.jpg","width":1600,"height":1067,"name":"Approved photo 6"}],"include":"Images","duplicates":"Remove duplicates"},"expected":{"assets":[{"kind":"image","assetId":"photo-1","versionId":"original-v1","url":"https://media.example/photo-1.jpg","width":1600,"height":1067,"name":"Approved photo 1"},{"kind":"image","assetId":"photo-2","versionId":"original-v1","url":"https://media.example/photo-2.jpg","width":1600,"height":1067,"name":"Approved photo 2"},{"kind":"image","assetId":"photo-3","versionId":"original-v1","url":"https://media.example/photo-3.jpg","width":1600,"height":1067,"name":"Approved photo 3"},{"kind":"image","assetId":"photo-4","versionId":"original-v1","url":"https://media.example/photo-4.jpg","width":1600,"height":1067,"name":"Approved photo 4"},{"kind":"image","assetId":"photo-5","versionId":"original-v1","url":"https://media.example/photo-5.jpg","width":1600,"height":1067,"name":"Approved photo 5"},{"kind":"image","assetId":"photo-6","versionId":"original-v1","url":"https://media.example/photo-6.jpg","width":1600,"height":1067,"name":"Approved photo 6"}]}},{"name":"Keep original media metadata and order","input":{"files":[{"kind":"image","assetId":"image","versionId":"original","url":"https://example.com/image.png","name":"Image","width":1920,"height":1080},{"kind":"audio","assetId":"audio","versionId":"original","url":"https://example.com/audio.mp3","name":"Audio","duration":4},{"kind":"video","assetId":"video","versionId":"original","url":"https://example.com/video.mp4","duration":3}],"include":"All","duplicates":"Keep all"},"expected":{"assets":[{"kind":"image","assetId":"image","versionId":"original","url":"https://example.com/image.png","name":"Image","width":1920,"height":1080},{"kind":"audio","assetId":"audio","versionId":"original","url":"https://example.com/audio.mp3","name":"Audio","duration":4},{"kind":"video","assetId":"video","versionId":"original","url":"https://example.com/video.mp4","duration":3}]}},{"name":"Filter and remove exact duplicate selections","input":{"files":[{"kind":"video","assetId":"video","versionId":"original","url":"https://example.com/video.mp4","duration":3},{"kind":"image","assetId":"image","versionId":"original","url":"https://example.com/image.png","name":"Image","width":1920,"height":1080},{"kind":"audio","assetId":"audio","versionId":"original","url":"https://example.com/audio.mp3","name":"Audio","duration":4},{"kind":"image","assetId":"image","versionId":"original","url":"https://example.com/image.png","name":"Image","width":1920,"height":1080}],"include":"Images","duplicates":"Remove duplicates"},"expected":{"assets":[{"kind":"image","assetId":"image","versionId":"original","url":"https://example.com/image.png","name":"Image","width":1920,"height":1080}]}},{"name":"Keep intentional repeated audio","input":{"files":[{"kind":"audio","assetId":"audio","versionId":"original","url":"https://example.com/audio.mp3","name":"Audio","duration":4},{"kind":"audio","assetId":"audio","versionId":"original","url":"https://example.com/audio.mp3","name":"Audio","duration":4}],"include":"Audio","duplicates":"Keep all"},"expected":{"assets":[{"kind":"audio","assetId":"audio","versionId":"original","url":"https://example.com/audio.mp3","name":"Audio","duration":4},{"kind":"audio","assetId":"audio","versionId":"original","url":"https://example.com/audio.mp3","name":"Audio","duration":4}]}},{"name":"Empty selection","input":{"files":[],"include":"All","duplicates":"Keep all"},"expected":{"assets":[]}}],"portable":{"format":"stillmade-block/1","dependencies":[],"sources":[{"kind":"original","license":"MIT","paths":["LICENSE","src/run.js"],"evidence":["upstream/0/LICENSE","upstream/0/src/run.js"],"changes":"Original implementation declared by the packager; no reused repository or dependency code."}],"files":{"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","upstream/0/src/run.js":"function collect(input){\n if(input.files.length>64)throw Error('Import up to 64 files at a time.');\n const kind={All:null,Images:'image',Video:'video',Audio:'audio'}[input.include];\n if(kind===undefined)throw Error('Choose All, Images, Video or Audio.');\n if(!['Keep all','Remove duplicates'].includes(input.duplicates))throw Error('Choose a supported duplicate policy.');\n const seen=new Set(),assets=[];\n for(const file of input.files){\n  if(kind&&file.kind!==kind)continue;\n  const key=JSON.stringify([file.kind,file.assetId,file.versionId,file.url]);\n  if(input.duplicates==='Remove duplicates'&&seen.has(key))continue;\n  seen.add(key);assets.push({...file});\n }\n return {assets};\n}\nreturn collect(input);\n"},"lineage":{"parents":[],"changes":"Packaged original Block source for StillMade."}}},{"manifest":{"schemaVersion":1,"sdkVersion":"0.1.0","id":"stillmade.photo-reel-composer","version":"1.0.0","name":"Photo Reel Composer","kind":"task","runtime":"javascript","entry":"src/run.js","license":"MIT","description":"Arrange original property or product photos and approved copy into a restrained, editable 20–30 second reel. No generated imagery or model calls.","inputs":{"assets":{"type":"asset[]","primary":true,"default":[],"description":"Ordered original photo references. Property: 6–8 photos. Product: 3–10. Exact versions and measured dimensions are required.","semantic":"legacy.asset-list.assets","required":true,"sources":["upstream","user"]},"purpose":{"type":"text","default":"property","description":"Property or product photo reel.","semantic":"stillmade.photo_reel.purpose","required":false,"sources":["user","default"]},"title":{"type":"text","default":"","description":"Approved title, up to 64 characters. Use only facts supplied and reviewed by the customer.","semantic":"stillmade.photo_reel.title","required":true,"sources":["user","default"]},"captions":{"type":"text","default":"","description":"One approved short caption per photo, one line each, up to 64 characters per line. Leave all empty for an uncaptioned sequence.","semantic":"stillmade.photo_reel.captions","required":false,"sources":["user","default"]},"cta":{"type":"text","default":"","description":"Approved call to action, up to 56 characters.","semantic":"stillmade.photo_reel.cta","required":true,"sources":["user","default"]},"contact":{"type":"text","default":"","description":"Exact contact or short destination to display, up to 64 characters.","semantic":"stillmade.photo_reel.contact","required":false,"sources":["user","default"]},"demoDisclosure":{"type":"text","default":"","description":"Optional exact disclosure shown throughout, such as Stock-photo demonstration · not a live listing.","semantic":"stillmade.photo_reel.demodisclosure","required":false,"sources":["user","default"]},"duration":{"type":"number","default":24,"min":20,"max":30,"description":"Total reel duration in seconds, including the four-second closing card.","semantic":"stillmade.photo_reel.duration","required":false,"sources":["user","default"]},"aspect":{"type":"text","default":"9:16","description":"Output frame shape; photos always retain their full original rectangle.","semantic":"stillmade.photo_reel.aspect","required":false,"sources":["user","default"]},"logo":{"type":"asset[]","default":[],"description":"Optional single original logo image with exact version and measured dimensions.","semantic":"stillmade.photo_reel.logo","required":false,"sources":["user","default"]},"music":{"type":"asset[]","default":[],"description":"Optional single licensed audio track with measured duration covering the reel. Otherwise output is silent.","semantic":"stillmade.photo_reel.music","required":false,"sources":["user","default"]},"rightsConfirmed":{"type":"boolean","default":false,"description":"I have permission to use the supplied photos, logo, music, and exact statements. This is my confirmation, not independent legal verification.","semantic":"stillmade.photo_reel.rightsconfirmed","required":true,"sources":["user"]}},"outputs":{"timeline":{"type":"timeline","primary":true,"semantic":"legacy.timeline.updatededitor","description":"Editable complete Editor timeline containing original photo clips, approved text, optional music and logo."},"review":{"type":"json","description":"Structural checks and remaining human review. Does not authorize export or certify claims or rights.","semantic":"stillmade.photo_reel.review","schema":{"schemaVersion":"integer","state":"string","durationSec":"number","aspect":"string","photoCount":"integer","checks":[{"id":"string","passed":"boolean","note":"string"}],"warnings":"string[]","humanApproved":"boolean","exportAuthorized":"boolean","cost":{"providerCalls":"integer","generationCredits":"integer","note":"string"}}},"provenance":{"type":"json","description":"Versioned source references, supplied copy and bounded transformation record.","semantic":"stillmade.photo_reel.provenance","schema":{"schemaVersion":"integer","kind":"string","block":{"id":"string","version":"string"},"purpose":"string","sourcePhotos":[{"index":"integer","assetId":"string","versionId":"string","width":"number","height":"number"}],"logo":"json","music":"json","approvedCopy":{"title":"string","captions":"string[]","cta":"string","contact":"string","demoDisclosure":"string"},"rights":{"userConfirmed":"boolean","independentlyVerified":"boolean"},"transformations":"string[]","generativeMedia":"boolean","providerCalls":"integer"}}},"permissions":{"project":["asset.read"],"network":[],"filesystem":[],"secrets":[]},"ui":[{"port":"purpose","control":"select","options":["property","product"]},{"port":"aspect","control":"select","options":["9:16","16:9","1:1"]},{"port":"duration","control":"slider","min":20,"max":30,"step":1}],"platforms":{"phone":{"supported":true},"browser":{"supported":true},"desktop":{"supported":true}},"provenance":{"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","portableSources":[{"kind":"original","license":"MIT","paths":["LICENSE","src/run.js"],"evidence":["upstream/0/LICENSE","upstream/0/src/run.js"],"changes":"Original implementation declared by the packager; no reused repository or dependency code."}]},"semantics":[{"name":"stillmade.photo_reel.purpose","description":"Property or product photo reel.","parents":[]},{"name":"stillmade.photo_reel.title","description":"Approved title, up to 64 characters. Use only facts supplied and reviewed by the customer.","parents":[]},{"name":"stillmade.photo_reel.captions","description":"One approved short caption per photo, one line each, up to 64 characters per line. Leave all empty for an uncaptioned sequence.","parents":[]},{"name":"stillmade.photo_reel.cta","description":"Approved call to action, up to 56 characters.","parents":[]},{"name":"stillmade.photo_reel.contact","description":"Exact contact or short destination to display, up to 64 characters.","parents":[]},{"name":"stillmade.photo_reel.demodisclosure","description":"Optional exact disclosure shown throughout, such as Stock-photo demonstration · not a live listing.","parents":[]},{"name":"stillmade.photo_reel.duration","description":"Total reel duration in seconds, including the four-second closing card.","parents":[]},{"name":"stillmade.photo_reel.aspect","description":"Output frame shape; photos always retain their full original rectangle.","parents":[]},{"name":"stillmade.photo_reel.logo","description":"Optional single original logo image with exact version and measured dimensions.","parents":[]},{"name":"stillmade.photo_reel.music","description":"Optional single licensed audio track with measured duration covering the reel. Otherwise output is silent.","parents":[]},{"name":"stillmade.photo_reel.rightsconfirmed","description":"I have permission to use the supplied photos, logo, music, and exact statements. This is my confirmation, not independent legal verification.","parents":[]},{"name":"stillmade.photo_reel.review","description":"Structural checks and remaining human review. Does not authorize export or certify claims or rights.","parents":[]},{"name":"stillmade.photo_reel.provenance","description":"Versioned source references, supplied copy and bounded transformation record.","parents":[]}]},"code":"// Photo Reel Composer 1.0.0. Runs only inside the isolated SDK JavaScript runtime.\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.');\nfail(input.rightsConfirmed === true, 'Confirm that you may use these photos, logo, music and exact supplied statements before building.');\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","tests":[{"name":"Compose exact original property photos and approved copy","input":{"assets":[{"kind":"image","assetId":"photo-1","versionId":"original-v1","url":"https://media.example/photo-1.jpg","width":1600,"height":1067,"name":"Approved photo 1"},{"kind":"image","assetId":"photo-2","versionId":"original-v1","url":"https://media.example/photo-2.jpg","width":1600,"height":1067,"name":"Approved photo 2"},{"kind":"image","assetId":"photo-3","versionId":"original-v1","url":"https://media.example/photo-3.jpg","width":1600,"height":1067,"name":"Approved photo 3"},{"kind":"image","assetId":"photo-4","versionId":"original-v1","url":"https://media.example/photo-4.jpg","width":1600,"height":1067,"name":"Approved photo 4"},{"kind":"image","assetId":"photo-5","versionId":"original-v1","url":"https://media.example/photo-5.jpg","width":1600,"height":1067,"name":"Approved photo 5"},{"kind":"image","assetId":"photo-6","versionId":"original-v1","url":"https://media.example/photo-6.jpg","width":1600,"height":1067,"name":"Approved photo 6"}],"purpose":"property","title":"An interior photo collection","captions":"Entry\nLiving area\nDining area\nKitchen\nBedroom\nDetail","cta":"Ask for the details","contact":"Example contact","demoDisclosure":"Sample only · not a live listing","duration":24,"aspect":"9:16","logo":[],"music":[],"rightsConfirmed":true},"expected":{"timeline":{"schemaVersion":1,"activeTimelineId":"photo-reel","name":"An interior photo collection","aspect":"9:16","aspectRatio":"9:16","previewH":369,"backgroundColor":"#101714","tracks":[{"id":"video","type":"video","label":"Original photos"},{"id":"text","type":"text","label":"Approved copy"},{"id":"overlay","type":"overlay","label":"Graphic details"},{"id":"audio","type":"audio","label":"Music"}],"trackOrder":["video","logo","text","overlay","audio"],"clips":[{"id":"photo-1","kind":"image","trackId":"video","asset":{"kind":"image","assetId":"photo-1","versionId":"original-v1","url":"https://media.example/photo-1.jpg","width":1600,"height":1067,"name":"Approved photo 1"},"assetId":"photo-1","versionId":"original-v1","url":"https://media.example/photo-1.jpg","title":"Approved photo 1","start":0,"duration":3.5,"trimStart":0,"speed":1,"volume":0,"transform":{"fit":"contain","scale":0.8737864077669902,"x":0,"y":0,"rotation":0,"opacity":1},"motion":{"type":"zoom","intensity":100,"zoomAmount":1.03,"zoomDirection":"in","zoomAnchor":"center","easing":"ease-in-out","loop":false,"window":[0,100]},"transition":{"type":"Cross dissolve","duration":0.3}},{"id":"photo-2","kind":"image","trackId":"video","asset":{"kind":"image","assetId":"photo-2","versionId":"original-v1","url":"https://media.example/photo-2.jpg","width":1600,"height":1067,"name":"Approved photo 2"},"assetId":"photo-2","versionId":"original-v1","url":"https://media.example/photo-2.jpg","title":"Approved photo 2","start":3.5,"duration":3.3,"trimStart":0,"speed":1,"volume":0,"transform":{"fit":"contain","scale":0.8737864077669902,"x":0,"y":0,"rotation":0,"opacity":1},"motion":{"type":"zoom","intensity":100,"zoomAmount":1.03,"zoomDirection":"out","zoomAnchor":"center","easing":"ease-in-out","loop":false,"window":[0,100]},"transition":{"type":"Cross dissolve","duration":0.3}},{"id":"photo-3","kind":"image","trackId":"video","asset":{"kind":"image","assetId":"photo-3","versionId":"original-v1","url":"https://media.example/photo-3.jpg","width":1600,"height":1067,"name":"Approved photo 3"},"assetId":"photo-3","versionId":"original-v1","url":"https://media.example/photo-3.jpg","title":"Approved photo 3","start":6.8,"duration":3.3,"trimStart":0,"speed":1,"volume":0,"transform":{"fit":"contain","scale":0.8737864077669902,"x":0,"y":0,"rotation":0,"opacity":1},"motion":{"type":"zoom","intensity":100,"zoomAmount":1.03,"zoomDirection":"in","zoomAnchor":"center","easing":"ease-in-out","loop":false,"window":[0,100]},"transition":{"type":"Cross dissolve","duration":0.3}},{"id":"photo-4","kind":"image","trackId":"video","asset":{"kind":"image","assetId":"photo-4","versionId":"original-v1","url":"https://media.example/photo-4.jpg","width":1600,"height":1067,"name":"Approved photo 4"},"assetId":"photo-4","versionId":"original-v1","url":"https://media.example/photo-4.jpg","title":"Approved photo 4","start":10.1,"duration":3.3,"trimStart":0,"speed":1,"volume":0,"transform":{"fit":"contain","scale":0.8737864077669902,"x":0,"y":0,"rotation":0,"opacity":1},"motion":{"type":"zoom","intensity":100,"zoomAmount":1.03,"zoomDirection":"out","zoomAnchor":"center","easing":"ease-in-out","loop":false,"window":[0,100]},"transition":{"type":"Cross dissolve","duration":0.3}},{"id":"photo-5","kind":"image","trackId":"video","asset":{"kind":"image","assetId":"photo-5","versionId":"original-v1","url":"https://media.example/photo-5.jpg","width":1600,"height":1067,"name":"Approved photo 5"},"assetId":"photo-5","versionId":"original-v1","url":"https://media.example/photo-5.jpg","title":"Approved photo 5","start":13.4,"duration":3.3,"trimStart":0,"speed":1,"volume":0,"transform":{"fit":"contain","scale":0.8737864077669902,"x":0,"y":0,"rotation":0,"opacity":1},"motion":{"type":"zoom","intensity":100,"zoomAmount":1.03,"zoomDirection":"in","zoomAnchor":"center","easing":"ease-in-out","loop":false,"window":[0,100]},"transition":{"type":"Cross dissolve","duration":0.3}},{"id":"photo-6","kind":"image","trackId":"video","asset":{"kind":"image","assetId":"photo-6","versionId":"original-v1","url":"https://media.example/photo-6.jpg","width":1600,"height":1067,"name":"Approved photo 6"},"assetId":"photo-6","versionId":"original-v1","url":"https://media.example/photo-6.jpg","title":"Approved photo 6","start":16.7,"duration":3.3,"trimStart":0,"speed":1,"volume":0,"transform":{"fit":"contain","scale":0.8737864077669902,"x":0,"y":0,"rotation":0,"opacity":1},"motion":{"type":"zoom","intensity":100,"zoomAmount":1.03,"zoomDirection":"out","zoomAnchor":"center","easing":"ease-in-out","loop":false,"window":[0,100]},"transition":{"type":"Cross dissolve","duration":0.3}},{"id":"end-card-photo-6","kind":"image","trackId":"video","asset":{"kind":"image","assetId":"photo-6","versionId":"original-v1","url":"https://media.example/photo-6.jpg","width":1600,"height":1067,"name":"Approved photo 6"},"assetId":"photo-6","versionId":"original-v1","url":"https://media.example/photo-6.jpg","title":"Approved photo 6","start":20,"duration":4,"trimStart":0,"speed":1,"volume":0,"transform":{"fit":"contain","scale":0.6213592233009708,"x":0,"y":-59.03999999999999,"rotation":0,"opacity":1},"motion":{"type":"none","intensity":100,"zoomAmount":1.03,"zoomDirection":"out","zoomAnchor":"center","easing":"ease-in-out","loop":false,"window":[0,100]},"transition":{"type":"Cut","duration":0.3}}],"audioItems":[],"textOverlays":[{"id":"photo-caption-1","trackId":"text","type":"text","text":"Entry","font":"Hanken Grotesk","size":11,"color":"#F7F0DB","background":"none","backgroundPadding":0,"width":0.82,"x":50,"y":79,"position":"center","start":0.2,"end":3.4,"duration":3.2,"animation":"fade","animIn":0.25,"animOut":0.2,"rotation":0},{"id":"photo-number-1","trackId":"text","type":"text","text":"01 / 06","font":"Hanken Grotesk","size":6.5,"color":"#CEB88B","background":"none","backgroundPadding":0,"width":0.82,"x":50,"y":88,"position":"center","start":0,"end":3.49,"duration":3.49,"animation":"fade","animIn":0.25,"animOut":0.2,"rotation":0},{"id":"photo-caption-2","trackId":"text","type":"text","text":"Living area","font":"Hanken Grotesk","size":11,"color":"#F7F0DB","background":"none","backgroundPadding":0,"width":0.82,"x":50,"y":79,"position":"center","start":3.7,"end":6.7,"duration":3,"animation":"fade","animIn":0.25,"animOut":0.2,"rotation":0},{"id":"photo-number-2","trackId":"text","type":"text","text":"02 / 06","font":"Hanken Grotesk","size":6.5,"color":"#CEB88B","background":"none","backgroundPadding":0,"width":0.82,"x":50,"y":88,"position":"center","start":3.5,"end":6.79,"duration":3.29,"animation":"fade","animIn":0.25,"animOut":0.2,"rotation":0},{"id":"photo-caption-3","trackId":"text","type":"text","text":"Dining area","font":"Hanken Grotesk","size":11,"color":"#F7F0DB","background":"none","backgroundPadding":0,"width":0.82,"x":50,"y":79,"position":"center","start":7,"end":10,"duration":3,"animation":"fade","animIn":0.25,"animOut":0.2,"rotation":0},{"id":"photo-number-3","trackId":"text","type":"text","text":"03 / 06","font":"Hanken Grotesk","size":6.5,"color":"#CEB88B","background":"none","backgroundPadding":0,"width":0.82,"x":50,"y":88,"position":"center","start":6.8,"end":10.09,"duration":3.29,"animation":"fade","animIn":0.25,"animOut":0.2,"rotation":0},{"id":"photo-caption-4","trackId":"text","type":"text","text":"Kitchen","font":"Hanken Grotesk","size":11,"color":"#F7F0DB","background":"none","backgroundPadding":0,"width":0.82,"x":50,"y":79,"position":"center","start":10.3,"end":13.3,"duration":3,"animation":"fade","animIn":0.25,"animOut":0.2,"rotation":0},{"id":"photo-number-4","trackId":"text","type":"text","text":"04 / 06","font":"Hanken Grotesk","size":6.5,"color":"#CEB88B","background":"none","backgroundPadding":0,"width":0.82,"x":50,"y":88,"position":"center","start":10.1,"end":13.39,"duration":3.29,"animation":"fade","animIn":0.25,"animOut":0.2,"rotation":0},{"id":"photo-caption-5","trackId":"text","type":"text","text":"Bedroom","font":"Hanken Grotesk","size":11,"color":"#F7F0DB","background":"none","backgroundPadding":0,"width":0.82,"x":50,"y":79,"position":"center","start":13.6,"end":16.6,"duration":3,"animation":"fade","animIn":0.25,"animOut":0.2,"rotation":0},{"id":"photo-number-5","trackId":"text","type":"text","text":"05 / 06","font":"Hanken Grotesk","size":6.5,"color":"#CEB88B","background":"none","backgroundPadding":0,"width":0.82,"x":50,"y":88,"position":"center","start":13.4,"end":16.69,"duration":3.29,"animation":"fade","animIn":0.25,"animOut":0.2,"rotation":0},{"id":"photo-caption-6","trackId":"text","type":"text","text":"Detail","font":"Hanken Grotesk","size":11,"color":"#F7F0DB","background":"none","backgroundPadding":0,"width":0.82,"x":50,"y":79,"position":"center","start":16.9,"end":19.9,"duration":3,"animation":"fade","animIn":0.25,"animOut":0.2,"rotation":0},{"id":"photo-number-6","trackId":"text","type":"text","text":"06 / 06","font":"Hanken Grotesk","size":6.5,"color":"#CEB88B","background":"none","backgroundPadding":0,"width":0.82,"x":50,"y":88,"position":"center","start":16.7,"end":19.99,"duration":3.29,"animation":"fade","animIn":0.25,"animOut":0.2,"rotation":0},{"id":"opening-title","trackId":"text","type":"text","text":"An interior photo collection","font":"Hanken Grotesk","size":16,"color":"#F7F0DB","background":"none","backgroundPadding":0,"width":0.82,"x":50,"y":18,"position":"center","start":0,"end":3.4,"duration":3.4,"animation":"fade","animIn":0.25,"animOut":0.2,"rotation":0},{"id":"sequence-title","trackId":"text","type":"text","text":"An interior photo collection","font":"Hanken Grotesk","size":10,"color":"#F7F0DB","background":"none","backgroundPadding":0,"width":0.82,"x":50,"y":16,"position":"center","start":3.5,"end":20,"duration":16.5,"animation":"fade","animIn":0.25,"animOut":0.2,"rotation":0},{"id":"closing-title","trackId":"text","type":"text","text":"An interior photo collection","font":"Hanken Grotesk","size":10,"color":"#CEB88B","background":"none","backgroundPadding":0,"width":0.82,"x":50,"y":13,"position":"center","start":20,"end":24,"duration":4,"animation":"fade","animIn":0.25,"animOut":0.2,"rotation":0},{"id":"closing-cta","trackId":"text","type":"text","text":"Ask for the details","font":"Hanken Grotesk","size":17,"color":"#F7F0DB","background":"none","backgroundPadding":0,"width":0.82,"x":50,"y":65,"position":"center","start":20.1,"end":24,"duration":3.9,"animation":"fade","animIn":0.25,"animOut":0.2,"rotation":0},{"id":"closing-contact","trackId":"text","type":"text","text":"Example contact","font":"Hanken Grotesk","size":9,"color":"#F7F0DB","background":"none","backgroundPadding":0,"width":0.82,"x":50,"y":79,"position":"center","start":20.25,"end":24,"duration":3.75,"animation":"fade","animIn":0.25,"animOut":0.2,"rotation":0},{"id":"demo-disclosure","trackId":"text","type":"text","text":"Sample only · not a live listing","font":"Hanken Grotesk","size":6.5,"color":"#D4D9D0","background":"none","backgroundPadding":0,"width":0.84,"x":50,"y":94,"position":"center","start":0,"end":24,"duration":24,"animation":"fade","animIn":0.25,"animOut":0.2,"rotation":0}],"captions":[],"stickers":[{"id":"editorial-rule","trackId":"overlay","shape":"underline","color":"#CEB88B","x":50,"y":25,"scale":0.2,"stretchX":1.1,"stretchY":0.24,"opacity":0.85,"rotation":0,"start":0,"end":20},{"id":"closing-rule","trackId":"overlay","shape":"underline","color":"#CEB88B","x":50,"y":55,"scale":0.2,"stretchX":1.1,"stretchY":0.24,"opacity":0.85,"rotation":0,"start":20,"end":24}],"effects":[],"audio":[],"overlays":[{"id":"photo-caption-1","trackId":"text","type":"text","text":"Entry","font":"Hanken Grotesk","size":11,"color":"#F7F0DB","background":"none","backgroundPadding":0,"width":0.82,"x":50,"y":79,"position":"center","start":0.2,"end":3.4,"duration":3.2,"animation":"fade","animIn":0.25,"animOut":0.2,"rotation":0},{"id":"photo-number-1","trackId":"text","type":"text","text":"01 / 06","font":"Hanken Grotesk","size":6.5,"color":"#CEB88B","background":"none","backgroundPadding":0,"width":0.82,"x":50,"y":88,"position":"center","start":0,"end":3.49,"duration":3.49,"animation":"fade","animIn":0.25,"animOut":0.2,"rotation":0},{"id":"photo-caption-2","trackId":"text","type":"text","text":"Living area","font":"Hanken Grotesk","size":11,"color":"#F7F0DB","background":"none","backgroundPadding":0,"width":0.82,"x":50,"y":79,"position":"center","start":3.7,"end":6.7,"duration":3,"animation":"fade","animIn":0.25,"animOut":0.2,"rotation":0},{"id":"photo-number-2","trackId":"text","type":"text","text":"02 / 06","font":"Hanken Grotesk","size":6.5,"color":"#CEB88B","background":"none","backgroundPadding":0,"width":0.82,"x":50,"y":88,"position":"center","start":3.5,"end":6.79,"duration":3.29,"animation":"fade","animIn":0.25,"animOut":0.2,"rotation":0},{"id":"photo-caption-3","trackId":"text","type":"text","text":"Dining area","font":"Hanken Grotesk","size":11,"color":"#F7F0DB","background":"none","backgroundPadding":0,"width":0.82,"x":50,"y":79,"position":"center","start":7,"end":10,"duration":3,"animation":"fade","animIn":0.25,"animOut":0.2,"rotation":0},{"id":"photo-number-3","trackId":"text","type":"text","text":"03 / 06","font":"Hanken Grotesk","size":6.5,"color":"#CEB88B","background":"none","backgroundPadding":0,"width":0.82,"x":50,"y":88,"position":"center","start":6.8,"end":10.09,"duration":3.29,"animation":"fade","animIn":0.25,"animOut":0.2,"rotation":0},{"id":"photo-caption-4","trackId":"text","type":"text","text":"Kitchen","font":"Hanken Grotesk","size":11,"color":"#F7F0DB","background":"none","backgroundPadding":0,"width":0.82,"x":50,"y":79,"position":"center","start":10.3,"end":13.3,"duration":3,"animation":"fade","animIn":0.25,"animOut":0.2,"rotation":0},{"id":"photo-number-4","trackId":"text","type":"text","text":"04 / 06","font":"Hanken Grotesk","size":6.5,"color":"#CEB88B","background":"none","backgroundPadding":0,"width":0.82,"x":50,"y":88,"position":"center","start":10.1,"end":13.39,"duration":3.29,"animation":"fade","animIn":0.25,"animOut":0.2,"rotation":0},{"id":"photo-caption-5","trackId":"text","type":"text","text":"Bedroom","font":"Hanken Grotesk","size":11,"color":"#F7F0DB","background":"none","backgroundPadding":0,"width":0.82,"x":50,"y":79,"position":"center","start":13.6,"end":16.6,"duration":3,"animation":"fade","animIn":0.25,"animOut":0.2,"rotation":0},{"id":"photo-number-5","trackId":"text","type":"text","text":"05 / 06","font":"Hanken Grotesk","size":6.5,"color":"#CEB88B","background":"none","backgroundPadding":0,"width":0.82,"x":50,"y":88,"position":"center","start":13.4,"end":16.69,"duration":3.29,"animation":"fade","animIn":0.25,"animOut":0.2,"rotation":0},{"id":"photo-caption-6","trackId":"text","type":"text","text":"Detail","font":"Hanken Grotesk","size":11,"color":"#F7F0DB","background":"none","backgroundPadding":0,"width":0.82,"x":50,"y":79,"position":"center","start":16.9,"end":19.9,"duration":3,"animation":"fade","animIn":0.25,"animOut":0.2,"rotation":0},{"id":"photo-number-6","trackId":"text","type":"text","text":"06 / 06","font":"Hanken Grotesk","size":6.5,"color":"#CEB88B","background":"none","backgroundPadding":0,"width":0.82,"x":50,"y":88,"position":"center","start":16.7,"end":19.99,"duration":3.29,"animation":"fade","animIn":0.25,"animOut":0.2,"rotation":0},{"id":"opening-title","trackId":"text","type":"text","text":"An interior photo collection","font":"Hanken Grotesk","size":16,"color":"#F7F0DB","background":"none","backgroundPadding":0,"width":0.82,"x":50,"y":18,"position":"center","start":0,"end":3.4,"duration":3.4,"animation":"fade","animIn":0.25,"animOut":0.2,"rotation":0},{"id":"sequence-title","trackId":"text","type":"text","text":"An interior photo collection","font":"Hanken Grotesk","size":10,"color":"#F7F0DB","background":"none","backgroundPadding":0,"width":0.82,"x":50,"y":16,"position":"center","start":3.5,"end":20,"duration":16.5,"animation":"fade","animIn":0.25,"animOut":0.2,"rotation":0},{"id":"closing-title","trackId":"text","type":"text","text":"An interior photo collection","font":"Hanken Grotesk","size":10,"color":"#CEB88B","background":"none","backgroundPadding":0,"width":0.82,"x":50,"y":13,"position":"center","start":20,"end":24,"duration":4,"animation":"fade","animIn":0.25,"animOut":0.2,"rotation":0},{"id":"closing-cta","trackId":"text","type":"text","text":"Ask for the details","font":"Hanken Grotesk","size":17,"color":"#F7F0DB","background":"none","backgroundPadding":0,"width":0.82,"x":50,"y":65,"position":"center","start":20.1,"end":24,"duration":3.9,"animation":"fade","animIn":0.25,"animOut":0.2,"rotation":0},{"id":"closing-contact","trackId":"text","type":"text","text":"Example contact","font":"Hanken Grotesk","size":9,"color":"#F7F0DB","background":"none","backgroundPadding":0,"width":0.82,"x":50,"y":79,"position":"center","start":20.25,"end":24,"duration":3.75,"animation":"fade","animIn":0.25,"animOut":0.2,"rotation":0},{"id":"demo-disclosure","trackId":"text","type":"text","text":"Sample only · not a live listing","font":"Hanken Grotesk","size":6.5,"color":"#D4D9D0","background":"none","backgroundPadding":0,"width":0.84,"x":50,"y":94,"position":"center","start":0,"end":24,"duration":24,"animation":"fade","animIn":0.25,"animOut":0.2,"rotation":0}],"targetDuration":24,"exportSettings":{"aspect":"9:16","fps":30,"resolution":"1080p","quality":"high"},"photoReel":{"schemaVersion":1,"kind":"photo-reel-source-record","block":{"id":"stillmade.photo-reel-composer","version":"1.0.0"},"purpose":"property","sourcePhotos":[{"index":0,"assetId":"photo-1","versionId":"original-v1","width":1600,"height":1067},{"index":1,"assetId":"photo-2","versionId":"original-v1","width":1600,"height":1067},{"index":2,"assetId":"photo-3","versionId":"original-v1","width":1600,"height":1067},{"index":3,"assetId":"photo-4","versionId":"original-v1","width":1600,"height":1067},{"index":4,"assetId":"photo-5","versionId":"original-v1","width":1600,"height":1067},{"index":5,"assetId":"photo-6","versionId":"original-v1","width":1600,"height":1067}],"logo":null,"music":null,"approvedCopy":{"title":"An interior photo collection","captions":["Entry","Living area","Dining area","Kitchen","Bedroom","Detail"],"cta":"Ask for the details","contact":"Example contact","demoDisclosure":"Sample only · not a live listing"},"rights":{"userConfirmed":true,"independentlyVerified":false},"transformations":["contain fit","uniform scale","at most three percent optical zoom"],"generativeMedia":false,"providerCalls":0}},"review":{"schemaVersion":1,"state":"needs-human-review","durationSec":24,"aspect":"9:16","photoCount":6,"checks":[{"id":"original-source-versions","passed":true,"note":"Every photo retains its supplied asset/version and full source rectangle."},{"id":"approved-copy-only","passed":true,"note":"No property features, prices, availability or product claims are generated."},{"id":"bounded-local-composition","passed":true,"note":"No provider call or credit spend occurs in this Block."},{"id":"complete-timing","passed":true,"note":"All source photos and a four-second closing card fit the requested duration."}],"warnings":["Review the full export for photo quality, exact facts, readable copy, licensing and the displayed contact before delivering.","Silent reel. Add rights-cleared music in this Block or the Editor if wanted.","This is labeled demonstration material and is not evidence of a real property or product offer."],"humanApproved":false,"exportAuthorized":false,"cost":{"providerCalls":0,"generationCredits":0,"note":"Host storage, export access and any separately added services use their own policy."}},"provenance":{"schemaVersion":1,"kind":"photo-reel-source-record","block":{"id":"stillmade.photo-reel-composer","version":"1.0.0"},"purpose":"property","sourcePhotos":[{"index":0,"assetId":"photo-1","versionId":"original-v1","width":1600,"height":1067},{"index":1,"assetId":"photo-2","versionId":"original-v1","width":1600,"height":1067},{"index":2,"assetId":"photo-3","versionId":"original-v1","width":1600,"height":1067},{"index":3,"assetId":"photo-4","versionId":"original-v1","width":1600,"height":1067},{"index":4,"assetId":"photo-5","versionId":"original-v1","width":1600,"height":1067},{"index":5,"assetId":"photo-6","versionId":"original-v1","width":1600,"height":1067}],"logo":null,"music":null,"approvedCopy":{"title":"An interior photo collection","captions":["Entry","Living area","Dining area","Kitchen","Bedroom","Detail"],"cta":"Ask for the details","contact":"Example contact","demoDisclosure":"Sample only · not a live listing"},"rights":{"userConfirmed":true,"independentlyVerified":false},"transformations":["contain fit","uniform scale","at most three percent optical zoom"],"generativeMedia":false,"providerCalls":0}}},{"name":"Compose a product reel without inventing product claims","input":{"assets":[{"kind":"image","assetId":"photo-1","versionId":"original-v1","url":"https://media.example/photo-1.jpg","width":1600,"height":1067,"name":"Approved photo 1"},{"kind":"image","assetId":"photo-2","versionId":"original-v1","url":"https://media.example/photo-2.jpg","width":1600,"height":1067,"name":"Approved photo 2"},{"kind":"image","assetId":"photo-3","versionId":"original-v1","url":"https://media.example/photo-3.jpg","width":1600,"height":1067,"name":"Approved photo 3"},{"kind":"image","assetId":"photo-4","versionId":"original-v1","url":"https://media.example/photo-4.jpg","width":1600,"height":1067,"name":"Approved photo 4"}],"purpose":"product","title":"An object in four views","captions":"Front\nSide\nTexture\nDetail","cta":"Explore the collection","contact":"Example contact","demoDisclosure":"Sample only · not a product offer","duration":24,"aspect":"9:16","logo":[],"music":[],"rightsConfirmed":true},"expected":{"timeline":{"schemaVersion":1,"activeTimelineId":"photo-reel","name":"An object in four views","aspect":"9:16","aspectRatio":"9:16","previewH":369,"backgroundColor":"#101714","tracks":[{"id":"video","type":"video","label":"Original photos"},{"id":"text","type":"text","label":"Approved copy"},{"id":"overlay","type":"overlay","label":"Graphic details"},{"id":"audio","type":"audio","label":"Music"}],"trackOrder":["video","logo","text","overlay","audio"],"clips":[{"id":"photo-1","kind":"image","trackId":"video","asset":{"kind":"image","assetId":"photo-1","versionId":"original-v1","url":"https://media.example/photo-1.jpg","width":1600,"height":1067,"name":"Approved photo 1"},"assetId":"photo-1","versionId":"original-v1","url":"https://media.example/photo-1.jpg","title":"Approved photo 1","start":0,"duration":3.5,"trimStart":0,"speed":1,"volume":0,"transform":{"fit":"contain","scale":0.8737864077669902,"x":0,"y":0,"rotation":0,"opacity":1},"motion":{"type":"zoom","intensity":100,"zoomAmount":1.03,"zoomDirection":"in","zoomAnchor":"center","easing":"ease-in-out","loop":false,"window":[0,100]},"transition":{"type":"Cross dissolve","duration":0.3}},{"id":"photo-2","kind":"image","trackId":"video","asset":{"kind":"image","assetId":"photo-2","versionId":"original-v1","url":"https://media.example/photo-2.jpg","width":1600,"height":1067,"name":"Approved photo 2"},"assetId":"photo-2","versionId":"original-v1","url":"https://media.example/photo-2.jpg","title":"Approved photo 2","start":3.5,"duration":5.5,"trimStart":0,"speed":1,"volume":0,"transform":{"fit":"contain","scale":0.8737864077669902,"x":0,"y":0,"rotation":0,"opacity":1},"motion":{"type":"zoom","intensity":100,"zoomAmount":1.03,"zoomDirection":"out","zoomAnchor":"center","easing":"ease-in-out","loop":false,"window":[0,100]},"transition":{"type":"Cross dissolve","duration":0.3}},{"id":"photo-3","kind":"image","trackId":"video","asset":{"kind":"image","assetId":"photo-3","versionId":"original-v1","url":"https://media.example/photo-3.jpg","width":1600,"height":1067,"name":"Approved photo 3"},"assetId":"photo-3","versionId":"original-v1","url":"https://media.example/photo-3.jpg","title":"Approved photo 3","start":9,"duration":5.5,"trimStart":0,"speed":1,"volume":0,"transform":{"fit":"contain","scale":0.8737864077669902,"x":0,"y":0,"rotation":0,"opacity":1},"motion":{"type":"zoom","intensity":100,"zoomAmount":1.03,"zoomDirection":"in","zoomAnchor":"center","easing":"ease-in-out","loop":false,"window":[0,100]},"transition":{"type":"Cross dissolve","duration":0.3}},{"id":"photo-4","kind":"image","trackId":"video","asset":{"kind":"image","assetId":"photo-4","versionId":"original-v1","url":"https://media.example/photo-4.jpg","width":1600,"height":1067,"name":"Approved photo 4"},"assetId":"photo-4","versionId":"original-v1","url":"https://media.example/photo-4.jpg","title":"Approved photo 4","start":14.5,"duration":5.5,"trimStart":0,"speed":1,"volume":0,"transform":{"fit":"contain","scale":0.8737864077669902,"x":0,"y":0,"rotation":0,"opacity":1},"motion":{"type":"zoom","intensity":100,"zoomAmount":1.03,"zoomDirection":"out","zoomAnchor":"center","easing":"ease-in-out","loop":false,"window":[0,100]},"transition":{"type":"Cross dissolve","duration":0.3}},{"id":"end-card-photo-4","kind":"image","trackId":"video","asset":{"kind":"image","assetId":"photo-4","versionId":"original-v1","url":"https://media.example/photo-4.jpg","width":1600,"height":1067,"name":"Approved photo 4"},"assetId":"photo-4","versionId":"original-v1","url":"https://media.example/photo-4.jpg","title":"Approved photo 4","start":20,"duration":4,"trimStart":0,"speed":1,"volume":0,"transform":{"fit":"contain","scale":0.6213592233009708,"x":0,"y":-59.03999999999999,"rotation":0,"opacity":1},"motion":{"type":"none","intensity":100,"zoomAmount":1.03,"zoomDirection":"out","zoomAnchor":"center","easing":"ease-in-out","loop":false,"window":[0,100]},"transition":{"type":"Cut","duration":0.3}}],"audioItems":[],"textOverlays":[{"id":"photo-caption-1","trackId":"text","type":"text","text":"Front","font":"Hanken Grotesk","size":11,"color":"#F7F0DB","background":"none","backgroundPadding":0,"width":0.82,"x":50,"y":79,"position":"center","start":0.2,"end":3.4,"duration":3.2,"animation":"fade","animIn":0.25,"animOut":0.2,"rotation":0},{"id":"photo-number-1","trackId":"text","type":"text","text":"01 / 04","font":"Hanken Grotesk","size":6.5,"color":"#CEB88B","background":"none","backgroundPadding":0,"width":0.82,"x":50,"y":88,"position":"center","start":0,"end":3.49,"duration":3.49,"animation":"fade","animIn":0.25,"animOut":0.2,"rotation":0},{"id":"photo-caption-2","trackId":"text","type":"text","text":"Side","font":"Hanken Grotesk","size":11,"color":"#F7F0DB","background":"none","backgroundPadding":0,"width":0.82,"x":50,"y":79,"position":"center","start":3.7,"end":8.9,"duration":5.2,"animation":"fade","animIn":0.25,"animOut":0.2,"rotation":0},{"id":"photo-number-2","trackId":"text","type":"text","text":"02 / 04","font":"Hanken Grotesk","size":6.5,"color":"#CEB88B","background":"none","backgroundPadding":0,"width":0.82,"x":50,"y":88,"position":"center","start":3.5,"end":8.99,"duration":5.49,"animation":"fade","animIn":0.25,"animOut":0.2,"rotation":0},{"id":"photo-caption-3","trackId":"text","type":"text","text":"Texture","font":"Hanken Grotesk","size":11,"color":"#F7F0DB","background":"none","backgroundPadding":0,"width":0.82,"x":50,"y":79,"position":"center","start":9.2,"end":14.4,"duration":5.2,"animation":"fade","animIn":0.25,"animOut":0.2,"rotation":0},{"id":"photo-number-3","trackId":"text","type":"text","text":"03 / 04","font":"Hanken Grotesk","size":6.5,"color":"#CEB88B","background":"none","backgroundPadding":0,"width":0.82,"x":50,"y":88,"position":"center","start":9,"end":14.49,"duration":5.49,"animation":"fade","animIn":0.25,"animOut":0.2,"rotation":0},{"id":"photo-caption-4","trackId":"text","type":"text","text":"Detail","font":"Hanken Grotesk","size":11,"color":"#F7F0DB","background":"none","backgroundPadding":0,"width":0.82,"x":50,"y":79,"position":"center","start":14.7,"end":19.9,"duration":5.2,"animation":"fade","animIn":0.25,"animOut":0.2,"rotation":0},{"id":"photo-number-4","trackId":"text","type":"text","text":"04 / 04","font":"Hanken Grotesk","size":6.5,"color":"#CEB88B","background":"none","backgroundPadding":0,"width":0.82,"x":50,"y":88,"position":"center","start":14.5,"end":19.99,"duration":5.49,"animation":"fade","animIn":0.25,"animOut":0.2,"rotation":0},{"id":"opening-title","trackId":"text","type":"text","text":"An object in four views","font":"Hanken Grotesk","size":16,"color":"#F7F0DB","background":"none","backgroundPadding":0,"width":0.82,"x":50,"y":18,"position":"center","start":0,"end":3.4,"duration":3.4,"animation":"fade","animIn":0.25,"animOut":0.2,"rotation":0},{"id":"sequence-title","trackId":"text","type":"text","text":"An object in four views","font":"Hanken Grotesk","size":10,"color":"#F7F0DB","background":"none","backgroundPadding":0,"width":0.82,"x":50,"y":16,"position":"center","start":3.5,"end":20,"duration":16.5,"animation":"fade","animIn":0.25,"animOut":0.2,"rotation":0},{"id":"closing-title","trackId":"text","type":"text","text":"An object in four views","font":"Hanken Grotesk","size":10,"color":"#CEB88B","background":"none","backgroundPadding":0,"width":0.82,"x":50,"y":13,"position":"center","start":20,"end":24,"duration":4,"animation":"fade","animIn":0.25,"animOut":0.2,"rotation":0},{"id":"closing-cta","trackId":"text","type":"text","text":"Explore the collection","font":"Hanken Grotesk","size":17,"color":"#F7F0DB","background":"none","backgroundPadding":0,"width":0.82,"x":50,"y":65,"position":"center","start":20.1,"end":24,"duration":3.9,"animation":"fade","animIn":0.25,"animOut":0.2,"rotation":0},{"id":"closing-contact","trackId":"text","type":"text","text":"Example contact","font":"Hanken Grotesk","size":9,"color":"#F7F0DB","background":"none","backgroundPadding":0,"width":0.82,"x":50,"y":79,"position":"center","start":20.25,"end":24,"duration":3.75,"animation":"fade","animIn":0.25,"animOut":0.2,"rotation":0},{"id":"demo-disclosure","trackId":"text","type":"text","text":"Sample only · not a product offer","font":"Hanken Grotesk","size":6.5,"color":"#D4D9D0","background":"none","backgroundPadding":0,"width":0.84,"x":50,"y":94,"position":"center","start":0,"end":24,"duration":24,"animation":"fade","animIn":0.25,"animOut":0.2,"rotation":0}],"captions":[],"stickers":[{"id":"editorial-rule","trackId":"overlay","shape":"underline","color":"#CEB88B","x":50,"y":25,"scale":0.2,"stretchX":1.1,"stretchY":0.24,"opacity":0.85,"rotation":0,"start":0,"end":20},{"id":"closing-rule","trackId":"overlay","shape":"underline","color":"#CEB88B","x":50,"y":55,"scale":0.2,"stretchX":1.1,"stretchY":0.24,"opacity":0.85,"rotation":0,"start":20,"end":24}],"effects":[],"audio":[],"overlays":[{"id":"photo-caption-1","trackId":"text","type":"text","text":"Front","font":"Hanken Grotesk","size":11,"color":"#F7F0DB","background":"none","backgroundPadding":0,"width":0.82,"x":50,"y":79,"position":"center","start":0.2,"end":3.4,"duration":3.2,"animation":"fade","animIn":0.25,"animOut":0.2,"rotation":0},{"id":"photo-number-1","trackId":"text","type":"text","text":"01 / 04","font":"Hanken Grotesk","size":6.5,"color":"#CEB88B","background":"none","backgroundPadding":0,"width":0.82,"x":50,"y":88,"position":"center","start":0,"end":3.49,"duration":3.49,"animation":"fade","animIn":0.25,"animOut":0.2,"rotation":0},{"id":"photo-caption-2","trackId":"text","type":"text","text":"Side","font":"Hanken Grotesk","size":11,"color":"#F7F0DB","background":"none","backgroundPadding":0,"width":0.82,"x":50,"y":79,"position":"center","start":3.7,"end":8.9,"duration":5.2,"animation":"fade","animIn":0.25,"animOut":0.2,"rotation":0},{"id":"photo-number-2","trackId":"text","type":"text","text":"02 / 04","font":"Hanken Grotesk","size":6.5,"color":"#CEB88B","background":"none","backgroundPadding":0,"width":0.82,"x":50,"y":88,"position":"center","start":3.5,"end":8.99,"duration":5.49,"animation":"fade","animIn":0.25,"animOut":0.2,"rotation":0},{"id":"photo-caption-3","trackId":"text","type":"text","text":"Texture","font":"Hanken Grotesk","size":11,"color":"#F7F0DB","background":"none","backgroundPadding":0,"width":0.82,"x":50,"y":79,"position":"center","start":9.2,"end":14.4,"duration":5.2,"animation":"fade","animIn":0.25,"animOut":0.2,"rotation":0},{"id":"photo-number-3","trackId":"text","type":"text","text":"03 / 04","font":"Hanken Grotesk","size":6.5,"color":"#CEB88B","background":"none","backgroundPadding":0,"width":0.82,"x":50,"y":88,"position":"center","start":9,"end":14.49,"duration":5.49,"animation":"fade","animIn":0.25,"animOut":0.2,"rotation":0},{"id":"photo-caption-4","trackId":"text","type":"text","text":"Detail","font":"Hanken Grotesk","size":11,"color":"#F7F0DB","background":"none","backgroundPadding":0,"width":0.82,"x":50,"y":79,"position":"center","start":14.7,"end":19.9,"duration":5.2,"animation":"fade","animIn":0.25,"animOut":0.2,"rotation":0},{"id":"photo-number-4","trackId":"text","type":"text","text":"04 / 04","font":"Hanken Grotesk","size":6.5,"color":"#CEB88B","background":"none","backgroundPadding":0,"width":0.82,"x":50,"y":88,"position":"center","start":14.5,"end":19.99,"duration":5.49,"animation":"fade","animIn":0.25,"animOut":0.2,"rotation":0},{"id":"opening-title","trackId":"text","type":"text","text":"An object in four views","font":"Hanken Grotesk","size":16,"color":"#F7F0DB","background":"none","backgroundPadding":0,"width":0.82,"x":50,"y":18,"position":"center","start":0,"end":3.4,"duration":3.4,"animation":"fade","animIn":0.25,"animOut":0.2,"rotation":0},{"id":"sequence-title","trackId":"text","type":"text","text":"An object in four views","font":"Hanken Grotesk","size":10,"color":"#F7F0DB","background":"none","backgroundPadding":0,"width":0.82,"x":50,"y":16,"position":"center","start":3.5,"end":20,"duration":16.5,"animation":"fade","animIn":0.25,"animOut":0.2,"rotation":0},{"id":"closing-title","trackId":"text","type":"text","text":"An object in four views","font":"Hanken Grotesk","size":10,"color":"#CEB88B","background":"none","backgroundPadding":0,"width":0.82,"x":50,"y":13,"position":"center","start":20,"end":24,"duration":4,"animation":"fade","animIn":0.25,"animOut":0.2,"rotation":0},{"id":"closing-cta","trackId":"text","type":"text","text":"Explore the collection","font":"Hanken Grotesk","size":17,"color":"#F7F0DB","background":"none","backgroundPadding":0,"width":0.82,"x":50,"y":65,"position":"center","start":20.1,"end":24,"duration":3.9,"animation":"fade","animIn":0.25,"animOut":0.2,"rotation":0},{"id":"closing-contact","trackId":"text","type":"text","text":"Example contact","font":"Hanken Grotesk","size":9,"color":"#F7F0DB","background":"none","backgroundPadding":0,"width":0.82,"x":50,"y":79,"position":"center","start":20.25,"end":24,"duration":3.75,"animation":"fade","animIn":0.25,"animOut":0.2,"rotation":0},{"id":"demo-disclosure","trackId":"text","type":"text","text":"Sample only · not a product offer","font":"Hanken Grotesk","size":6.5,"color":"#D4D9D0","background":"none","backgroundPadding":0,"width":0.84,"x":50,"y":94,"position":"center","start":0,"end":24,"duration":24,"animation":"fade","animIn":0.25,"animOut":0.2,"rotation":0}],"targetDuration":24,"exportSettings":{"aspect":"9:16","fps":30,"resolution":"1080p","quality":"high"},"photoReel":{"schemaVersion":1,"kind":"photo-reel-source-record","block":{"id":"stillmade.photo-reel-composer","version":"1.0.0"},"purpose":"product","sourcePhotos":[{"index":0,"assetId":"photo-1","versionId":"original-v1","width":1600,"height":1067},{"index":1,"assetId":"photo-2","versionId":"original-v1","width":1600,"height":1067},{"index":2,"assetId":"photo-3","versionId":"original-v1","width":1600,"height":1067},{"index":3,"assetId":"photo-4","versionId":"original-v1","width":1600,"height":1067}],"logo":null,"music":null,"approvedCopy":{"title":"An object in four views","captions":["Front","Side","Texture","Detail"],"cta":"Explore the collection","contact":"Example contact","demoDisclosure":"Sample only · not a product offer"},"rights":{"userConfirmed":true,"independentlyVerified":false},"transformations":["contain fit","uniform scale","at most three percent optical zoom"],"generativeMedia":false,"providerCalls":0}},"review":{"schemaVersion":1,"state":"needs-human-review","durationSec":24,"aspect":"9:16","photoCount":4,"checks":[{"id":"original-source-versions","passed":true,"note":"Every photo retains its supplied asset/version and full source rectangle."},{"id":"approved-copy-only","passed":true,"note":"No property features, prices, availability or product claims are generated."},{"id":"bounded-local-composition","passed":true,"note":"No provider call or credit spend occurs in this Block."},{"id":"complete-timing","passed":true,"note":"All source photos and a four-second closing card fit the requested duration."}],"warnings":["Review the full export for photo quality, exact facts, readable copy, licensing and the displayed contact before delivering.","Silent reel. Add rights-cleared music in this Block or the Editor if wanted.","This is labeled demonstration material and is not evidence of a real property or product offer."],"humanApproved":false,"exportAuthorized":false,"cost":{"providerCalls":0,"generationCredits":0,"note":"Host storage, export access and any separately added services use their own policy."}},"provenance":{"schemaVersion":1,"kind":"photo-reel-source-record","block":{"id":"stillmade.photo-reel-composer","version":"1.0.0"},"purpose":"product","sourcePhotos":[{"index":0,"assetId":"photo-1","versionId":"original-v1","width":1600,"height":1067},{"index":1,"assetId":"photo-2","versionId":"original-v1","width":1600,"height":1067},{"index":2,"assetId":"photo-3","versionId":"original-v1","width":1600,"height":1067},{"index":3,"assetId":"photo-4","versionId":"original-v1","width":1600,"height":1067}],"logo":null,"music":null,"approvedCopy":{"title":"An object in four views","captions":["Front","Side","Texture","Detail"],"cta":"Explore the collection","contact":"Example contact","demoDisclosure":"Sample only · not a product offer"},"rights":{"userConfirmed":true,"independentlyVerified":false},"transformations":["contain fit","uniform scale","at most three percent optical zoom"],"generativeMedia":false,"providerCalls":0}}}],"portable":{"format":"stillmade-block/1","dependencies":[],"sources":[{"kind":"original","license":"MIT","paths":["LICENSE","src/run.js"],"evidence":["upstream/0/LICENSE","upstream/0/src/run.js"],"changes":"Original implementation declared by the packager; no reused repository or dependency code."}],"files":{"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","upstream/0/src/run.js":"// Photo Reel Composer 1.0.0. Runs only inside the isolated SDK JavaScript runtime.\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.');\nfail(input.rightsConfirmed === true, 'Confirm that you may use these photos, logo, music and exact supplied statements before building.');\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"},"lineage":{"parents":[],"changes":"Packaged original Block source for StillMade."}}},{"manifest":{"schemaVersion":1,"sdkVersion":"0.1.0","id":"sm.editor","version":"2.2.9","name":"Editor","description":"Build and render a complete editable timeline, including coded vector scenes whose transforms may be omitted or retained in the scene document.","kind":"workspace","runtime":"javascript","entry":"src/run.js","license":"MIT","provenance":{"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, src/view.json\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","portableSources":[{"kind":"original","license":"MIT","paths":["LICENSE","src/run.js","src/view.json"],"evidence":["upstream/0/LICENSE","upstream/0/src/run.js","upstream/0/src/view.json"],"changes":"Original implementation declared by the packager; no reused repository or dependency code."}]},"inputs":{"editor":{"type":"timeline","context":"editor","primary":true,"required":false,"default":{"schemaVersion":1,"activeTimelineId":"timeline-1","tracks":[],"clips":[],"audioItems":[],"captions":[],"textOverlays":[],"effects":[]},"description":"The complete current Editor document. All internal editing tools and retained media/version data remain together.","semantic":"editor_timeline","sources":["project"]},"assets":{"type":"asset[]","context":"assets","required":false,"default":[],"description":"Current project media used to assemble an initial editable timeline when the Editor is empty.","semantic":"legacy.asset-list.assets","sources":["project"]},"updatedEditor":{"type":"timeline","required":false,"default":{"schemaVersion":1,"activeTimelineId":"timeline-1","tracks":[],"clips":[],"audioItems":[],"captions":[],"textOverlays":[],"effects":[]},"description":"The complete edited timeline prepared in this workspace.","semantic":"legacy.timeline.updatededitor","sources":["upstream","user","default"]},"commit":{"type":"boolean","default":false,"description":"Prepare the edited Editor document for StillMade review and Apply.","semantic":"legacy.boolean.commit","required":false,"sources":["user","default"]},"action":{"type":"text","required":false,"default":"none","description":"The Block-owned interface selects a declared host action.","semantic":"legacy.text.action","sources":["user","default"]},"renderFps":{"type":"integer","min":12,"max":60,"default":30,"description":"Frames per second sent to the declared final-render service.","semantic":"legacy.integer.renderfps","required":false,"sources":["user","default"]},"renderResolution":{"type":"integer","min":480,"max":2160,"default":1080,"description":"Output height sent to the declared final-render service.","semantic":"legacy.integer.renderresolution","required":false,"sources":["user","default"]},"renderQuality":{"type":"text","default":"high","description":"Output quality sent to the declared final-render service.","semantic":"legacy.text.renderquality","required":false,"sources":["user","default"]}},"outputs":{"editor":{"type":"timeline","primary":true,"role":"editor_timeline","description":"The complete edited timeline for immediate use and downstream Blocks.","semantic":"editor_timeline"},"edit":{"type":"workspace-edit","description":"An exact before/after Editor change reviewed before it updates the project.","semantic":"legacy.workspace-edit.edit"},"mediaAction":{"type":"approval","required":false,"role":"portable_media_action","description":"A bounded final-render request executed only after the user chooses Render and play.","semantic":"portable_media_action"}},"permissions":{"project":["context.editor.read","context.assets.read","workspace.editor.propose"],"network":[],"filesystem":[],"secrets":[],"actions":["final.render"]},"ui":[],"platforms":{"phone":{"supported":true},"browser":{"supported":true},"desktop":{"supported":true}}},"code":"const copy = value => JSON.parse(JSON.stringify(value));\nconst source = copy(input.editor || {schemaVersion:1,activeTimelineId:'timeline-1',tracks:[],clips:[],audioItems:[],captions:[],textOverlays:[],effects:[]});\nconst assets=Array.isArray(input.assets)?input.assets.filter(asset=>asset&&['image','video','audio'].includes(asset.kind)&&asset.assetId&&asset.versionId&&asset.url).slice(0,500):[];\nconst assembled=copy(source);if(!assembled.clips?.length&&!assembled.audioItems?.length&&assets.length){assembled.tracks=[{id:'video',kind:'video',label:'Video'},{id:'audio',kind:'audio',label:'Audio'}];let cursor=0;assembled.clips=assets.filter(asset=>asset.kind!=='audio').map((asset,index)=>{const duration=Math.max(.001,Number(asset.duration)||Number(asset.durationSec)||4),clip={id:'clip-'+String(index+1)+'-'+String(asset.assetId),trackId:'video',start:cursor,duration,trimStart:0,speed:1,volume:1,asset:copy(asset),url:asset.url};cursor+=duration;return clip});assembled.audioItems=assets.filter(asset=>asset.kind==='audio').map((asset,index)=>({id:'audio-'+String(index+1)+'-'+String(asset.assetId),trackId:'audio',start:0,duration:Math.max(.001,Number(asset.duration)||Number(asset.durationSec)||cursor||1),trimStart:0,volume:1,asset:copy(asset),url:asset.url}));assembled.targetDuration=Math.max(cursor,...assembled.audioItems.map(item=>item.duration));}\nconst candidate = input.commit || input.action === 'render' ? copy(input.updatedEditor) : assembled;\nif (!candidate || Array.isArray(candidate) || candidate.schemaVersion !== 1 || typeof candidate.activeTimelineId !== 'string' || !candidate.activeTimelineId) throw new Error('Keep the complete version 1 Editor document and active timeline identity.');\nconst names=['tracks','clips','audioItems','captions','textOverlays','stickers','effects','markers','fxEvents','liveOverlays','mediaItems','timelineVersions'];\nif (!names.every(name => candidate[name] === undefined || Array.isArray(candidate[name]) && candidate[name].length <= 10000 && candidate[name].every(item => item && typeof item === 'object' && !Array.isArray(item) && typeof item.id === 'string' && item.id.length > 0 && item.id.length <= 200) && new Set(candidate[name].map(item => item.id)).size === candidate[name].length)) throw new Error('Editor collections need valid distinct retained item IDs.');\nconst itemEnd=item=>Math.max(0,Number(item.start)||0)+Math.max(.001,Number(item.duration)||Number(item.durationMs)/1000||.001);\nconst outputs={editor:candidate,edit:{schemaVersion:1,title:`Update Editor timeline ${candidate.activeTimelineId}`,field:'editor',before:source,after:candidate}};\nif(input.action==='render'){const clips=Array.isArray(candidate.clips)?candidate.clips:[],audio=Array.isArray(candidate.audioItems)?candidate.audioItems:[],overlays=[...(candidate.captions||[]),...(candidate.textOverlays||[]),...(candidate.effects||[])],targetDuration=Math.max(.001,Number(candidate.targetDuration)||0,...clips.map(itemEnd),...audio.map(itemEnd),...overlays.map(itemEnd));outputs.mediaAction={schemaVersion:1,kind:'stillmade.media-action',operation:'final.render',request:{schemaVersion:1,kind:'video',timeline:{...candidate,clips,audio,overlays,targetDuration},settings:{fps:input.renderFps,resolution:input.renderResolution,quality:input.renderQuality},source:{id:'sm.editor',version:'2.2.9'}},resultPort:null};}\nreturn outputs;\n","view":{"html":"<main class=\"editor-workspace\">\n  <header><div><small>Production workspace</small><h1>Editor</h1><p id=\"summary\">Loading the complete timeline…</p></div><div class=\"actions\"><button id=\"build\" type=\"button\" class=\"secondary\">Build from project media</button><button id=\"undo\" type=\"button\" class=\"secondary\">Undo</button><button id=\"redo\" type=\"button\" class=\"secondary\">Redo</button><button id=\"snapshot\" type=\"button\" class=\"secondary\">Save version</button><button id=\"reset\" type=\"button\" class=\"secondary\">Reset changes</button><button id=\"addText\" type=\"button\" class=\"secondary\">Add text</button><button id=\"addCaption\" type=\"button\" class=\"secondary\">Add caption</button><button id=\"save\" type=\"button\" data-stillmade-action=\"run\">Review changes</button></div></header>\n  <output id=\"status\" role=\"status\" aria-live=\"polite\"></output>\n  <p id=\"sharedResult\" class=\"hint\" aria-live=\"polite\"></p>\n  <section id=\"recovery\" hidden><h2>Keep your work safe</h2><p id=\"recoveryMessage\"></p><div class=\"row\"><button id=\"retryShared\" type=\"button\" class=\"secondary\">Retry checked changes</button><button id=\"discardShared\" type=\"button\" class=\"secondary\">Discard my unshared changes</button><button id=\"resetSharedSource\" type=\"button\" class=\"secondary\" hidden>Review updated project timeline</button></div><p>The project source changed. Starting from it replaces the shared timeline draft for everyone. Unfinished JSON is retained for separate review.</p><button id=\"confirmSourceReset\" type=\"button\" hidden>Replace everyone's shared draft with the updated project timeline</button></section>\n  <section><h2>Timeline</h2><div class=\"grid\"><label>Active timeline<input id=\"timelineId\" type=\"text\" disabled></label><label>Aspect ratio<input id=\"aspect\" type=\"text\"></label><label>Playhead (seconds)<input id=\"playhead\" type=\"range\" min=\"0\" max=\"1\" step=\"0.05\" value=\"0\"></label></div><p id=\"timecode\" class=\"hint\"></p><div id=\"composition\" class=\"cards\"></div></section>\n  <section><h2>Project media</h2><p class=\"hint\">Preview an exact saved version, then add it to this timeline without leaving the Block.</p><img id=\"imagePreview\" alt=\"Selected project media preview\" hidden><video id=\"videoPreview\" controls hidden></video><audio id=\"audioPreview\" controls hidden></audio><div id=\"assets\" class=\"cards media-grid\"></div></section>\n  <section><h2>Video</h2><div id=\"clips\" class=\"cards\"></div></section>\n  <section><h2>Audio and scoring</h2><div id=\"audio\" class=\"cards\"></div></section>\n  <section><h2>Saved timeline versions</h2><div id=\"versions\" class=\"cards\"></div></section>\n  <section><h2>Text and motion graphics</h2><div id=\"text\" class=\"cards\"></div><p id=\"graphics\" class=\"hint\"></p></section>\n  <section><h2>Captions</h2><div id=\"captions\" class=\"cards\"></div></section>\n  <section><div class=\"card-head\"><h2>Effects</h2><button id=\"addEffect\" type=\"button\" class=\"secondary\">Add effect</button></div><div id=\"effects\" class=\"cards\"></div></section>\n  <details id=\"rawDetails\"><summary>Complete Editor data</summary><p>All timeline lanes, motion graphics, grade settings, transitions, scoring, versions, and export settings are retained. Use this editor for fields not shown on the lane cards.</p><p id=\"rawNotice\" class=\"hint\"></p><textarea id=\"raw\" class=\"raw\" spellcheck=\"false\" aria-label=\"Complete Editor data\"></textarea><div class=\"row\"><button id=\"applyRaw\" type=\"button\" class=\"secondary\">Apply JSON to workspace</button><button id=\"discardRaw\" type=\"button\" class=\"secondary\">Discard shared JSON draft</button></div></details>\n<section><h2>Final export</h2><p>Render this exact timeline through StillMade’s approved device renderer.</p><div class=\"grid\"><label>Frame rate<select id=\"renderFps\"><option value=\"24\">24 fps</option><option value=\"30\" selected>30 fps</option><option value=\"60\">60 fps</option></select></label><label>Resolution<select id=\"renderResolution\"><option value=\"720\">720p</option><option value=\"1080\" selected>1080p</option><option value=\"2160\">2160p</option></select></label><label>Quality<select id=\"renderQuality\"><option value=\"standard\">Standard</option><option value=\"high\" selected>High</option></select></label></div><button id=\"renderFinal\" type=\"button\">Render final video</button></section></main>\n","css":".editor-workspace{max-width:1160px;margin:0 auto;padding:8px;color:var(--fg)}header,.actions,.card-head,.row{display:flex;align-items:center;gap:10px;flex-wrap:wrap}header{justify-content:space-between;margin-bottom:14px}header h1{margin:2px 0}header p,header small,details p,output,.hint{color:var(--fg-muted)}section,details{margin:12px 0;padding:16px;border:1px solid var(--border-tok);border-radius:var(--r-3);background:var(--bg-elev-1)}section h2{margin:0 0 10px}.cards{display:grid;gap:10px}.card{padding:12px;border:1px solid var(--border-tok);border-radius:var(--r-2);background:var(--bg)}.card-head{justify-content:space-between}.card h3{margin:0;font-size:16px}.grid{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:10px}label{display:grid;gap:5px;margin:7px 0;font-size:13px;font-weight:600}input,textarea{width:100%;background:var(--bg)}textarea{min-height:70px;resize:vertical}.wide{grid-column:1/-1}.secondary{background:transparent;color:var(--fg)}.danger{background:transparent;color:var(--signal)}.raw{min-height:440px;font-family:var(--font-mono);font-size:12px}output{display:block;min-height:24px;margin:8px 2px}\n@media(max-width:900px){.grid{grid-template-columns:repeat(2,minmax(0,1fr))}}@media(max-width:560px){.editor-workspace{padding:0}.grid{grid-template-columns:1fr}.actions{width:100%}.actions button{flex:1}section,details{padding:12px}}\n.media-grid{grid-template-columns:repeat(3,minmax(0,1fr))}.media-card p{margin:4px 0;color:var(--fg-muted)}video,audio{display:block;width:100%;margin:10px 0}video[hidden],audio[hidden]{display:none}select{width:100%;background:var(--bg)}\n.card,.grid>*{min-width:0}.card h3,.card strong,.card p{overflow-wrap:anywhere}img{max-width:100%;height:auto}@media(max-width:560px){.media-grid{grid-template-columns:1fr}}\n","javascript":"const status=document.getElementById('status'),summary=document.getElementById('summary'),raw=document.getElementById('raw'),save=document.getElementById('save'),aspect=document.getElementById('aspect'),timelineId=document.getElementById('timelineId'),graphics=document.getElementById('graphics'),build=document.getElementById('build'),imagePreview=document.getElementById('imagePreview'),videoPreview=document.getElementById('videoPreview'),audioPreview=document.getElementById('audioPreview'),playhead=document.getElementById('playhead'),timecode=document.getElementById('timecode');\nconst empty={schemaVersion:1,activeTimelineId:'timeline-1',tracks:[],clips:[],audioItems:[],captions:[],textOverlays:[],effects:[]};\nlet source=empty,draft=empty,assets=[],previewUrl='',previewSerial=0,busy=false,revision=0;\nlet shared=null,scratchShared=null,optionsShared=null,scratch={text:null,base:null},options={renderFps:30,renderResolution:1080,renderQuality:'high'};\nlet access={canEdit:false,error:null},scratchAccess={canEdit:false,error:null},optionsAccess={canEdit:false,error:null},sourceResetArmed=false,rawBaseline=null,rawStale=false;\nlet undoStack=[],redoStack=[],historyBefore=null,historyAfter=null;\nconst tokens=new Map(),identities=new Map(),pages=new Map();\nconst clone=value=>JSON.parse(JSON.stringify(value));\nconst text=value=>value===undefined||value===null?'':String(value);\nconst uniqueId=prefix=>prefix+'-'+Date.now().toString(36)+'-'+Math.random().toString(36).slice(2,14);\nconst blocked=()=>busy||!shared||!scratchShared||!optionsShared||!access.canEdit||!scratchAccess.canEdit||!optionsAccess.canEdit||!!access.error||!!scratchAccess.error||!!optionsAccess.error;\nconst allowed=()=>access.canEdit&&scratchAccess.canEdit&&optionsAccess.canEdit&&!access.error&&!scratchAccess.error&&!optionsAccess.error;\nconst token=(kind,item)=>{const key=JSON.stringify(kind==='asset'?[kind,item.kind,item.assetId,item.versionId]:[kind,item.id]);if(!tokens.has(key)){const value='i'+String(tokens.size+1);tokens.set(key,value);}const handle=tokens.get(key);identities.set(handle,{kind,item});return handle;};\nconst normalize=value=>({...value,tracks:Array.isArray(value.tracks)?value.tracks:[],clips:Array.isArray(value.clips)?value.clips:[],audioItems:Array.isArray(value.audioItems)?value.audioItems:[],captions:Array.isArray(value.captions)?value.captions:[],textOverlays:Array.isArray(value.textOverlays)?value.textOverlays:[],effects:Array.isArray(value.effects)?value.effects:[]});\nconst assemble=()=>{const next=clone(source),media=assets.filter(asset=>asset&&['image','video','audio'].includes(asset.kind)&&asset.assetId&&asset.versionId&&asset.url).slice(0,500);if(next.clips?.length||next.audioItems?.length||!media.length)return normalize(next);next.tracks=[{id:'video',kind:'video',label:'Video'},{id:'audio',kind:'audio',label:'Audio'}];let cursor=0;next.clips=media.filter(asset=>asset.kind!=='audio').map((asset,index)=>{const duration=Math.max(.001,Number(asset.duration)||Number(asset.durationSec)||4),clip={id:'clip-'+String(index+1)+'-'+text(asset.assetId),trackId:'video',start:cursor,duration,trimStart:0,speed:1,volume:1,asset:clone(asset),url:asset.url};cursor+=duration;return clip;});next.audioItems=media.filter(asset=>asset.kind==='audio').map((asset,index)=>({id:'audio-'+String(index+1)+'-'+text(asset.assetId),trackId:'audio',start:0,duration:Math.max(.001,Number(asset.duration)||Number(asset.durationSec)||cursor||1),trimStart:0,volume:1,asset:clone(asset),url:asset.url}));next.targetDuration=Math.max(cursor,...next.audioItems.map(item=>item.duration));return normalize(next);};\nconst field=(label,action,value,type='text',wide=false)=>({tag:'label',className:wide?'wide':'',children:[{tag:'span',text:label},{tag:'input',id:'control-'+action.replace(':','-'),type,action,value:Number.isFinite(value)?value:text(value),disabled:blocked(),label}]});\nconst remove=action=>({tag:'button',action,text:'Remove',className:'danger',disabled:blocked()});\nconst assetCard=(item,index)=>({tag:'article',id:'card-'+token('asset',item),className:'card media-card',children:[{tag:'strong',text:text(item.name)||text(item.assetId)},{tag:'p',text:text(item.kind)+' · version '+text(item.versionId)},{tag:'div',className:'row',children:[{tag:'button',action:'preview-asset:'+token('asset',item),text:'Preview',className:'secondary',disabled:false},{tag:'button',action:'add-asset:'+token('asset',item),text:item.kind==='audio'?'Add audio':'Add clip',className:'secondary',disabled:blocked()}]}]});\nconst clipCard=(item,index)=>({tag:'article',id:'card-'+token('clip',item),className:'card',children:[{tag:'div',className:'card-head',children:[{tag:'h3',text:text(item.label)||text(item.name)||text(item.id)},{tag:'div',className:'row',children:[{tag:'button',action:'preview-clip:'+token('clip',item),text:'Preview',className:'secondary',disabled:!(item.asset||item.assetId)},{tag:'button',action:'move-clip-up:'+token('clip',item),text:'Up',className:'secondary',disabled:blocked()||index===0},{tag:'button',action:'move-clip-down:'+token('clip',item),text:'Down',className:'secondary',disabled:blocked()||index===draft.clips.length-1},{tag:'button',action:'split-clip:'+token('clip',item),text:'Split',className:'secondary',disabled:blocked()||Number(item.duration)<=0.002},{tag:'button',action:'duplicate-clip:'+token('clip',item),text:'Duplicate',className:'secondary',disabled:blocked()},remove('remove-clip:'+token('clip',item))]}]},{tag:'div',className:'grid',children:[field('Track','clip-track:'+token('clip',item),item.trackId),field('Start (seconds)','clip-start:'+token('clip',item),item.start,'number'),field('Length (seconds)','clip-duration:'+token('clip',item),item.duration,'number'),field('Trim start','clip-trim:'+token('clip',item),item.trimStart,'number'),field('Speed','clip-speed:'+token('clip',item),item.speed,'number'),field('Volume','clip-volume:'+token('clip',item),item.volume,'number'),field('Transition','clip-transition:'+token('clip',item),item.transition&&item.transition.type),field('Transition length','clip-transition-duration:'+token('clip',item),item.transition&&item.transition.duration,'number'),field('Motion','clip-motion:'+token('clip',item),item.motion&&item.motion.type),field('Motion easing','clip-easing:'+token('clip',item),item.motion&&item.motion.easing),field('Opacity','clip-opacity:'+token('clip',item),item.transform&&item.transform.opacity!==undefined?item.transform.opacity:item.opacity,'number'),{tag:'label',children:[{tag:'span',text:'Reverse playback'},{tag:'input',type:'checkbox',id:'control-'+('clip-reverse:'+token('clip',item)).replace(':','-'),action:'clip-reverse:'+token('clip',item),checked:!!item.reverse,disabled:blocked(),label:'Reverse playback'}]}]}]});\nconst audioCard=(item,index)=>({tag:'article',id:'card-'+token('audio',item),className:'card',children:[{tag:'div',className:'card-head',children:[{tag:'h3',text:text(item.label)||text(item.name)||text(item.id)},{tag:'div',className:'row',children:[{tag:'button',action:'preview-audio:'+token('audio',item),text:'Preview',className:'secondary',disabled:!(item.asset||item.assetId)},remove('remove-audio:'+token('audio',item))]}]},{tag:'div',className:'grid',children:[field('Track','audio-track:'+token('audio',item),item.trackId),field('Start (seconds)','audio-start:'+token('audio',item),item.start,'number'),field('Length (seconds)','audio-duration:'+token('audio',item),item.duration,'number'),field('Trim start','audio-trim:'+token('audio',item),item.trimStart===undefined?item.sourceOffset:item.trimStart,'number'),field('Volume','audio-volume:'+token('audio',item),item.volume,'number'),field('Fade in','audio-fade-in:'+token('audio',item),item.fadeIn,'number'),field('Fade out','audio-fade-out:'+token('audio',item),item.fadeOut,'number'),field('Ducking','audio-ducking:'+token('audio',item),item.ducking,'number'),field('Playback speed','audio-rate:'+token('audio',item),item.rate,'number'),{tag:'label',children:[{tag:'span',text:'Loop source'},{tag:'input',type:'checkbox',id:'control-'+('audio-loop:'+token('audio',item)).replace(':','-'),action:'audio-loop:'+token('audio',item),checked:!!item.loop,disabled:blocked(),label:'Loop source'}]},{tag:'label',children:[{tag:'span',text:'Muted'},{tag:'input',type:'checkbox',id:'control-'+('audio-muted:'+token('audio',item)).replace(':','-'),action:'audio-muted:'+token('audio',item),checked:!!item.muted,disabled:blocked(),label:'Muted'}]}]}]});\nconst textCard=(item,index)=>({tag:'article',id:'card-'+token('text',item),className:'card',children:[{tag:'div',className:'card-head',children:[{tag:'h3',text:text(item.id)},remove('remove-text:'+token('text',item))]},{tag:'div',className:'grid',children:[field('Text','text-value:'+token('text',item),item.text,'text',true),field('Track','text-track:'+token('text',item),item.trackId),field('Start (seconds)','text-start:'+token('text',item),item.start,'number'),field('Length (seconds)','text-duration:'+token('text',item),item.duration,'number'),field('Animation','text-animation:'+token('text',item),item.animation)]}]});\nconst captionCard=(item,index)=>({tag:'article',id:'card-'+token('caption',item),className:'card',children:[{tag:'div',className:'card-head',children:[{tag:'h3',text:text(item.id)},remove('remove-caption:'+token('caption',item))]},{tag:'div',className:'grid',children:[field('Caption','caption-value:'+token('caption',item),item.text,'text',true),field('Track','caption-track:'+token('caption',item),item.trackId),field('Start (seconds)','caption-start:'+token('caption',item),item.start,'number'),field('End (seconds)','caption-end:'+token('caption',item),item.end,'number'),field('Animation','caption-animation:'+token('caption',item),item.animation)]}]});\nconst effectCard=(item,index)=>({tag:'article',id:'card-'+token('effect',item),className:'card',children:[{tag:'div',className:'card-head',children:[{tag:'h3',text:text(item.type)||text(item.id)},remove('remove-effect:'+token('effect',item))]},{tag:'div',className:'grid',children:[field('Type','effect-type:'+token('effect',item),item.type),field('Track','effect-track:'+token('effect',item),item.trackId),field('Start (seconds)','effect-start:'+token('effect',item),item.start,'number'),field('Length (seconds)','effect-duration:'+token('effect',item),item.duration,'number'),field('Intensity','effect-intensity:'+token('effect',item),item.intensity,'number')]}]});\nconst largeTimeline=()=>draft.clips.length+draft.audioItems.length+draft.textOverlays.length+draft.captions.length+draft.effects.length>2000;\nconst itemFinish=item=>(Number(item.start)||0)+Math.max(.001,Number(item.duration)||Number(item.end)-(Number(item.start)||0)||.001);\nconst composition=()=>{const at=Number(playhead.value)||0;if(largeTimeline()){StillMade.render('composition',[{tag:'strong',text:'Lightweight preview · large timeline'},{tag:'p',text:'Partial preview: the live composition summary is paused to keep editing responsive. Every timeline item is retained for editing and the complete final render.'},{tag:'p',text:'The personal playhead uses the saved timeline duration, or a 24-hour preview window when none is supplied.'}]);timecode.textContent='Playhead '+at.toFixed(2)+' seconds';return;}const activeClips=draft.clips.filter(item=>at>=(Number(item.start)||0)&&at<itemFinish(item)),activeAudio=draft.audioItems.filter(item=>at>=(Number(item.start)||0)&&at<itemFinish(item)),activeOverlays=[...draft.textOverlays,...draft.captions,...draft.effects].filter(item=>at>=(Number(item.start)||0)&&at<itemFinish(item));StillMade.render('composition',[{tag:'strong',text:'At '+at.toFixed(2)+'s'},{tag:'p',text:(activeClips.length?activeClips.slice(0,12).map(item=>text(item.label)||text(item.id)).join(', '):'No video')+' · '+String(activeAudio.length)+' audio · '+String(activeOverlays.length)+' overlays'}]);timecode.textContent='Playhead '+at.toFixed(2)+' seconds';};\nconst rawIsOpen=()=>document.getElementById('rawDetails').getAttribute('open')!==null;\nconst renderRawPreview=()=>{if(!rawIsOpen())return;if(scratch.text!==null){if(raw.value!==scratch.text)raw.value=scratch.text;return;}rawBaseline=draft;const value=JSON.stringify(draft,null,2);if(raw.value!==value)raw.value=value;};\nconst schedulePreview=()=>{if(rawIsOpen())StillMade.scheduleSharedEdit('editorRawPreview',renderRawPreview);};\nconst drawRecovery=()=>{const states=[access,scratchAccess,optionsAccess],failure=states.find(value=>value.error),sourceMismatch=states.some(value=>value.sourceConflict),waiting=states.some(value=>value.pending),editable=states.every(value=>value.canEdit);document.getElementById('recovery').hidden=!failure;document.getElementById('recoveryMessage').textContent=failure?failure.error:'';document.getElementById('retryShared').disabled=!editable||waiting||sourceMismatch||busy;document.getElementById('discardShared').disabled=!editable||waiting||sourceMismatch||busy;document.getElementById('resetSharedSource').hidden=!sourceMismatch;document.getElementById('resetSharedSource').disabled=!editable||waiting||busy;document.getElementById('confirmSourceReset').hidden=!sourceMismatch||!sourceResetArmed;document.getElementById('confirmSourceReset').disabled=!editable||waiting||busy;document.getElementById('rawNotice').textContent=scratch.text===null?'JSON typing is shared live. Apply checks the original displayed values before updating the timeline.':rawStale?'The timeline changed. Shared JSON text is retained; Apply will check for conflicting edits.':'This unfinished JSON draft is shared live. Apply it when ready, or discard the shared scratch.';save.disabled=blocked()||scratch.text!==null;document.getElementById('renderFinal').disabled=blocked()||scratch.text!==null;document.getElementById('applyRaw').disabled=blocked()||scratch.text===null;document.getElementById('discardRaw').disabled=blocked()||scratch.text===null;document.getElementById('undo').disabled=blocked()||(!undoStack.length&&!historyBefore);document.getElementById('redo').disabled=blocked()||!redoStack.length;};\nconst versionCard=(item,index)=>({tag:'article',id:'card-'+token('version',item),className:'card',children:[{tag:'strong',text:text(item.label)||text(item.id)},{tag:'p',text:text(item.createdAt)},{tag:'button',action:'restore-version:'+token('version',item),text:'Restore this version',className:'secondary',disabled:blocked()}]});\nconst renderPage=(target,rows,card,size,emptyMessage)=>{const last=Math.max(0,Math.ceil(rows.length/size)-1),page=Math.min(last,pages.get(target)||0);pages.set(target,page);const content=rows.length?rows.slice(page*size,(page+1)*size).map((item,index)=>card(item,page*size+index)):[{tag:'p',text:emptyMessage}];if(rows.length>size)content.push({tag:'div',className:'row',children:[{tag:'button',action:'page-prev:'+target,text:'Previous',className:'secondary',disabled:page===0},{tag:'span',text:'Page '+String(page+1)+' of '+String(last+1)+' · '+String(rows.length)+' items'},{tag:'button',action:'page-next:'+target,text:'Next',className:'secondary',disabled:page===last}]});StillMade.render(target,content);};\nconst drawOptions=()=>{document.getElementById('renderFps').value=String(options.renderFps);document.getElementById('renderResolution').value=String(options.renderResolution);document.getElementById('renderQuality').value=options.renderQuality;};\nconst draw=()=>{renderPage('assets',assets,assetCard,12,'No saved project media is available.');renderPage('clips',draft.clips,clipCard,4,'No video clips.');renderPage('audio',draft.audioItems,audioCard,4,'No audio items.');renderPage('text',draft.textOverlays,textCard,8,'No text overlays.');renderPage('captions',draft.captions,captionCard,8,'No captions.');renderPage('effects',draft.effects,effectCard,8,'No effects.');renderPage('versions',Array.isArray(draft.timelineVersions)?draft.timelineVersions:[],versionCard,5,'No saved versions yet.');timelineId.value=text(draft.activeTimelineId);const duration=largeTimeline()?Math.max(1,Number(draft.targetDuration)||86400,Number(playhead.value)||0):Math.max(1,Number(draft.targetDuration)||0,...draft.clips.map(itemFinish),...draft.audioItems.map(itemFinish));playhead.max=String(duration);StillMade.scheduleSharedEdit('editorComposition',composition);aspect.value=text(draft.aspect);const mg=(Array.isArray(draft.fullscreenFrames)?draft.fullscreenFrames.length:0)+(Array.isArray(draft.figureSweeps)?draft.figureSweeps.length:0)+(Array.isArray(draft.graphs)?draft.graphs.length:0);graphics.textContent=String(mg)+' motion graphic item'+(mg===1?'':'s')+' retained in this Editor Block. Use Complete Editor data for advanced motion-graphics fields.';summary.textContent=String(draft.clips.length)+' video · '+String(draft.audioItems.length)+' audio · '+String(draft.captions.length)+' captions · '+String(draft.textOverlays.length)+' text';['reset','addText','addCaption','addEffect','snapshot','renderFps','renderResolution','renderQuality'].forEach(id=>{document.getElementById(id).disabled=blocked();});build.disabled=blocked()||!assets.length;aspect.disabled=blocked();raw.disabled=blocked();drawOptions();drawRecovery();schedulePreview();};\nconst captureHistory=()=>{if(!historyBefore)return;const before=historyBefore,after=historyAfter;historyBefore=null;historyAfter=null;try{const forward=StillMade.diffDocument(before,after);if(forward.length){undoStack=[...undoStack,{forward,inverse:StillMade.diffDocument(after,before)}].slice(-50);redoStack=[];}}catch(error){undoStack=[];redoStack=[];status.textContent='This change is too large for local undo. The shared timeline is retained.';}drawRecovery();};\nconst commitEdit=(next,record=true)=>{if(record){if(!historyBefore)historyBefore=draft;historyAfter=next;StillMade.scheduleSharedEdit('editorHistory',captureHistory);}draft=next;revision+=1;sourceResetArmed=false;if(scratch.text!==null)rawStale=true;shared.changed();schedulePreview();StillMade.scheduleSharedEdit('editorComposition',composition);drawRecovery();status.textContent='Sharing timeline changes. Review changes applies the document to your project.';};\nconst replaceRows=(kind,rows)=>{if(kind==='clip')commitEdit({...draft,clips:rows});if(kind==='audio')commitEdit({...draft,audioItems:rows});if(kind==='text')commitEdit({...draft,textOverlays:rows});if(kind==='caption')commitEdit({...draft,captions:rows});if(kind==='effect')commitEdit({...draft,effects:rows});};\nconst rowsFor=kind=>kind==='clip'?draft.clips:kind==='audio'?draft.audioItems:kind==='text'?draft.textOverlays:kind==='caption'?draft.captions:kind==='effect'?draft.effects:kind==='version'?(draft.timelineVersions||[]):[];\nconst previewItem=(item,kind,trim=0)=>{const serial=++previewSerial;if(previewUrl){StillMade.releaseMedia(previewUrl);previewUrl='';}imagePreview.hidden=true;videoPreview.hidden=true;audioPreview.hidden=true;const value=item.asset||item,mediaKind=value.kind||kind,request=mediaKind==='image'?StillMade.previewImage(value):StillMade.previewMedia({...value,kind:mediaKind});request.then(result=>{if(serial!==previewSerial){if(mediaKind!=='image')StillMade.releaseMedia(result.url);return;}if(mediaKind==='image'){imagePreview.src=result.url;imagePreview.hidden=false;}else if(mediaKind==='audio'){previewUrl=result.url;audioPreview.src=result.url;audioPreview.hidden=false;}else{previewUrl=result.url;videoPreview.src=result.url;videoPreview.currentTime=Math.max(0,Number(trim)||0);videoPreview.hidden=false;}status.textContent='Previewing '+text(item.label||item.name||item.id||item.assetId)+'.';}).catch(error=>{if(serial===previewSerial)status.textContent=error.message;});};\naspect.addEventListener('input',()=>{if(blocked())return;commitEdit({...draft,aspect:aspect.value});});\nplayhead.addEventListener('input',()=>{timecode.textContent='Playhead '+(Number(playhead.value)||0).toFixed(2)+' seconds';StillMade.scheduleSharedEdit('editorComposition',composition);});\ndocument.getElementById('rawDetails').addEventListener('toggle',renderRawPreview);\nraw.addEventListener('input',()=>{if(blocked())return;scratch={text:raw.value,base:scratch.text===null?(rawBaseline||draft):scratch.base};scratchShared.changed();drawRecovery();});\ndocument.getElementById('discardRaw').addEventListener('click',()=>{if(blocked())return;scratch={text:null,base:null};rawStale=false;scratchShared.changed();renderRawPreview();drawRecovery();});\nStillMade.onAction(event=>{const parts=event.action.split(':'),kind=parts[0],handle=parts[1];if(kind==='page-prev'||kind==='page-next'){if(!['assets','clips','audio','text','captions','effects','versions'].includes(handle))return;pages.set(handle,Math.max(0,(pages.get(handle)||0)+(kind==='page-prev'?-1:1)));draw();return;}const identity=identities.get(handle);if(!identity)return;const rows=rowsFor(identity.kind),item=(identity.kind==='asset'?assets:rows).includes(identity.item)?identity.item:null,index=rows.indexOf(item);if(!item)return;\n if(kind==='preview-clip'&&identity.kind==='clip'){previewItem(item,'video',item.trimStart);return;}if(kind==='preview-audio'&&identity.kind==='audio'){previewItem(item,'audio');return;}if(kind==='preview-asset'&&identity.kind==='asset'){previewItem(item,item.kind);return;}if(blocked())return;\n if(kind==='restore-version'&&identity.kind==='version'){commitEdit({...draft,clips:clone(item.clips||[]),audioItems:clone(item.audioItems||[]),captions:clone(item.captions||[]),textOverlays:clone(item.textOverlays||[]),effects:clone(item.effects||[])});draw();return;}\n if(kind==='add-asset'&&identity.kind==='asset'){const media=clone(item),duration=Math.max(.001,Number(media.duration)||Number(media.durationSec)||4);if(media.kind==='audio')commitEdit({...draft,audioItems:[...draft.audioItems,{id:uniqueId('audio'),trackId:'audio',start:0,duration,trimStart:0,volume:1,asset:media,url:media.url}]});else{const start=draft.clips.reduce((end,value)=>Math.max(end,itemFinish(value)),0);commitEdit({...draft,clips:[...draft.clips,{id:uniqueId('clip'),trackId:'video',start,duration,trimStart:0,speed:1,volume:1,asset:media,url:media.url}]});}draw();return;}\n if((kind==='move-clip-up'||kind==='move-clip-down')&&identity.kind==='clip'){const targetIndex=index+(kind==='move-clip-up'?-1:1),target=rows.at(targetIndex);if(targetIndex<0||!target)return;const reordered=rows.slice();reordered.splice(targetIndex,1,item);reordered.splice(index,1,target);replaceRows('clip',reordered);draw();return;}\n if(kind==='duplicate-clip'&&identity.kind==='clip'){const next={...clone(item),id:uniqueId('clip-copy'),start:itemFinish(item)};replaceRows('clip',[...rows.slice(0,index+1),next,...rows.slice(index+1)]);draw();return;}\n if(kind==='split-clip'&&identity.kind==='clip'){const half=Math.max(.001,Number(item.duration)||.001)/2;if(half<.001)return;const second={...clone(item),id:uniqueId('clip-split'),start:(Number(item.start)||0)+half,duration:half,trimStart:(Number(item.trimStart)||0)+half};replaceRows('clip',[...rows.slice(0,index),{...item,duration:half},second,...rows.slice(index+1)]);draw();return;}\n if(kind==='remove-'+identity.kind&&['clip','audio','text','caption','effect'].includes(identity.kind)){const remaining=rows.slice();remaining.splice(index,1);replaceRows(identity.kind,remaining);draw();return;}\n const next={...item};let recognized=true;\n if(identity.kind==='clip'){if(kind==='clip-track')next.trackId=event.value;else if(kind==='clip-start')next.start=Math.max(0,Number(event.value)||0);else if(kind==='clip-duration')next.duration=Math.max(.001,Number(event.value)||.001);else if(kind==='clip-trim')next.trimStart=Math.max(0,Number(event.value)||0);else if(kind==='clip-speed')next.speed=Math.max(.01,Number(event.value)||1);else if(kind==='clip-volume')next.volume=Math.max(0,Number(event.value)||0);else if(kind==='clip-transition'||kind==='clip-transition-duration'){const prior=item.transition&&!Array.isArray(item.transition)?item.transition:{type:'Cut',duration:0};next.transition=kind==='clip-transition'?{...prior,type:event.value}:{...prior,duration:Math.max(0,Number(event.value)||0)};}else if(kind==='clip-opacity')next.transform={...(item.transform&&!Array.isArray(item.transform)?item.transform:{}),opacity:Math.max(0,Math.min(1,Number(event.value)||0))};else if(kind==='clip-reverse')next.reverse=event.checked;else if(kind==='clip-motion'||kind==='clip-easing'){const prior=item.motion&&!Array.isArray(item.motion)?item.motion:{type:'none',easing:'ease-in-out'};next.motion=kind==='clip-motion'?{...prior,type:event.value}:{...prior,easing:event.value};}else recognized=false;}\n else if(identity.kind==='audio'){if(kind==='audio-track')next.trackId=event.value;else if(kind==='audio-start')next.start=Math.max(0,Number(event.value)||0);else if(kind==='audio-duration')next.duration=Math.max(.001,Number(event.value)||.001);else if(kind==='audio-trim'){next.trimStart=Math.max(0,Number(event.value)||0);next.sourceOffset=next.trimStart;}else if(kind==='audio-volume')next.volume=Math.max(0,Number(event.value)||0);else if(kind==='audio-fade-in')next.fadeIn=Math.max(0,Number(event.value)||0);else if(kind==='audio-fade-out')next.fadeOut=Math.max(0,Number(event.value)||0);else if(kind==='audio-ducking')next.ducking=Math.max(0,Math.min(1,Number(event.value)||0));else if(kind==='audio-rate')next.rate=Math.max(.05,Math.min(4,Number(event.value)||1));else if(kind==='audio-loop')next.loop=event.checked;else if(kind==='audio-muted')next.muted=event.checked;else recognized=false;}\n else if(identity.kind==='text'){if(kind==='text-value')next.text=event.value;else if(kind==='text-track')next.trackId=event.value;else if(kind==='text-start')next.start=Math.max(0,Number(event.value)||0);else if(kind==='text-duration')next.duration=Math.max(.001,Number(event.value)||.001);else if(kind==='text-animation')next.animation=event.value;else recognized=false;}\n else if(identity.kind==='caption'){if(kind==='caption-value')next.text=event.value;else if(kind==='caption-track')next.trackId=event.value;else if(kind==='caption-start')next.start=Math.max(0,Number(event.value)||0);else if(kind==='caption-end')next.end=Math.max((Number(item.start)||0)+.001,Number(event.value)||(Number(item.start)||0)+.001);else if(kind==='caption-animation')next.animation=event.value;else recognized=false;}\n else if(identity.kind==='effect'){if(kind==='effect-type')next.type=event.value;else if(kind==='effect-track')next.trackId=event.value;else if(kind==='effect-start')next.start=Math.max(0,Number(event.value)||0);else if(kind==='effect-duration')next.duration=Math.max(.001,Number(event.value)||.001);else if(kind==='effect-intensity')next.intensity=Number(event.value)||0;else recognized=false;}\n else recognized=false;if(recognized){const updated=rows.slice();updated.splice(index,1,next);replaceRows(identity.kind,updated);identities.set(handle,{kind:identity.kind,item:next});}\n});\nbuild.addEventListener('click',()=>{if(blocked())return;const assembled=assemble();const next=source.clips?.length||source.audioItems?.length?assembled:{...assembled,clips:assembled.clips.map(item=>({...item,id:uniqueId('clip')})),audioItems:assembled.audioItems.map(item=>({...item,id:uniqueId('audio')}))};commitEdit(next);draw();});\ndocument.getElementById('addText').addEventListener('click',()=>{if(blocked())return;commitEdit({...draft,textOverlays:[...draft.textOverlays,{id:uniqueId('text'),trackId:'text',text:'New text',start:0,duration:2,animation:'none'}]});draw();});\ndocument.getElementById('addCaption').addEventListener('click',()=>{if(blocked())return;commitEdit({...draft,captions:[...draft.captions,{id:uniqueId('caption'),trackId:'captions',text:'New caption',start:0,end:2,font:'Hanken Grotesk',animation:'none'}]});draw();});\ndocument.getElementById('addEffect').addEventListener('click',()=>{if(blocked())return;commitEdit({...draft,effects:[...draft.effects,{id:uniqueId('effect'),trackId:'video',type:'fade',start:0,duration:1,intensity:1}]});draw();});\nconst undoEdit=redo=>{if(blocked())return;captureHistory();const entry=redo?redoStack.at(-1):undoStack.at(-1);if(!entry)return;try{const next=StillMade.applyDocument(draft,redo?entry.forward:entry.inverse);if(redo){redoStack=redoStack.slice(0,-1);undoStack=[...undoStack,entry];}else{undoStack=undoStack.slice(0,-1);redoStack=[...redoStack,entry];}commitEdit(next,false);draw();status.textContent=redo?'Your checked change was restored.':'Your checked change was undone. Teammate edits are retained.';}catch(error){status.textContent='Cannot '+(redo?'redo':'undo')+' because the same content changed. The shared timeline is retained.';}};\ndocument.getElementById('undo').addEventListener('click',()=>undoEdit(false));\ndocument.getElementById('redo').addEventListener('click',()=>undoEdit(true));\ndocument.getElementById('snapshot').addEventListener('click',()=>{if(blocked())return;const version={id:uniqueId('timeline-version'),label:'Timeline version '+String((draft.timelineVersions||[]).length+1),createdAt:new Date().toISOString(),clips:clone(draft.clips),audioItems:clone(draft.audioItems),captions:clone(draft.captions),textOverlays:clone(draft.textOverlays),effects:clone(draft.effects)};commitEdit({...draft,timelineVersions:[...(draft.timelineVersions||[]),version].slice(-50)});draw();});\ndocument.getElementById('reset').addEventListener('click',()=>{if(blocked())return;commitEdit(normalize(clone(source)));draw();status.textContent='Reset to the current project timeline is being shared.';});\nconst validateEditor=value=>{if(!value||Array.isArray(value)||value.schemaVersion!==1||typeof value.activeTimelineId!=='string'||!value.activeTimelineId)throw Error('Editor JSON must be a version 1 document with an active timeline ID.');[value.tracks,value.clips,value.audioItems,value.captions,value.textOverlays,value.stickers,value.effects,value.markers,value.fxEvents,value.liveOverlays,value.mediaItems,value.timelineVersions].forEach(rows=>{if(rows===undefined)return;if(!Array.isArray(rows)||rows.length>10000||rows.some(item=>!item||Array.isArray(item)||typeof item.id!=='string'||!item.id||item.id.length>200)||new Set(rows.map(item=>item.id)).size!==rows.length)throw Error('Each Editor collection needs at most 10,000 items with distinct, nonempty IDs of at most 200 characters.');});return normalize(value);};\ndocument.getElementById('applyRaw').addEventListener('click',async()=>{if(blocked()||scratch.text===null)return;const requested=JSON.stringify(scratch);busy=true;draw();try{await scratchShared.flush();if(JSON.stringify(scratch)!==requested)throw Error('The shared JSON draft changed. Review the latest text before applying it.');if(!allowed())throw Error('Editing access changed. JSON text is retained.');const pending=scratch,value=validateEditor(JSON.parse(pending.text)),edits=StillMade.diffDocument(pending.base||draft,value);commitEdit(StillMade.applyDocument(draft,edits));await shared.flush();if(!allowed())throw Error('Editing access changed. JSON text is retained.');if(JSON.stringify(scratch)===requested){scratch={text:null,base:null};rawStale=false;scratchShared.changed();await scratchShared.flush();}status.textContent='Complete Editor data applied to the shared timeline.';}catch(error){status.textContent=error.message;}finally{busy=false;draw();}});\nconst editOptions=next=>{if(blocked())return;options=next;optionsShared.changed();revision+=1;};\ndocument.getElementById('renderFps').addEventListener('change',()=>editOptions({...options,renderFps:Number(document.getElementById('renderFps').value)}));\ndocument.getElementById('renderResolution').addEventListener('change',()=>editOptions({...options,renderResolution:Number(document.getElementById('renderResolution').value)}));\ndocument.getElementById('renderQuality').addEventListener('change',()=>editOptions({...options,renderQuality:document.getElementById('renderQuality').value}));\nconst runAction=async(render)=>{if(blocked()||scratch.text!==null)return;busy=true;draw();status.textContent=render?'Preparing the exact final render…':'Checking the complete Editor timeline…';try{await shared.flush();await scratchShared.flush();await optionsShared.flush();if(!allowed()||scratch.text!==null)throw Error('Editing access or the shared draft changed. The action was not run.');const started=revision;await StillMade.run({editor:clone(source),updatedEditor:clone(draft),commit:!render,...render?{action:'render',...options}:{}});status.textContent=started!==revision?'A result is ready below; newer shared edits were retained.':render?'Final render is ready below. Choose Render final file to start the device renderer.':'Editor changes are ready below. Review the before and after, then choose Apply.';}catch(error){status.textContent=error.message||'The Editor action could not be prepared.';}finally{busy=false;draw();}};\nsave.addEventListener('click',()=>runAction(false));\ndocument.getElementById('renderFinal').addEventListener('click',()=>runAction(true));\nconst recover=async(action)=>{if(busy||!shared||![access,scratchAccess,optionsAccess].every(value=>value.canEdit&&!value.pending))return;busy=true;draw();try{await action();sourceResetArmed=false;undoStack=[];redoStack=[];historyBefore=null;historyAfter=null;status.textContent='Shared Editor draft recovered.';}catch(error){status.textContent=error.message;}finally{busy=false;draw();}};\ndocument.getElementById('retryShared').addEventListener('click',()=>recover(async()=>{await shared.retry();await scratchShared.retry();await optionsShared.retry();}));\ndocument.getElementById('discardShared').addEventListener('click',()=>recover(async()=>{await shared.discard();await scratchShared.discard();await optionsShared.discard();}));\ndocument.getElementById('resetSharedSource').addEventListener('click',()=>{sourceResetArmed=true;drawRecovery();});\ndocument.getElementById('confirmSourceReset').addEventListener('click',()=>{if(!sourceResetArmed||!access.sourceConflict)return;sourceResetArmed=false;recover(()=>shared.resetSharedSource({discardSharedEdits:true}));});\nStillMade.onShared(value=>{sourceResetArmed=false;drawRecovery();const outputs=value.outputs||{};document.getElementById('sharedResult').textContent=outputs.mediaAction?'A final-render plan is available in the shared result below.':outputs.editor?'The latest reviewed Editor result is available below.':'';});\nStillMade.onInput(values=>{sourceResetArmed=false;source=clone(values.editor||empty);assets=Array.isArray(values.assets)?clone(values.assets):[];previewSerial+=1;if(previewUrl){StillMade.releaseMedia(previewUrl);previewUrl='';}imagePreview.hidden=true;videoPreview.hidden=true;audioPreview.hidden=true;const next=normalize(values.updatedEditor&&(values.updatedEditor.clips?.length||values.updatedEditor.audioItems?.length||values.updatedEditor.timelineVersions?.length)?clone(values.updatedEditor):source.clips?.length||source.audioItems?.length?clone(source):assemble());if(!shared){draft=next;shared=StillMade.connectDocument({key:'editorDocument',source:next,read:()=>draft,replace:value=>{sourceResetArmed=false;captureHistory();draft=value;revision+=1;if(scratch.text!==null)rawStale=true;draw();}});scratchShared=StillMade.connectDocument({key:'editorScratch',source:{text:null,base:null},read:()=>scratch,replace:value=>{scratch=value;rawStale=scratch.text!==null;if(scratch.text!==null&&rawIsOpen()&&raw.value!==scratch.text)raw.value=scratch.text;else schedulePreview();drawRecovery();}});optionsShared=StillMade.connectDocument({key:'editorOptions',source:{renderFps:30,renderResolution:1080,renderQuality:'high'},read:()=>options,replace:value=>{options=value;revision+=1;drawOptions();}});shared.onStatus(value=>{const redraw=access.canEdit!==value.canEdit||access.error!==value.error;access=value;if(redraw)draw();else drawRecovery();});scratchShared.onStatus(value=>{const redraw=scratchAccess.canEdit!==value.canEdit||scratchAccess.error!==value.error;scratchAccess=value;if(redraw)draw();else drawRecovery();});optionsShared.onStatus(value=>{const redraw=optionsAccess.canEdit!==value.canEdit||optionsAccess.error!==value.error;optionsAccess=value;if(redraw)draw();else drawRecovery();});playhead.value='0';}else shared.replaceSource(next);revision+=1;draw();status.textContent=access.error||'Timeline changes, JSON drafts and export settings are shared live. Playback and previews are personal.';});\n"},"tests":[{"name":"Retain the complete Editor and all internal tools","input":{"editor":{"schemaVersion":1,"activeTimelineId":"timeline-1","aspect":"16:9","tracks":[{"id":"video","type":"video"},{"id":"audio","type":"audio"}],"clips":[{"id":"clip-1","trackId":"video","start":0,"duration":4,"url":"https://media.example/clip.mp4","motion":{"type":"push-in"},"color":{"brightness":5},"transition":{"type":"Fade","duration":0.5}}],"audioItems":[{"id":"music-1","trackId":"audio","start":0,"duration":4,"url":"https://media.example/music.mp3","volume":70}],"captions":[{"id":"caption-1","trackId":"captions","text":"Hello","start":0,"end":1}],"textOverlays":[{"id":"title-1","text":"Opening","start":0,"duration":2}],"effects":[{"id":"effect-1","type":"grain","start":0,"duration":4}],"fullscreenFrames":[{"id":"mg-frame-1","title":"Motion graphic"}],"figureSweeps":[{"id":"mg-figure-1","value":42}],"audioScore":{"wholeMusic":{"versions":[{"url":"https://media.example/score.mp3"}],"idx":0}},"timelineVersions":[{"id":"timeline-1","name":"Main","clips":[]}],"exportSettings":{"resolution":"1080p"},"custom":{"keep":true}},"updatedEditor":{"schemaVersion":1,"activeTimelineId":"timeline-1","tracks":[],"clips":[],"audioItems":[],"captions":[],"textOverlays":[],"effects":[]},"commit":false},"expected":{"editor":{"schemaVersion":1,"activeTimelineId":"timeline-1","aspect":"16:9","tracks":[{"id":"video","type":"video"},{"id":"audio","type":"audio"}],"clips":[{"id":"clip-1","trackId":"video","start":0,"duration":4,"url":"https://media.example/clip.mp4","motion":{"type":"push-in"},"color":{"brightness":5},"transition":{"type":"Fade","duration":0.5}}],"audioItems":[{"id":"music-1","trackId":"audio","start":0,"duration":4,"url":"https://media.example/music.mp3","volume":70}],"captions":[{"id":"caption-1","trackId":"captions","text":"Hello","start":0,"end":1}],"textOverlays":[{"id":"title-1","text":"Opening","start":0,"duration":2}],"effects":[{"id":"effect-1","type":"grain","start":0,"duration":4}],"fullscreenFrames":[{"id":"mg-frame-1","title":"Motion graphic"}],"figureSweeps":[{"id":"mg-figure-1","value":42}],"audioScore":{"wholeMusic":{"versions":[{"url":"https://media.example/score.mp3"}],"idx":0}},"timelineVersions":[{"id":"timeline-1","name":"Main","clips":[]}],"exportSettings":{"resolution":"1080p"},"custom":{"keep":true}},"edit":{"schemaVersion":1,"title":"Update Editor timeline timeline-1","field":"editor","before":{"schemaVersion":1,"activeTimelineId":"timeline-1","aspect":"16:9","tracks":[{"id":"video","type":"video"},{"id":"audio","type":"audio"}],"clips":[{"id":"clip-1","trackId":"video","start":0,"duration":4,"url":"https://media.example/clip.mp4","motion":{"type":"push-in"},"color":{"brightness":5},"transition":{"type":"Fade","duration":0.5}}],"audioItems":[{"id":"music-1","trackId":"audio","start":0,"duration":4,"url":"https://media.example/music.mp3","volume":70}],"captions":[{"id":"caption-1","trackId":"captions","text":"Hello","start":0,"end":1}],"textOverlays":[{"id":"title-1","text":"Opening","start":0,"duration":2}],"effects":[{"id":"effect-1","type":"grain","start":0,"duration":4}],"fullscreenFrames":[{"id":"mg-frame-1","title":"Motion graphic"}],"figureSweeps":[{"id":"mg-figure-1","value":42}],"audioScore":{"wholeMusic":{"versions":[{"url":"https://media.example/score.mp3"}],"idx":0}},"timelineVersions":[{"id":"timeline-1","name":"Main","clips":[]}],"exportSettings":{"resolution":"1080p"},"custom":{"keep":true}},"after":{"schemaVersion":1,"activeTimelineId":"timeline-1","aspect":"16:9","tracks":[{"id":"video","type":"video"},{"id":"audio","type":"audio"}],"clips":[{"id":"clip-1","trackId":"video","start":0,"duration":4,"url":"https://media.example/clip.mp4","motion":{"type":"push-in"},"color":{"brightness":5},"transition":{"type":"Fade","duration":0.5}}],"audioItems":[{"id":"music-1","trackId":"audio","start":0,"duration":4,"url":"https://media.example/music.mp3","volume":70}],"captions":[{"id":"caption-1","trackId":"captions","text":"Hello","start":0,"end":1}],"textOverlays":[{"id":"title-1","text":"Opening","start":0,"duration":2}],"effects":[{"id":"effect-1","type":"grain","start":0,"duration":4}],"fullscreenFrames":[{"id":"mg-frame-1","title":"Motion graphic"}],"figureSweeps":[{"id":"mg-figure-1","value":42}],"audioScore":{"wholeMusic":{"versions":[{"url":"https://media.example/score.mp3"}],"idx":0}},"timelineVersions":[{"id":"timeline-1","name":"Main","clips":[]}],"exportSettings":{"resolution":"1080p"},"custom":{"keep":true}}}}},{"name":"Prepare one exact complete Editor update","input":{"editor":{"schemaVersion":1,"activeTimelineId":"timeline-1","tracks":[{"id":"video","type":"video"}],"clips":[{"id":"clip-1","trackId":"video","start":0,"duration":4,"url":"https://media.example/clip.mp4","volume":100}],"audioItems":[],"captions":[],"textOverlays":[{"id":"title-1","text":"Old","start":0,"duration":2}],"effects":[]},"updatedEditor":{"schemaVersion":1,"activeTimelineId":"timeline-1","tracks":[{"id":"video","type":"video"}],"clips":[{"id":"clip-1","trackId":"video","start":1,"duration":3,"url":"https://media.example/clip.mp4","volume":80}],"audioItems":[],"captions":[],"textOverlays":[{"id":"title-1","text":"New","start":1,"duration":2}],"effects":[]},"commit":true},"expected":{"editor":{"schemaVersion":1,"activeTimelineId":"timeline-1","tracks":[{"id":"video","type":"video"}],"clips":[{"id":"clip-1","trackId":"video","start":1,"duration":3,"url":"https://media.example/clip.mp4","volume":80}],"audioItems":[],"captions":[],"textOverlays":[{"id":"title-1","text":"New","start":1,"duration":2}],"effects":[]},"edit":{"schemaVersion":1,"title":"Update Editor timeline timeline-1","field":"editor","before":{"schemaVersion":1,"activeTimelineId":"timeline-1","tracks":[{"id":"video","type":"video"}],"clips":[{"id":"clip-1","trackId":"video","start":0,"duration":4,"url":"https://media.example/clip.mp4","volume":100}],"audioItems":[],"captions":[],"textOverlays":[{"id":"title-1","text":"Old","start":0,"duration":2}],"effects":[]},"after":{"schemaVersion":1,"activeTimelineId":"timeline-1","tracks":[{"id":"video","type":"video"}],"clips":[{"id":"clip-1","trackId":"video","start":1,"duration":3,"url":"https://media.example/clip.mp4","volume":80}],"audioItems":[],"captions":[],"textOverlays":[{"id":"title-1","text":"New","start":1,"duration":2}],"effects":[]}}}},{"name":"Build initial timeline from exact project media versions","input":{"editor":{"schemaVersion":1,"activeTimelineId":"timeline-1","tracks":[],"clips":[],"audioItems":[],"captions":[],"textOverlays":[],"effects":[]},"assets":[{"kind":"video","assetId":"video-1","versionId":"v1","url":"https://media.example/video.mp4","duration":5},{"kind":"image","assetId":"image-1","versionId":"v2","url":"https://media.example/image.png"},{"kind":"audio","assetId":"music-1","versionId":"v1","url":"https://media.example/music.mp3","duration":9}],"updatedEditor":{"schemaVersion":1,"activeTimelineId":"timeline-1","tracks":[],"clips":[],"audioItems":[],"captions":[],"textOverlays":[],"effects":[]},"commit":false,"action":"none"},"expected":{"editor":{"schemaVersion":1,"activeTimelineId":"timeline-1","tracks":[{"id":"video","kind":"video","label":"Video"},{"id":"audio","kind":"audio","label":"Audio"}],"clips":[{"id":"clip-1-video-1","trackId":"video","start":0,"duration":5,"trimStart":0,"speed":1,"volume":1,"asset":{"kind":"video","assetId":"video-1","versionId":"v1","url":"https://media.example/video.mp4","duration":5},"url":"https://media.example/video.mp4"},{"id":"clip-2-image-1","trackId":"video","start":5,"duration":4,"trimStart":0,"speed":1,"volume":1,"asset":{"kind":"image","assetId":"image-1","versionId":"v2","url":"https://media.example/image.png"},"url":"https://media.example/image.png"}],"audioItems":[{"id":"audio-1-music-1","trackId":"audio","start":0,"duration":9,"trimStart":0,"volume":1,"asset":{"kind":"audio","assetId":"music-1","versionId":"v1","url":"https://media.example/music.mp3","duration":9},"url":"https://media.example/music.mp3"}],"captions":[],"textOverlays":[],"effects":[],"targetDuration":9},"edit":{"schemaVersion":1,"title":"Update Editor timeline timeline-1","field":"editor","before":{"schemaVersion":1,"activeTimelineId":"timeline-1","tracks":[],"clips":[],"audioItems":[],"captions":[],"textOverlays":[],"effects":[]},"after":{"schemaVersion":1,"activeTimelineId":"timeline-1","tracks":[{"id":"video","kind":"video","label":"Video"},{"id":"audio","kind":"audio","label":"Audio"}],"clips":[{"id":"clip-1-video-1","trackId":"video","start":0,"duration":5,"trimStart":0,"speed":1,"volume":1,"asset":{"kind":"video","assetId":"video-1","versionId":"v1","url":"https://media.example/video.mp4","duration":5},"url":"https://media.example/video.mp4"},{"id":"clip-2-image-1","trackId":"video","start":5,"duration":4,"trimStart":0,"speed":1,"volume":1,"asset":{"kind":"image","assetId":"image-1","versionId":"v2","url":"https://media.example/image.png"},"url":"https://media.example/image.png"}],"audioItems":[{"id":"audio-1-music-1","trackId":"audio","start":0,"duration":9,"trimStart":0,"volume":1,"asset":{"kind":"audio","assetId":"music-1","versionId":"v1","url":"https://media.example/music.mp3","duration":9},"url":"https://media.example/music.mp3"}],"captions":[],"textOverlays":[],"effects":[],"targetDuration":9}}}},{"name":"Retain valid legacy native IDs and nullable transforms","input":{"editor":{"schemaVersion":1,"activeTimelineId":"timeline-1","tracks":[],"clips":[],"audioItems":[],"captions":[],"textOverlays":[],"effects":[]},"updatedEditor":{"schemaVersion":1,"activeTimelineId":"timeline-1","tracks":[],"clips":[{"id":"界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界\u0001","start":0,"duration":4,"transform":null}],"audioItems":[],"captions":[],"textOverlays":[],"effects":[],"custom":{"keep":true}},"commit":true},"expected":{"editor":{"schemaVersion":1,"activeTimelineId":"timeline-1","tracks":[],"clips":[{"id":"界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界\u0001","start":0,"duration":4,"transform":null}],"audioItems":[],"captions":[],"textOverlays":[],"effects":[],"custom":{"keep":true}},"edit":{"schemaVersion":1,"title":"Update Editor timeline timeline-1","field":"editor","before":{"schemaVersion":1,"activeTimelineId":"timeline-1","tracks":[],"clips":[],"audioItems":[],"captions":[],"textOverlays":[],"effects":[]},"after":{"schemaVersion":1,"activeTimelineId":"timeline-1","tracks":[],"clips":[{"id":"界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界界\u0001","start":0,"duration":4,"transform":null}],"audioItems":[],"captions":[],"textOverlays":[],"effects":[],"custom":{"keep":true}}}}}],"portable":{"format":"stillmade-block/1","dependencies":[],"sources":[{"kind":"original","license":"MIT","paths":["LICENSE","src/run.js","src/view.json"],"evidence":["upstream/0/LICENSE","upstream/0/src/run.js","upstream/0/src/view.json"],"changes":"Original implementation declared by the packager; no reused repository or dependency code."}],"files":{"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","upstream/0/src/run.js":"const copy = value => JSON.parse(JSON.stringify(value));\nconst source = copy(input.editor || {schemaVersion:1,activeTimelineId:'timeline-1',tracks:[],clips:[],audioItems:[],captions:[],textOverlays:[],effects:[]});\nconst assets=Array.isArray(input.assets)?input.assets.filter(asset=>asset&&['image','video','audio'].includes(asset.kind)&&asset.assetId&&asset.versionId&&asset.url).slice(0,500):[];\nconst assembled=copy(source);if(!assembled.clips?.length&&!assembled.audioItems?.length&&assets.length){assembled.tracks=[{id:'video',kind:'video',label:'Video'},{id:'audio',kind:'audio',label:'Audio'}];let cursor=0;assembled.clips=assets.filter(asset=>asset.kind!=='audio').map((asset,index)=>{const duration=Math.max(.001,Number(asset.duration)||Number(asset.durationSec)||4),clip={id:'clip-'+String(index+1)+'-'+String(asset.assetId),trackId:'video',start:cursor,duration,trimStart:0,speed:1,volume:1,asset:copy(asset),url:asset.url};cursor+=duration;return clip});assembled.audioItems=assets.filter(asset=>asset.kind==='audio').map((asset,index)=>({id:'audio-'+String(index+1)+'-'+String(asset.assetId),trackId:'audio',start:0,duration:Math.max(.001,Number(asset.duration)||Number(asset.durationSec)||cursor||1),trimStart:0,volume:1,asset:copy(asset),url:asset.url}));assembled.targetDuration=Math.max(cursor,...assembled.audioItems.map(item=>item.duration));}\nconst candidate = input.commit || input.action === 'render' ? copy(input.updatedEditor) : assembled;\nif (!candidate || Array.isArray(candidate) || candidate.schemaVersion !== 1 || typeof candidate.activeTimelineId !== 'string' || !candidate.activeTimelineId) throw new Error('Keep the complete version 1 Editor document and active timeline identity.');\nconst names=['tracks','clips','audioItems','captions','textOverlays','stickers','effects','markers','fxEvents','liveOverlays','mediaItems','timelineVersions'];\nif (!names.every(name => candidate[name] === undefined || Array.isArray(candidate[name]) && candidate[name].length <= 10000 && candidate[name].every(item => item && typeof item === 'object' && !Array.isArray(item) && typeof item.id === 'string' && item.id.length > 0 && item.id.length <= 200) && new Set(candidate[name].map(item => item.id)).size === candidate[name].length)) throw new Error('Editor collections need valid distinct retained item IDs.');\nconst itemEnd=item=>Math.max(0,Number(item.start)||0)+Math.max(.001,Number(item.duration)||Number(item.durationMs)/1000||.001);\nconst outputs={editor:candidate,edit:{schemaVersion:1,title:`Update Editor timeline ${candidate.activeTimelineId}`,field:'editor',before:source,after:candidate}};\nif(input.action==='render'){const clips=Array.isArray(candidate.clips)?candidate.clips:[],audio=Array.isArray(candidate.audioItems)?candidate.audioItems:[],overlays=[...(candidate.captions||[]),...(candidate.textOverlays||[]),...(candidate.effects||[])],targetDuration=Math.max(.001,Number(candidate.targetDuration)||0,...clips.map(itemEnd),...audio.map(itemEnd),...overlays.map(itemEnd));outputs.mediaAction={schemaVersion:1,kind:'stillmade.media-action',operation:'final.render',request:{schemaVersion:1,kind:'video',timeline:{...candidate,clips,audio,overlays,targetDuration},settings:{fps:input.renderFps,resolution:input.renderResolution,quality:input.renderQuality},source:{id:'sm.editor',version:'2.2.9'}},resultPort:null};}\nreturn outputs;\n","upstream/0/src/view.json":"{\n  \"html\": \"<main class=\\\"editor-workspace\\\">\\n  <header><div><small>Production workspace</small><h1>Editor</h1><p id=\\\"summary\\\">Loading the complete timeline…</p></div><div class=\\\"actions\\\"><button id=\\\"build\\\" type=\\\"button\\\" class=\\\"secondary\\\">Build from project media</button><button id=\\\"undo\\\" type=\\\"button\\\" class=\\\"secondary\\\">Undo</button><button id=\\\"redo\\\" type=\\\"button\\\" class=\\\"secondary\\\">Redo</button><button id=\\\"snapshot\\\" type=\\\"button\\\" class=\\\"secondary\\\">Save version</button><button id=\\\"reset\\\" type=\\\"button\\\" class=\\\"secondary\\\">Reset changes</button><button id=\\\"addText\\\" type=\\\"button\\\" class=\\\"secondary\\\">Add text</button><button id=\\\"addCaption\\\" type=\\\"button\\\" class=\\\"secondary\\\">Add caption</button><button id=\\\"save\\\" type=\\\"button\\\" data-stillmade-action=\\\"run\\\">Review changes</button></div></header>\\n  <output id=\\\"status\\\" role=\\\"status\\\" aria-live=\\\"polite\\\"></output>\\n  <p id=\\\"sharedResult\\\" class=\\\"hint\\\" aria-live=\\\"polite\\\"></p>\\n  <section id=\\\"recovery\\\" hidden><h2>Keep your work safe</h2><p id=\\\"recoveryMessage\\\"></p><div class=\\\"row\\\"><button id=\\\"retryShared\\\" type=\\\"button\\\" class=\\\"secondary\\\">Retry checked changes</button><button id=\\\"discardShared\\\" type=\\\"button\\\" class=\\\"secondary\\\">Discard my unshared changes</button><button id=\\\"resetSharedSource\\\" type=\\\"button\\\" class=\\\"secondary\\\" hidden>Review updated project timeline</button></div><p>The project source changed. Starting from it replaces the shared timeline draft for everyone. Unfinished JSON is retained for separate review.</p><button id=\\\"confirmSourceReset\\\" type=\\\"button\\\" hidden>Replace everyone's shared draft with the updated project timeline</button></section>\\n  <section><h2>Timeline</h2><div class=\\\"grid\\\"><label>Active timeline<input id=\\\"timelineId\\\" type=\\\"text\\\" disabled></label><label>Aspect ratio<input id=\\\"aspect\\\" type=\\\"text\\\"></label><label>Playhead (seconds)<input id=\\\"playhead\\\" type=\\\"range\\\" min=\\\"0\\\" max=\\\"1\\\" step=\\\"0.05\\\" value=\\\"0\\\"></label></div><p id=\\\"timecode\\\" class=\\\"hint\\\"></p><div id=\\\"composition\\\" class=\\\"cards\\\"></div></section>\\n  <section><h2>Project media</h2><p class=\\\"hint\\\">Preview an exact saved version, then add it to this timeline without leaving the Block.</p><img id=\\\"imagePreview\\\" alt=\\\"Selected project media preview\\\" hidden><video id=\\\"videoPreview\\\" controls hidden></video><audio id=\\\"audioPreview\\\" controls hidden></audio><div id=\\\"assets\\\" class=\\\"cards media-grid\\\"></div></section>\\n  <section><h2>Video</h2><div id=\\\"clips\\\" class=\\\"cards\\\"></div></section>\\n  <section><h2>Audio and scoring</h2><div id=\\\"audio\\\" class=\\\"cards\\\"></div></section>\\n  <section><h2>Saved timeline versions</h2><div id=\\\"versions\\\" class=\\\"cards\\\"></div></section>\\n  <section><h2>Text and motion graphics</h2><div id=\\\"text\\\" class=\\\"cards\\\"></div><p id=\\\"graphics\\\" class=\\\"hint\\\"></p></section>\\n  <section><h2>Captions</h2><div id=\\\"captions\\\" class=\\\"cards\\\"></div></section>\\n  <section><div class=\\\"card-head\\\"><h2>Effects</h2><button id=\\\"addEffect\\\" type=\\\"button\\\" class=\\\"secondary\\\">Add effect</button></div><div id=\\\"effects\\\" class=\\\"cards\\\"></div></section>\\n  <details id=\\\"rawDetails\\\"><summary>Complete Editor data</summary><p>All timeline lanes, motion graphics, grade settings, transitions, scoring, versions, and export settings are retained. Use this editor for fields not shown on the lane cards.</p><p id=\\\"rawNotice\\\" class=\\\"hint\\\"></p><textarea id=\\\"raw\\\" class=\\\"raw\\\" spellcheck=\\\"false\\\" aria-label=\\\"Complete Editor data\\\"></textarea><div class=\\\"row\\\"><button id=\\\"applyRaw\\\" type=\\\"button\\\" class=\\\"secondary\\\">Apply JSON to workspace</button><button id=\\\"discardRaw\\\" type=\\\"button\\\" class=\\\"secondary\\\">Discard shared JSON draft</button></div></details>\\n<section><h2>Final export</h2><p>Render this exact timeline through StillMade’s approved device renderer.</p><div class=\\\"grid\\\"><label>Frame rate<select id=\\\"renderFps\\\"><option value=\\\"24\\\">24 fps</option><option value=\\\"30\\\" selected>30 fps</option><option value=\\\"60\\\">60 fps</option></select></label><label>Resolution<select id=\\\"renderResolution\\\"><option value=\\\"720\\\">720p</option><option value=\\\"1080\\\" selected>1080p</option><option value=\\\"2160\\\">2160p</option></select></label><label>Quality<select id=\\\"renderQuality\\\"><option value=\\\"standard\\\">Standard</option><option value=\\\"high\\\" selected>High</option></select></label></div><button id=\\\"renderFinal\\\" type=\\\"button\\\">Render final video</button></section></main>\\n\",\n  \"css\": \".editor-workspace{max-width:1160px;margin:0 auto;padding:8px;color:var(--fg)}header,.actions,.card-head,.row{display:flex;align-items:center;gap:10px;flex-wrap:wrap}header{justify-content:space-between;margin-bottom:14px}header h1{margin:2px 0}header p,header small,details p,output,.hint{color:var(--fg-muted)}section,details{margin:12px 0;padding:16px;border:1px solid var(--border-tok);border-radius:var(--r-3);background:var(--bg-elev-1)}section h2{margin:0 0 10px}.cards{display:grid;gap:10px}.card{padding:12px;border:1px solid var(--border-tok);border-radius:var(--r-2);background:var(--bg)}.card-head{justify-content:space-between}.card h3{margin:0;font-size:16px}.grid{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:10px}label{display:grid;gap:5px;margin:7px 0;font-size:13px;font-weight:600}input,textarea{width:100%;background:var(--bg)}textarea{min-height:70px;resize:vertical}.wide{grid-column:1/-1}.secondary{background:transparent;color:var(--fg)}.danger{background:transparent;color:var(--signal)}.raw{min-height:440px;font-family:var(--font-mono);font-size:12px}output{display:block;min-height:24px;margin:8px 2px}\\n@media(max-width:900px){.grid{grid-template-columns:repeat(2,minmax(0,1fr))}}@media(max-width:560px){.editor-workspace{padding:0}.grid{grid-template-columns:1fr}.actions{width:100%}.actions button{flex:1}section,details{padding:12px}}\\n.media-grid{grid-template-columns:repeat(3,minmax(0,1fr))}.media-card p{margin:4px 0;color:var(--fg-muted)}video,audio{display:block;width:100%;margin:10px 0}video[hidden],audio[hidden]{display:none}select{width:100%;background:var(--bg)}\\n.card,.grid>*{min-width:0}.card h3,.card strong,.card p{overflow-wrap:anywhere}img{max-width:100%;height:auto}@media(max-width:560px){.media-grid{grid-template-columns:1fr}}\\n\",\n  \"javascript\": \"const status=document.getElementById('status'),summary=document.getElementById('summary'),raw=document.getElementById('raw'),save=document.getElementById('save'),aspect=document.getElementById('aspect'),timelineId=document.getElementById('timelineId'),graphics=document.getElementById('graphics'),build=document.getElementById('build'),imagePreview=document.getElementById('imagePreview'),videoPreview=document.getElementById('videoPreview'),audioPreview=document.getElementById('audioPreview'),playhead=document.getElementById('playhead'),timecode=document.getElementById('timecode');\\nconst empty={schemaVersion:1,activeTimelineId:'timeline-1',tracks:[],clips:[],audioItems:[],captions:[],textOverlays:[],effects:[]};\\nlet source=empty,draft=empty,assets=[],previewUrl='',previewSerial=0,busy=false,revision=0;\\nlet shared=null,scratchShared=null,optionsShared=null,scratch={text:null,base:null},options={renderFps:30,renderResolution:1080,renderQuality:'high'};\\nlet access={canEdit:false,error:null},scratchAccess={canEdit:false,error:null},optionsAccess={canEdit:false,error:null},sourceResetArmed=false,rawBaseline=null,rawStale=false;\\nlet undoStack=[],redoStack=[],historyBefore=null,historyAfter=null;\\nconst tokens=new Map(),identities=new Map(),pages=new Map();\\nconst clone=value=>JSON.parse(JSON.stringify(value));\\nconst text=value=>value===undefined||value===null?'':String(value);\\nconst uniqueId=prefix=>prefix+'-'+Date.now().toString(36)+'-'+Math.random().toString(36).slice(2,14);\\nconst blocked=()=>busy||!shared||!scratchShared||!optionsShared||!access.canEdit||!scratchAccess.canEdit||!optionsAccess.canEdit||!!access.error||!!scratchAccess.error||!!optionsAccess.error;\\nconst allowed=()=>access.canEdit&&scratchAccess.canEdit&&optionsAccess.canEdit&&!access.error&&!scratchAccess.error&&!optionsAccess.error;\\nconst token=(kind,item)=>{const key=JSON.stringify(kind==='asset'?[kind,item.kind,item.assetId,item.versionId]:[kind,item.id]);if(!tokens.has(key)){const value='i'+String(tokens.size+1);tokens.set(key,value);}const handle=tokens.get(key);identities.set(handle,{kind,item});return handle;};\\nconst normalize=value=>({...value,tracks:Array.isArray(value.tracks)?value.tracks:[],clips:Array.isArray(value.clips)?value.clips:[],audioItems:Array.isArray(value.audioItems)?value.audioItems:[],captions:Array.isArray(value.captions)?value.captions:[],textOverlays:Array.isArray(value.textOverlays)?value.textOverlays:[],effects:Array.isArray(value.effects)?value.effects:[]});\\nconst assemble=()=>{const next=clone(source),media=assets.filter(asset=>asset&&['image','video','audio'].includes(asset.kind)&&asset.assetId&&asset.versionId&&asset.url).slice(0,500);if(next.clips?.length||next.audioItems?.length||!media.length)return normalize(next);next.tracks=[{id:'video',kind:'video',label:'Video'},{id:'audio',kind:'audio',label:'Audio'}];let cursor=0;next.clips=media.filter(asset=>asset.kind!=='audio').map((asset,index)=>{const duration=Math.max(.001,Number(asset.duration)||Number(asset.durationSec)||4),clip={id:'clip-'+String(index+1)+'-'+text(asset.assetId),trackId:'video',start:cursor,duration,trimStart:0,speed:1,volume:1,asset:clone(asset),url:asset.url};cursor+=duration;return clip;});next.audioItems=media.filter(asset=>asset.kind==='audio').map((asset,index)=>({id:'audio-'+String(index+1)+'-'+text(asset.assetId),trackId:'audio',start:0,duration:Math.max(.001,Number(asset.duration)||Number(asset.durationSec)||cursor||1),trimStart:0,volume:1,asset:clone(asset),url:asset.url}));next.targetDuration=Math.max(cursor,...next.audioItems.map(item=>item.duration));return normalize(next);};\\nconst field=(label,action,value,type='text',wide=false)=>({tag:'label',className:wide?'wide':'',children:[{tag:'span',text:label},{tag:'input',id:'control-'+action.replace(':','-'),type,action,value:Number.isFinite(value)?value:text(value),disabled:blocked(),label}]});\\nconst remove=action=>({tag:'button',action,text:'Remove',className:'danger',disabled:blocked()});\\nconst assetCard=(item,index)=>({tag:'article',id:'card-'+token('asset',item),className:'card media-card',children:[{tag:'strong',text:text(item.name)||text(item.assetId)},{tag:'p',text:text(item.kind)+' · version '+text(item.versionId)},{tag:'div',className:'row',children:[{tag:'button',action:'preview-asset:'+token('asset',item),text:'Preview',className:'secondary',disabled:false},{tag:'button',action:'add-asset:'+token('asset',item),text:item.kind==='audio'?'Add audio':'Add clip',className:'secondary',disabled:blocked()}]}]});\\nconst clipCard=(item,index)=>({tag:'article',id:'card-'+token('clip',item),className:'card',children:[{tag:'div',className:'card-head',children:[{tag:'h3',text:text(item.label)||text(item.name)||text(item.id)},{tag:'div',className:'row',children:[{tag:'button',action:'preview-clip:'+token('clip',item),text:'Preview',className:'secondary',disabled:!(item.asset||item.assetId)},{tag:'button',action:'move-clip-up:'+token('clip',item),text:'Up',className:'secondary',disabled:blocked()||index===0},{tag:'button',action:'move-clip-down:'+token('clip',item),text:'Down',className:'secondary',disabled:blocked()||index===draft.clips.length-1},{tag:'button',action:'split-clip:'+token('clip',item),text:'Split',className:'secondary',disabled:blocked()||Number(item.duration)<=0.002},{tag:'button',action:'duplicate-clip:'+token('clip',item),text:'Duplicate',className:'secondary',disabled:blocked()},remove('remove-clip:'+token('clip',item))]}]},{tag:'div',className:'grid',children:[field('Track','clip-track:'+token('clip',item),item.trackId),field('Start (seconds)','clip-start:'+token('clip',item),item.start,'number'),field('Length (seconds)','clip-duration:'+token('clip',item),item.duration,'number'),field('Trim start','clip-trim:'+token('clip',item),item.trimStart,'number'),field('Speed','clip-speed:'+token('clip',item),item.speed,'number'),field('Volume','clip-volume:'+token('clip',item),item.volume,'number'),field('Transition','clip-transition:'+token('clip',item),item.transition&&item.transition.type),field('Transition length','clip-transition-duration:'+token('clip',item),item.transition&&item.transition.duration,'number'),field('Motion','clip-motion:'+token('clip',item),item.motion&&item.motion.type),field('Motion easing','clip-easing:'+token('clip',item),item.motion&&item.motion.easing),field('Opacity','clip-opacity:'+token('clip',item),item.transform&&item.transform.opacity!==undefined?item.transform.opacity:item.opacity,'number'),{tag:'label',children:[{tag:'span',text:'Reverse playback'},{tag:'input',type:'checkbox',id:'control-'+('clip-reverse:'+token('clip',item)).replace(':','-'),action:'clip-reverse:'+token('clip',item),checked:!!item.reverse,disabled:blocked(),label:'Reverse playback'}]}]}]});\\nconst audioCard=(item,index)=>({tag:'article',id:'card-'+token('audio',item),className:'card',children:[{tag:'div',className:'card-head',children:[{tag:'h3',text:text(item.label)||text(item.name)||text(item.id)},{tag:'div',className:'row',children:[{tag:'button',action:'preview-audio:'+token('audio',item),text:'Preview',className:'secondary',disabled:!(item.asset||item.assetId)},remove('remove-audio:'+token('audio',item))]}]},{tag:'div',className:'grid',children:[field('Track','audio-track:'+token('audio',item),item.trackId),field('Start (seconds)','audio-start:'+token('audio',item),item.start,'number'),field('Length (seconds)','audio-duration:'+token('audio',item),item.duration,'number'),field('Trim start','audio-trim:'+token('audio',item),item.trimStart===undefined?item.sourceOffset:item.trimStart,'number'),field('Volume','audio-volume:'+token('audio',item),item.volume,'number'),field('Fade in','audio-fade-in:'+token('audio',item),item.fadeIn,'number'),field('Fade out','audio-fade-out:'+token('audio',item),item.fadeOut,'number'),field('Ducking','audio-ducking:'+token('audio',item),item.ducking,'number'),field('Playback speed','audio-rate:'+token('audio',item),item.rate,'number'),{tag:'label',children:[{tag:'span',text:'Loop source'},{tag:'input',type:'checkbox',id:'control-'+('audio-loop:'+token('audio',item)).replace(':','-'),action:'audio-loop:'+token('audio',item),checked:!!item.loop,disabled:blocked(),label:'Loop source'}]},{tag:'label',children:[{tag:'span',text:'Muted'},{tag:'input',type:'checkbox',id:'control-'+('audio-muted:'+token('audio',item)).replace(':','-'),action:'audio-muted:'+token('audio',item),checked:!!item.muted,disabled:blocked(),label:'Muted'}]}]}]});\\nconst textCard=(item,index)=>({tag:'article',id:'card-'+token('text',item),className:'card',children:[{tag:'div',className:'card-head',children:[{tag:'h3',text:text(item.id)},remove('remove-text:'+token('text',item))]},{tag:'div',className:'grid',children:[field('Text','text-value:'+token('text',item),item.text,'text',true),field('Track','text-track:'+token('text',item),item.trackId),field('Start (seconds)','text-start:'+token('text',item),item.start,'number'),field('Length (seconds)','text-duration:'+token('text',item),item.duration,'number'),field('Animation','text-animation:'+token('text',item),item.animation)]}]});\\nconst captionCard=(item,index)=>({tag:'article',id:'card-'+token('caption',item),className:'card',children:[{tag:'div',className:'card-head',children:[{tag:'h3',text:text(item.id)},remove('remove-caption:'+token('caption',item))]},{tag:'div',className:'grid',children:[field('Caption','caption-value:'+token('caption',item),item.text,'text',true),field('Track','caption-track:'+token('caption',item),item.trackId),field('Start (seconds)','caption-start:'+token('caption',item),item.start,'number'),field('End (seconds)','caption-end:'+token('caption',item),item.end,'number'),field('Animation','caption-animation:'+token('caption',item),item.animation)]}]});\\nconst effectCard=(item,index)=>({tag:'article',id:'card-'+token('effect',item),className:'card',children:[{tag:'div',className:'card-head',children:[{tag:'h3',text:text(item.type)||text(item.id)},remove('remove-effect:'+token('effect',item))]},{tag:'div',className:'grid',children:[field('Type','effect-type:'+token('effect',item),item.type),field('Track','effect-track:'+token('effect',item),item.trackId),field('Start (seconds)','effect-start:'+token('effect',item),item.start,'number'),field('Length (seconds)','effect-duration:'+token('effect',item),item.duration,'number'),field('Intensity','effect-intensity:'+token('effect',item),item.intensity,'number')]}]});\\nconst largeTimeline=()=>draft.clips.length+draft.audioItems.length+draft.textOverlays.length+draft.captions.length+draft.effects.length>2000;\\nconst itemFinish=item=>(Number(item.start)||0)+Math.max(.001,Number(item.duration)||Number(item.end)-(Number(item.start)||0)||.001);\\nconst composition=()=>{const at=Number(playhead.value)||0;if(largeTimeline()){StillMade.render('composition',[{tag:'strong',text:'Lightweight preview · large timeline'},{tag:'p',text:'Partial preview: the live composition summary is paused to keep editing responsive. Every timeline item is retained for editing and the complete final render.'},{tag:'p',text:'The personal playhead uses the saved timeline duration, or a 24-hour preview window when none is supplied.'}]);timecode.textContent='Playhead '+at.toFixed(2)+' seconds';return;}const activeClips=draft.clips.filter(item=>at>=(Number(item.start)||0)&&at<itemFinish(item)),activeAudio=draft.audioItems.filter(item=>at>=(Number(item.start)||0)&&at<itemFinish(item)),activeOverlays=[...draft.textOverlays,...draft.captions,...draft.effects].filter(item=>at>=(Number(item.start)||0)&&at<itemFinish(item));StillMade.render('composition',[{tag:'strong',text:'At '+at.toFixed(2)+'s'},{tag:'p',text:(activeClips.length?activeClips.slice(0,12).map(item=>text(item.label)||text(item.id)).join(', '):'No video')+' · '+String(activeAudio.length)+' audio · '+String(activeOverlays.length)+' overlays'}]);timecode.textContent='Playhead '+at.toFixed(2)+' seconds';};\\nconst rawIsOpen=()=>document.getElementById('rawDetails').getAttribute('open')!==null;\\nconst renderRawPreview=()=>{if(!rawIsOpen())return;if(scratch.text!==null){if(raw.value!==scratch.text)raw.value=scratch.text;return;}rawBaseline=draft;const value=JSON.stringify(draft,null,2);if(raw.value!==value)raw.value=value;};\\nconst schedulePreview=()=>{if(rawIsOpen())StillMade.scheduleSharedEdit('editorRawPreview',renderRawPreview);};\\nconst drawRecovery=()=>{const states=[access,scratchAccess,optionsAccess],failure=states.find(value=>value.error),sourceMismatch=states.some(value=>value.sourceConflict),waiting=states.some(value=>value.pending),editable=states.every(value=>value.canEdit);document.getElementById('recovery').hidden=!failure;document.getElementById('recoveryMessage').textContent=failure?failure.error:'';document.getElementById('retryShared').disabled=!editable||waiting||sourceMismatch||busy;document.getElementById('discardShared').disabled=!editable||waiting||sourceMismatch||busy;document.getElementById('resetSharedSource').hidden=!sourceMismatch;document.getElementById('resetSharedSource').disabled=!editable||waiting||busy;document.getElementById('confirmSourceReset').hidden=!sourceMismatch||!sourceResetArmed;document.getElementById('confirmSourceReset').disabled=!editable||waiting||busy;document.getElementById('rawNotice').textContent=scratch.text===null?'JSON typing is shared live. Apply checks the original displayed values before updating the timeline.':rawStale?'The timeline changed. Shared JSON text is retained; Apply will check for conflicting edits.':'This unfinished JSON draft is shared live. Apply it when ready, or discard the shared scratch.';save.disabled=blocked()||scratch.text!==null;document.getElementById('renderFinal').disabled=blocked()||scratch.text!==null;document.getElementById('applyRaw').disabled=blocked()||scratch.text===null;document.getElementById('discardRaw').disabled=blocked()||scratch.text===null;document.getElementById('undo').disabled=blocked()||(!undoStack.length&&!historyBefore);document.getElementById('redo').disabled=blocked()||!redoStack.length;};\\nconst versionCard=(item,index)=>({tag:'article',id:'card-'+token('version',item),className:'card',children:[{tag:'strong',text:text(item.label)||text(item.id)},{tag:'p',text:text(item.createdAt)},{tag:'button',action:'restore-version:'+token('version',item),text:'Restore this version',className:'secondary',disabled:blocked()}]});\\nconst renderPage=(target,rows,card,size,emptyMessage)=>{const last=Math.max(0,Math.ceil(rows.length/size)-1),page=Math.min(last,pages.get(target)||0);pages.set(target,page);const content=rows.length?rows.slice(page*size,(page+1)*size).map((item,index)=>card(item,page*size+index)):[{tag:'p',text:emptyMessage}];if(rows.length>size)content.push({tag:'div',className:'row',children:[{tag:'button',action:'page-prev:'+target,text:'Previous',className:'secondary',disabled:page===0},{tag:'span',text:'Page '+String(page+1)+' of '+String(last+1)+' · '+String(rows.length)+' items'},{tag:'button',action:'page-next:'+target,text:'Next',className:'secondary',disabled:page===last}]});StillMade.render(target,content);};\\nconst drawOptions=()=>{document.getElementById('renderFps').value=String(options.renderFps);document.getElementById('renderResolution').value=String(options.renderResolution);document.getElementById('renderQuality').value=options.renderQuality;};\\nconst draw=()=>{renderPage('assets',assets,assetCard,12,'No saved project media is available.');renderPage('clips',draft.clips,clipCard,4,'No video clips.');renderPage('audio',draft.audioItems,audioCard,4,'No audio items.');renderPage('text',draft.textOverlays,textCard,8,'No text overlays.');renderPage('captions',draft.captions,captionCard,8,'No captions.');renderPage('effects',draft.effects,effectCard,8,'No effects.');renderPage('versions',Array.isArray(draft.timelineVersions)?draft.timelineVersions:[],versionCard,5,'No saved versions yet.');timelineId.value=text(draft.activeTimelineId);const duration=largeTimeline()?Math.max(1,Number(draft.targetDuration)||86400,Number(playhead.value)||0):Math.max(1,Number(draft.targetDuration)||0,...draft.clips.map(itemFinish),...draft.audioItems.map(itemFinish));playhead.max=String(duration);StillMade.scheduleSharedEdit('editorComposition',composition);aspect.value=text(draft.aspect);const mg=(Array.isArray(draft.fullscreenFrames)?draft.fullscreenFrames.length:0)+(Array.isArray(draft.figureSweeps)?draft.figureSweeps.length:0)+(Array.isArray(draft.graphs)?draft.graphs.length:0);graphics.textContent=String(mg)+' motion graphic item'+(mg===1?'':'s')+' retained in this Editor Block. Use Complete Editor data for advanced motion-graphics fields.';summary.textContent=String(draft.clips.length)+' video · '+String(draft.audioItems.length)+' audio · '+String(draft.captions.length)+' captions · '+String(draft.textOverlays.length)+' text';['reset','addText','addCaption','addEffect','snapshot','renderFps','renderResolution','renderQuality'].forEach(id=>{document.getElementById(id).disabled=blocked();});build.disabled=blocked()||!assets.length;aspect.disabled=blocked();raw.disabled=blocked();drawOptions();drawRecovery();schedulePreview();};\\nconst captureHistory=()=>{if(!historyBefore)return;const before=historyBefore,after=historyAfter;historyBefore=null;historyAfter=null;try{const forward=StillMade.diffDocument(before,after);if(forward.length){undoStack=[...undoStack,{forward,inverse:StillMade.diffDocument(after,before)}].slice(-50);redoStack=[];}}catch(error){undoStack=[];redoStack=[];status.textContent='This change is too large for local undo. The shared timeline is retained.';}drawRecovery();};\\nconst commitEdit=(next,record=true)=>{if(record){if(!historyBefore)historyBefore=draft;historyAfter=next;StillMade.scheduleSharedEdit('editorHistory',captureHistory);}draft=next;revision+=1;sourceResetArmed=false;if(scratch.text!==null)rawStale=true;shared.changed();schedulePreview();StillMade.scheduleSharedEdit('editorComposition',composition);drawRecovery();status.textContent='Sharing timeline changes. Review changes applies the document to your project.';};\\nconst replaceRows=(kind,rows)=>{if(kind==='clip')commitEdit({...draft,clips:rows});if(kind==='audio')commitEdit({...draft,audioItems:rows});if(kind==='text')commitEdit({...draft,textOverlays:rows});if(kind==='caption')commitEdit({...draft,captions:rows});if(kind==='effect')commitEdit({...draft,effects:rows});};\\nconst rowsFor=kind=>kind==='clip'?draft.clips:kind==='audio'?draft.audioItems:kind==='text'?draft.textOverlays:kind==='caption'?draft.captions:kind==='effect'?draft.effects:kind==='version'?(draft.timelineVersions||[]):[];\\nconst previewItem=(item,kind,trim=0)=>{const serial=++previewSerial;if(previewUrl){StillMade.releaseMedia(previewUrl);previewUrl='';}imagePreview.hidden=true;videoPreview.hidden=true;audioPreview.hidden=true;const value=item.asset||item,mediaKind=value.kind||kind,request=mediaKind==='image'?StillMade.previewImage(value):StillMade.previewMedia({...value,kind:mediaKind});request.then(result=>{if(serial!==previewSerial){if(mediaKind!=='image')StillMade.releaseMedia(result.url);return;}if(mediaKind==='image'){imagePreview.src=result.url;imagePreview.hidden=false;}else if(mediaKind==='audio'){previewUrl=result.url;audioPreview.src=result.url;audioPreview.hidden=false;}else{previewUrl=result.url;videoPreview.src=result.url;videoPreview.currentTime=Math.max(0,Number(trim)||0);videoPreview.hidden=false;}status.textContent='Previewing '+text(item.label||item.name||item.id||item.assetId)+'.';}).catch(error=>{if(serial===previewSerial)status.textContent=error.message;});};\\naspect.addEventListener('input',()=>{if(blocked())return;commitEdit({...draft,aspect:aspect.value});});\\nplayhead.addEventListener('input',()=>{timecode.textContent='Playhead '+(Number(playhead.value)||0).toFixed(2)+' seconds';StillMade.scheduleSharedEdit('editorComposition',composition);});\\ndocument.getElementById('rawDetails').addEventListener('toggle',renderRawPreview);\\nraw.addEventListener('input',()=>{if(blocked())return;scratch={text:raw.value,base:scratch.text===null?(rawBaseline||draft):scratch.base};scratchShared.changed();drawRecovery();});\\ndocument.getElementById('discardRaw').addEventListener('click',()=>{if(blocked())return;scratch={text:null,base:null};rawStale=false;scratchShared.changed();renderRawPreview();drawRecovery();});\\nStillMade.onAction(event=>{const parts=event.action.split(':'),kind=parts[0],handle=parts[1];if(kind==='page-prev'||kind==='page-next'){if(!['assets','clips','audio','text','captions','effects','versions'].includes(handle))return;pages.set(handle,Math.max(0,(pages.get(handle)||0)+(kind==='page-prev'?-1:1)));draw();return;}const identity=identities.get(handle);if(!identity)return;const rows=rowsFor(identity.kind),item=(identity.kind==='asset'?assets:rows).includes(identity.item)?identity.item:null,index=rows.indexOf(item);if(!item)return;\\n if(kind==='preview-clip'&&identity.kind==='clip'){previewItem(item,'video',item.trimStart);return;}if(kind==='preview-audio'&&identity.kind==='audio'){previewItem(item,'audio');return;}if(kind==='preview-asset'&&identity.kind==='asset'){previewItem(item,item.kind);return;}if(blocked())return;\\n if(kind==='restore-version'&&identity.kind==='version'){commitEdit({...draft,clips:clone(item.clips||[]),audioItems:clone(item.audioItems||[]),captions:clone(item.captions||[]),textOverlays:clone(item.textOverlays||[]),effects:clone(item.effects||[])});draw();return;}\\n if(kind==='add-asset'&&identity.kind==='asset'){const media=clone(item),duration=Math.max(.001,Number(media.duration)||Number(media.durationSec)||4);if(media.kind==='audio')commitEdit({...draft,audioItems:[...draft.audioItems,{id:uniqueId('audio'),trackId:'audio',start:0,duration,trimStart:0,volume:1,asset:media,url:media.url}]});else{const start=draft.clips.reduce((end,value)=>Math.max(end,itemFinish(value)),0);commitEdit({...draft,clips:[...draft.clips,{id:uniqueId('clip'),trackId:'video',start,duration,trimStart:0,speed:1,volume:1,asset:media,url:media.url}]});}draw();return;}\\n if((kind==='move-clip-up'||kind==='move-clip-down')&&identity.kind==='clip'){const targetIndex=index+(kind==='move-clip-up'?-1:1),target=rows.at(targetIndex);if(targetIndex<0||!target)return;const reordered=rows.slice();reordered.splice(targetIndex,1,item);reordered.splice(index,1,target);replaceRows('clip',reordered);draw();return;}\\n if(kind==='duplicate-clip'&&identity.kind==='clip'){const next={...clone(item),id:uniqueId('clip-copy'),start:itemFinish(item)};replaceRows('clip',[...rows.slice(0,index+1),next,...rows.slice(index+1)]);draw();return;}\\n if(kind==='split-clip'&&identity.kind==='clip'){const half=Math.max(.001,Number(item.duration)||.001)/2;if(half<.001)return;const second={...clone(item),id:uniqueId('clip-split'),start:(Number(item.start)||0)+half,duration:half,trimStart:(Number(item.trimStart)||0)+half};replaceRows('clip',[...rows.slice(0,index),{...item,duration:half},second,...rows.slice(index+1)]);draw();return;}\\n if(kind==='remove-'+identity.kind&&['clip','audio','text','caption','effect'].includes(identity.kind)){const remaining=rows.slice();remaining.splice(index,1);replaceRows(identity.kind,remaining);draw();return;}\\n const next={...item};let recognized=true;\\n if(identity.kind==='clip'){if(kind==='clip-track')next.trackId=event.value;else if(kind==='clip-start')next.start=Math.max(0,Number(event.value)||0);else if(kind==='clip-duration')next.duration=Math.max(.001,Number(event.value)||.001);else if(kind==='clip-trim')next.trimStart=Math.max(0,Number(event.value)||0);else if(kind==='clip-speed')next.speed=Math.max(.01,Number(event.value)||1);else if(kind==='clip-volume')next.volume=Math.max(0,Number(event.value)||0);else if(kind==='clip-transition'||kind==='clip-transition-duration'){const prior=item.transition&&!Array.isArray(item.transition)?item.transition:{type:'Cut',duration:0};next.transition=kind==='clip-transition'?{...prior,type:event.value}:{...prior,duration:Math.max(0,Number(event.value)||0)};}else if(kind==='clip-opacity')next.transform={...(item.transform&&!Array.isArray(item.transform)?item.transform:{}),opacity:Math.max(0,Math.min(1,Number(event.value)||0))};else if(kind==='clip-reverse')next.reverse=event.checked;else if(kind==='clip-motion'||kind==='clip-easing'){const prior=item.motion&&!Array.isArray(item.motion)?item.motion:{type:'none',easing:'ease-in-out'};next.motion=kind==='clip-motion'?{...prior,type:event.value}:{...prior,easing:event.value};}else recognized=false;}\\n else if(identity.kind==='audio'){if(kind==='audio-track')next.trackId=event.value;else if(kind==='audio-start')next.start=Math.max(0,Number(event.value)||0);else if(kind==='audio-duration')next.duration=Math.max(.001,Number(event.value)||.001);else if(kind==='audio-trim'){next.trimStart=Math.max(0,Number(event.value)||0);next.sourceOffset=next.trimStart;}else if(kind==='audio-volume')next.volume=Math.max(0,Number(event.value)||0);else if(kind==='audio-fade-in')next.fadeIn=Math.max(0,Number(event.value)||0);else if(kind==='audio-fade-out')next.fadeOut=Math.max(0,Number(event.value)||0);else if(kind==='audio-ducking')next.ducking=Math.max(0,Math.min(1,Number(event.value)||0));else if(kind==='audio-rate')next.rate=Math.max(.05,Math.min(4,Number(event.value)||1));else if(kind==='audio-loop')next.loop=event.checked;else if(kind==='audio-muted')next.muted=event.checked;else recognized=false;}\\n else if(identity.kind==='text'){if(kind==='text-value')next.text=event.value;else if(kind==='text-track')next.trackId=event.value;else if(kind==='text-start')next.start=Math.max(0,Number(event.value)||0);else if(kind==='text-duration')next.duration=Math.max(.001,Number(event.value)||.001);else if(kind==='text-animation')next.animation=event.value;else recognized=false;}\\n else if(identity.kind==='caption'){if(kind==='caption-value')next.text=event.value;else if(kind==='caption-track')next.trackId=event.value;else if(kind==='caption-start')next.start=Math.max(0,Number(event.value)||0);else if(kind==='caption-end')next.end=Math.max((Number(item.start)||0)+.001,Number(event.value)||(Number(item.start)||0)+.001);else if(kind==='caption-animation')next.animation=event.value;else recognized=false;}\\n else if(identity.kind==='effect'){if(kind==='effect-type')next.type=event.value;else if(kind==='effect-track')next.trackId=event.value;else if(kind==='effect-start')next.start=Math.max(0,Number(event.value)||0);else if(kind==='effect-duration')next.duration=Math.max(.001,Number(event.value)||.001);else if(kind==='effect-intensity')next.intensity=Number(event.value)||0;else recognized=false;}\\n else recognized=false;if(recognized){const updated=rows.slice();updated.splice(index,1,next);replaceRows(identity.kind,updated);identities.set(handle,{kind:identity.kind,item:next});}\\n});\\nbuild.addEventListener('click',()=>{if(blocked())return;const assembled=assemble();const next=source.clips?.length||source.audioItems?.length?assembled:{...assembled,clips:assembled.clips.map(item=>({...item,id:uniqueId('clip')})),audioItems:assembled.audioItems.map(item=>({...item,id:uniqueId('audio')}))};commitEdit(next);draw();});\\ndocument.getElementById('addText').addEventListener('click',()=>{if(blocked())return;commitEdit({...draft,textOverlays:[...draft.textOverlays,{id:uniqueId('text'),trackId:'text',text:'New text',start:0,duration:2,animation:'none'}]});draw();});\\ndocument.getElementById('addCaption').addEventListener('click',()=>{if(blocked())return;commitEdit({...draft,captions:[...draft.captions,{id:uniqueId('caption'),trackId:'captions',text:'New caption',start:0,end:2,font:'Hanken Grotesk',animation:'none'}]});draw();});\\ndocument.getElementById('addEffect').addEventListener('click',()=>{if(blocked())return;commitEdit({...draft,effects:[...draft.effects,{id:uniqueId('effect'),trackId:'video',type:'fade',start:0,duration:1,intensity:1}]});draw();});\\nconst undoEdit=redo=>{if(blocked())return;captureHistory();const entry=redo?redoStack.at(-1):undoStack.at(-1);if(!entry)return;try{const next=StillMade.applyDocument(draft,redo?entry.forward:entry.inverse);if(redo){redoStack=redoStack.slice(0,-1);undoStack=[...undoStack,entry];}else{undoStack=undoStack.slice(0,-1);redoStack=[...redoStack,entry];}commitEdit(next,false);draw();status.textContent=redo?'Your checked change was restored.':'Your checked change was undone. Teammate edits are retained.';}catch(error){status.textContent='Cannot '+(redo?'redo':'undo')+' because the same content changed. The shared timeline is retained.';}};\\ndocument.getElementById('undo').addEventListener('click',()=>undoEdit(false));\\ndocument.getElementById('redo').addEventListener('click',()=>undoEdit(true));\\ndocument.getElementById('snapshot').addEventListener('click',()=>{if(blocked())return;const version={id:uniqueId('timeline-version'),label:'Timeline version '+String((draft.timelineVersions||[]).length+1),createdAt:new Date().toISOString(),clips:clone(draft.clips),audioItems:clone(draft.audioItems),captions:clone(draft.captions),textOverlays:clone(draft.textOverlays),effects:clone(draft.effects)};commitEdit({...draft,timelineVersions:[...(draft.timelineVersions||[]),version].slice(-50)});draw();});\\ndocument.getElementById('reset').addEventListener('click',()=>{if(blocked())return;commitEdit(normalize(clone(source)));draw();status.textContent='Reset to the current project timeline is being shared.';});\\nconst validateEditor=value=>{if(!value||Array.isArray(value)||value.schemaVersion!==1||typeof value.activeTimelineId!=='string'||!value.activeTimelineId)throw Error('Editor JSON must be a version 1 document with an active timeline ID.');[value.tracks,value.clips,value.audioItems,value.captions,value.textOverlays,value.stickers,value.effects,value.markers,value.fxEvents,value.liveOverlays,value.mediaItems,value.timelineVersions].forEach(rows=>{if(rows===undefined)return;if(!Array.isArray(rows)||rows.length>10000||rows.some(item=>!item||Array.isArray(item)||typeof item.id!=='string'||!item.id||item.id.length>200)||new Set(rows.map(item=>item.id)).size!==rows.length)throw Error('Each Editor collection needs at most 10,000 items with distinct, nonempty IDs of at most 200 characters.');});return normalize(value);};\\ndocument.getElementById('applyRaw').addEventListener('click',async()=>{if(blocked()||scratch.text===null)return;const requested=JSON.stringify(scratch);busy=true;draw();try{await scratchShared.flush();if(JSON.stringify(scratch)!==requested)throw Error('The shared JSON draft changed. Review the latest text before applying it.');if(!allowed())throw Error('Editing access changed. JSON text is retained.');const pending=scratch,value=validateEditor(JSON.parse(pending.text)),edits=StillMade.diffDocument(pending.base||draft,value);commitEdit(StillMade.applyDocument(draft,edits));await shared.flush();if(!allowed())throw Error('Editing access changed. JSON text is retained.');if(JSON.stringify(scratch)===requested){scratch={text:null,base:null};rawStale=false;scratchShared.changed();await scratchShared.flush();}status.textContent='Complete Editor data applied to the shared timeline.';}catch(error){status.textContent=error.message;}finally{busy=false;draw();}});\\nconst editOptions=next=>{if(blocked())return;options=next;optionsShared.changed();revision+=1;};\\ndocument.getElementById('renderFps').addEventListener('change',()=>editOptions({...options,renderFps:Number(document.getElementById('renderFps').value)}));\\ndocument.getElementById('renderResolution').addEventListener('change',()=>editOptions({...options,renderResolution:Number(document.getElementById('renderResolution').value)}));\\ndocument.getElementById('renderQuality').addEventListener('change',()=>editOptions({...options,renderQuality:document.getElementById('renderQuality').value}));\\nconst runAction=async(render)=>{if(blocked()||scratch.text!==null)return;busy=true;draw();status.textContent=render?'Preparing the exact final render…':'Checking the complete Editor timeline…';try{await shared.flush();await scratchShared.flush();await optionsShared.flush();if(!allowed()||scratch.text!==null)throw Error('Editing access or the shared draft changed. The action was not run.');const started=revision;await StillMade.run({editor:clone(source),updatedEditor:clone(draft),commit:!render,...render?{action:'render',...options}:{}});status.textContent=started!==revision?'A result is ready below; newer shared edits were retained.':render?'Final render is ready below. Choose Render final file to start the device renderer.':'Editor changes are ready below. Review the before and after, then choose Apply.';}catch(error){status.textContent=error.message||'The Editor action could not be prepared.';}finally{busy=false;draw();}};\\nsave.addEventListener('click',()=>runAction(false));\\ndocument.getElementById('renderFinal').addEventListener('click',()=>runAction(true));\\nconst recover=async(action)=>{if(busy||!shared||![access,scratchAccess,optionsAccess].every(value=>value.canEdit&&!value.pending))return;busy=true;draw();try{await action();sourceResetArmed=false;undoStack=[];redoStack=[];historyBefore=null;historyAfter=null;status.textContent='Shared Editor draft recovered.';}catch(error){status.textContent=error.message;}finally{busy=false;draw();}};\\ndocument.getElementById('retryShared').addEventListener('click',()=>recover(async()=>{await shared.retry();await scratchShared.retry();await optionsShared.retry();}));\\ndocument.getElementById('discardShared').addEventListener('click',()=>recover(async()=>{await shared.discard();await scratchShared.discard();await optionsShared.discard();}));\\ndocument.getElementById('resetSharedSource').addEventListener('click',()=>{sourceResetArmed=true;drawRecovery();});\\ndocument.getElementById('confirmSourceReset').addEventListener('click',()=>{if(!sourceResetArmed||!access.sourceConflict)return;sourceResetArmed=false;recover(()=>shared.resetSharedSource({discardSharedEdits:true}));});\\nStillMade.onShared(value=>{sourceResetArmed=false;drawRecovery();const outputs=value.outputs||{};document.getElementById('sharedResult').textContent=outputs.mediaAction?'A final-render plan is available in the shared result below.':outputs.editor?'The latest reviewed Editor result is available below.':'';});\\nStillMade.onInput(values=>{sourceResetArmed=false;source=clone(values.editor||empty);assets=Array.isArray(values.assets)?clone(values.assets):[];previewSerial+=1;if(previewUrl){StillMade.releaseMedia(previewUrl);previewUrl='';}imagePreview.hidden=true;videoPreview.hidden=true;audioPreview.hidden=true;const next=normalize(values.updatedEditor&&(values.updatedEditor.clips?.length||values.updatedEditor.audioItems?.length||values.updatedEditor.timelineVersions?.length)?clone(values.updatedEditor):source.clips?.length||source.audioItems?.length?clone(source):assemble());if(!shared){draft=next;shared=StillMade.connectDocument({key:'editorDocument',source:next,read:()=>draft,replace:value=>{sourceResetArmed=false;captureHistory();draft=value;revision+=1;if(scratch.text!==null)rawStale=true;draw();}});scratchShared=StillMade.connectDocument({key:'editorScratch',source:{text:null,base:null},read:()=>scratch,replace:value=>{scratch=value;rawStale=scratch.text!==null;if(scratch.text!==null&&rawIsOpen()&&raw.value!==scratch.text)raw.value=scratch.text;else schedulePreview();drawRecovery();}});optionsShared=StillMade.connectDocument({key:'editorOptions',source:{renderFps:30,renderResolution:1080,renderQuality:'high'},read:()=>options,replace:value=>{options=value;revision+=1;drawOptions();}});shared.onStatus(value=>{const redraw=access.canEdit!==value.canEdit||access.error!==value.error;access=value;if(redraw)draw();else drawRecovery();});scratchShared.onStatus(value=>{const redraw=scratchAccess.canEdit!==value.canEdit||scratchAccess.error!==value.error;scratchAccess=value;if(redraw)draw();else drawRecovery();});optionsShared.onStatus(value=>{const redraw=optionsAccess.canEdit!==value.canEdit||optionsAccess.error!==value.error;optionsAccess=value;if(redraw)draw();else drawRecovery();});playhead.value='0';}else shared.replaceSource(next);revision+=1;draw();status.textContent=access.error||'Timeline changes, JSON drafts and export settings are shared live. Playback and previews are personal.';});\\n\"\n}"},"lineage":{"parents":[],"changes":"Packaged original Block source for StillMade."}}}],"stages":[{"id":"photos","blockId":"sm.import-media","version":"2.0.2","label":"Import 6–8 Property Photos","config":{"include":"Images","duplicates":"Remove duplicates"}},{"id":"compose","blockId":"stillmade.photo-reel-composer","version":"1.0.0","label":"Compose Reel","config":{"purpose":"property","duration":24,"aspect":"9:16"}},{"id":"finish","blockId":"sm.editor","version":"2.2.9","label":"Editor","config":{"commit":true,"renderFps":30,"renderResolution":1080,"renderQuality":"high"}}],"connections":[{"from":{"stage":"photos","port":"assets"},"to":{"stage":"compose","port":"assets"}},{"from":{"stage":"compose","port":"timeline"},"to":{"stage":"finish","port":"updatedEditor"}}],"outcome":{"type":"export","dedupe":"material_project_state","minimumDurationSec":20}}