stillmade-sdk 0.1.34 → 0.1.36

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/AGENTS.md +8 -4
  2. package/DESKTOP_CAPABILITIES.md +81 -6
  3. package/FRAME_VIEWS.md +5 -0
  4. package/MODULES.md +104 -11
  5. package/PASTE_TO_LLM.md +14 -6
  6. package/SDK.md +245 -50
  7. package/blocks/connected-app/versions/1.6.0/view.js +27 -0
  8. package/blocks/gmail/versions/1.1.0/workspace.js +15 -0
  9. package/blocks/native/sm.when/versions/1.1.0/package.js +216 -0
  10. package/package.json +1 -1
  11. package/packages/block-cli/cli.js +27 -32
  12. package/packages/block-cli/dev-host/assets/host.js +38 -37
  13. package/packages/block-cli/dev-host/assets/index.css +1 -1
  14. package/packages/block-cli/dev-host/index.html +1 -0
  15. package/packages/block-cli/format.js +8 -0
  16. package/packages/block-platform/catalog.js +17 -1
  17. package/packages/block-platform/segment-runner.js +10 -4
  18. package/packages/block-platform/steps.js +6 -0
  19. package/packages/block-sdk/admission.js +11 -4
  20. package/packages/block-sdk/builder-contract.js +10 -5
  21. package/packages/block-sdk/capabilities.js +3 -0
  22. package/packages/block-sdk/connected-app.js +1 -1
  23. package/packages/block-sdk/contracts.js +1 -0
  24. package/packages/block-sdk/desktop-permissions.js +1 -1
  25. package/packages/block-sdk/first-party-identities.js +1 -0
  26. package/packages/block-sdk/first-party-packages.js +2 -1
  27. package/packages/block-sdk/folder-package.js +75 -0
  28. package/packages/block-sdk/index.d.ts +32 -6
  29. package/packages/block-sdk/index.js +4 -0
  30. package/packages/block-sdk/local-admission.js +4 -2
  31. package/packages/block-sdk/media-transform.js +42 -2
  32. package/packages/block-sdk/mock-host.js +3 -16
  33. package/packages/block-sdk/mocks.js +20 -0
  34. package/packages/block-sdk/module-host-calls.js +23 -0
  35. package/packages/block-sdk/module-hosted.js +22 -1
  36. package/packages/block-sdk/module-node.js +15 -20
  37. package/packages/block-sdk/module-runtime.js +7 -4
  38. package/packages/block-sdk/module-sockets.js +66 -0
  39. package/packages/block-sdk/module-worker.js +38 -1
  40. package/packages/block-sdk/network.js +20 -0
  41. package/packages/block-sdk/permission-summary.js +97 -0
  42. package/packages/block-sdk/permission-usage.js +72 -0
  43. package/packages/block-sdk/port-types.js +1 -1
  44. package/packages/block-sdk/schema-fields.js +20 -5
  45. package/packages/block-sdk/testing.js +20 -7
  46. package/packages/block-sdk/view-bridge.js +2 -1
  47. package/blocks/connected-app/versions/1.5.0/view.js +0 -18
  48. package/blocks/gmail/versions/1.0.0/workspace.js +0 -6
@@ -1 +1 @@
1
- @font-face{font-family:Hanken Grotesk;font-style:normal;font-weight:100 900;font-display:swap;src:url(/fonts/HankenGrotesk-Variable.ttf)format("truetype")}@font-face{font-family:Hanken Grotesk;font-style:italic;font-weight:100 900;font-display:swap;src:url(/fonts/HankenGrotesk-Italic-Variable.ttf)format("truetype")}:root{--cream-50:#fdfaf1;--cream-100:#f7f0db;--cream-200:#ece3c9;--cream-300:#dcd2b5;--cream-600:#4e483a;--ink-50:#f2f0ec;--ink-100:#c9c6be;--ink-300:#4a4843;--ink-400:#26241f;--ink-500:#16140f;--ink-600:#0a0a0a;--signal:#e8b23a;--signal-dim:#e8b23a26;--signal-glow:#e8b23a4d;--font-display:"Balgin", "Hanken Grotesk", ui-sans-serif, sans-serif;--font-body:"Hanken Grotesk", ui-sans-serif, sans-serif;--font-mono:ui-monospace, "SF Mono", Menlo, Consolas, monospace;--font:var(--font-body);--fs-display-xl:clamp(72px, 14vw, 220px);--fs-display-l:clamp(56px, 9vw, 144px);--fs-display-m:clamp(40px, 6vw, 96px);--fs-h4:1.25rem;--fs-body:1rem;--fs-body-s:.875rem;--fs-caption:.8125rem;--fs-micro:.75rem;--tr-display:-.02em;--tr-body:-.005em;--tr-micro:.08em;--lh-display:.92;--lh-tight:1.05;--s-1:4px;--s-2:8px;--s-3:12px;--s-4:16px;--s-5:24px;--s-6:32px;--s-7:48px;--s-8:64px;--s-9:96px;--s-10:128px;--s-11:192px;--page-gutter:clamp(20px, 3vw, 48px);--page-gutter-compact:clamp(16px, 5vw, 24px);--page-section-gap:clamp(32px, 5vw, 64px);--content-readable:720px;--content-wide:1440px;--control-height:40px;--touch-target:44px;--mobile-dock-height:66px;--z-nav:30;--hover:var(--hover-wash);--r-0:0;--r-1:2px;--r-2:4px;--r-3:8px;--r-pill:999px;--radius-sm:var(--r-2);--radius:var(--r-3);--radius-lg:var(--r-3);--bw-hair:.5px;--bw-1:1px;--bw-2:2px;--ease-out:cubic-bezier(.22, 1, .36, 1);--ease-in-out:cubic-bezier(.65, 0, .35, 1);--ease-spring:cubic-bezier(.34, 1.56, .64, 1);--dur-instant:80ms;--dur-fast:.16s;--dur-base:.24s;--dur-slow:.48s;--dur-reveal:.72s}:root,[data-mode=dark]{--lightningcss-light: ;--lightningcss-dark:initial;color-scheme:dark;--bg:#101010;--bg-elev-1:#232221;--bg-elev-2:#33312f;--bg-inset:#171716;--fg:#f3eee5;--fg-muted:#bbb2a7;--fg-subtle:#f3eee594;--fg-on-accent:#191716;--border-tok:#58534d;--border-strong:#8b7e6f;--divider:#393735;--signal:#d0ae77;--signal-dim:#d0ae7726;--signal-glow:#d0ae774d;--hover-wash:#f7f0db0f;--press-wash:#f7f0db1f;--selection-bg:var(--cream-100);--selection-fg:var(--ink-600);--shadow-1:0 1px 0 #f7f0db0a inset, 0 4px 12px #00000080;--shadow-2:0 1px 0 #f7f0db0f inset, 0 16px 48px #0009;--scrim-top:linear-gradient(to bottom, #0a0a0ad9, #0a0a0a00);--scrim-bottom:linear-gradient(to top, #0a0a0ad9, #0a0a0a00);--bg-primary:var(--bg);--bg-secondary:var(--bg-elev-1);--bg-tertiary:var(--bg-elev-2);--bg-elevated:var(--bg-elev-1);--bg-hover:#403d3a;--border:var(--border-tok);--border-light:var(--border-strong);--accent:var(--fg);--accent-hover:var(--cream-50);--accent-dim:var(--hover-wash);--accent-glow:#f7f0db38;--accent-contrast:var(--bg);--editor-standout-bg:var(--fg);--editor-standout-fg:var(--bg);--text-primary:var(--fg);--text-secondary:var(--fg-muted);--text-muted:var(--fg-subtle);--success:var(--fg);--warning:var(--signal);--danger:var(--signal);--attention-warning:#e0b24f;--attention-error:#e26a5f;--attention-ok:#6bc38e;--attention-info:#7da7e8;--shadow:var(--shadow-1);--shadow-lg:var(--shadow-2)}[data-mode=light]{--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light;--signal:#e8b23a;--signal-dim:#e8b23a26;--signal-glow:#e8b23a4d;--bg:var(--cream-100);--bg-elev-1:var(--cream-50);--bg-elev-2:var(--cream-200);--bg-inset:var(--cream-200);--fg:var(--ink-600);--fg-muted:var(--cream-600);--fg-subtle:#0a0a0a61;--fg-on-accent:var(--cream-100);--border-tok:#0a0a0a1a;--border-strong:#0a0a0a38;--divider:#0a0a0a0f;--hover-wash:#0a0a0a0d;--press-wash:#0a0a0a1a;--selection-bg:var(--ink-600);--selection-fg:var(--cream-100);--shadow-1:0 1px 0 #ffffff80 inset, 0 4px 12px #0a0a0a0f;--shadow-2:0 1px 0 #fff9 inset, 0 16px 48px #0a0a0a1a;--scrim-top:linear-gradient(to bottom, #f7f0dbe6, #f7f0db00);--scrim-bottom:linear-gradient(to top, #f7f0dbe6, #f7f0db00);--bg-primary:var(--bg);--bg-secondary:var(--bg-elev-1);--bg-tertiary:var(--bg-elev-2);--bg-elevated:var(--bg-elev-2);--bg-hover:var(--cream-300);--border:var(--border-tok);--border-light:var(--border-strong);--accent:var(--fg);--accent-hover:var(--ink-500);--accent-dim:var(--hover-wash);--accent-glow:#0a0a0a38;--accent-contrast:var(--bg);--editor-standout-bg:var(--ink-300);--editor-standout-fg:var(--cream-100);--text-primary:var(--fg);--text-secondary:var(--fg-muted);--text-muted:var(--fg-subtle);--success:var(--fg);--warning:var(--signal);--danger:var(--signal);--attention-warning:#b98200;--attention-error:#c2453d;--attention-ok:#2e7d4f;--attention-info:#2f67b8;--shadow:var(--shadow-1);--shadow-lg:var(--shadow-2)}[data-mode=white]{--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light;--bg:#e9e8e6;--bg-elev-1:#fff;--bg-elev-2:#ebe9e6;--bg-inset:#f7f6f4;--fg:#1a1917;--fg-muted:#615c56;--fg-subtle:#77716b;--fg-on-accent:#fff;--border-tok:#bcb8b2;--border-strong:#978b7d;--divider:#cdc8c2;--hover-wash:#00000014;--press-wash:#00000021;--selection-bg:#1a1917;--selection-fg:#fff;--signal:#8e6b46;--signal-dim:#8e6b461a;--signal-glow:#8e6b4638;--shadow-1:0 1px 3px #0000000a;--shadow-2:0 12px 40px #0000001a;--scrim-top:linear-gradient(to bottom,#e9e8e6e6,#e9e8e600);--scrim-bottom:linear-gradient(to top,#e9e8e6e6,#e9e8e600);--bg-primary:var(--bg);--bg-secondary:var(--bg-elev-1);--bg-tertiary:var(--bg-elev-2);--bg-elevated:var(--bg-elev-1);--bg-hover:#e0ddd8;--border:var(--border-tok);--border-light:var(--border-strong);--accent:var(--fg);--accent-hover:#33302c;--accent-dim:var(--hover-wash);--accent-glow:#0000001f;--accent-contrast:var(--bg);--editor-standout-bg:#24211d;--editor-standout-fg:#fff;--text-primary:var(--fg);--text-secondary:var(--fg-muted);--text-muted:var(--fg-subtle);--success:var(--fg);--warning:var(--signal);--danger:var(--signal);--attention-warning:#b98200;--attention-error:#c2453d;--attention-ok:#2e7d4f;--attention-info:#2f67b8;--shadow:var(--shadow-1);--shadow-lg:var(--shadow-2)}button[data-icon-action=true]{cursor:pointer;flex-shrink:0;line-height:1;min-width:36px!important;min-height:36px!important;box-shadow:none!important;color:var(--text-secondary,var(--fg))!important;background:0 0!important;border:0!important;border-radius:0!important;justify-content:center!important;align-items:center!important;padding:8px!important;display:inline-flex!important}button[data-icon-action=true]:hover:not(:disabled){color:var(--text-primary,var(--fg))!important}button[data-icon-action=true]:focus-visible{outline-offset:2px;outline:2px solid var(--signal)!important}button[data-icon-action=true]:disabled{cursor:not-allowed;opacity:.4!important}button[data-icon-action=true][aria-pressed=true]{text-underline-offset:5px;text-decoration:underline;color:var(--text-primary,var(--fg))!important}@media (pointer:coarse){button[data-icon-action=true]{min-width:44px!important;min-height:44px!important}}*,:before,:after{box-sizing:border-box;margin:0;padding:0}:root[data-anim-theme="1"]:not([data-mode=white]){--lightningcss-light: ;--lightningcss-dark:initial;color-scheme:dark;--bg:#0a0d13;--bg-elev-1:#0f141c;--bg-elev-2:#1a2230;--bg-inset:#0f141c;--fg:#d8dee8;--fg-muted:#8b98ac;--fg-subtle:#64748b;--fg-on-accent:#0a0d13;--border-tok:#232d3d;--border-strong:#303d50;--divider:#1a2230;--hover-wash:#8b98ac14;--press-wash:#8b98ac29;--selection-bg:#303d50;--selection-fg:#d8dee8;--bg-primary:#0a0d13;--bg-secondary:#0f141c;--bg-tertiary:#1a2230;--bg-elevated:#1a2230;--bg-hover:#232d3d;--border:#232d3d;--border-light:#303d50;--accent:#8b98ac;--accent-hover:#b6c0ce;--accent-dim:#8b98ac1f;--accent-glow:#8b98ac40;--accent-contrast:#0a0d13;--editor-standout-bg:#d8dee8;--editor-standout-fg:#0a0d13;--text-primary:#d8dee8;--text-secondary:#8b98ac;--text-muted:#64748b;--success:#8b98ac;--warning:#b6c0ce;--danger:#b6c0ce}html,body{background:var(--bg);width:100%;height:100%;color:var(--fg);font-family:var(--font-body);letter-spacing:var(--tr-body);-webkit-font-smoothing:antialiased;text-rendering:optimizelegibility;font-size:14px;line-height:1.6;overflow:hidden}#root{width:100%;height:100%;overflow:hidden}.sm-app-shell{height:100dvh}@keyframes sm-navmenu-in{0%{opacity:0;transform:translateY(10px)}to{opacity:1;transform:none}}.mobile-nav-menu{animation:.2s cubic-bezier(.22,1,.36,1) sm-navmenu-in}::selection{background:var(--selection-bg);color:var(--selection-fg)}.display,h1,h2{font-family:var(--font-display);font-style:italic;font-weight:200;line-height:var(--lh-tight);letter-spacing:var(--tr-display);margin:0}.display{line-height:var(--lh-display)}h3,h4{font-family:var(--font-body);font-weight:500;line-height:var(--lh-tight);letter-spacing:-.01em;margin:0}.eyebrow{color:var(--fg-muted);font-size:11px;font-weight:500}code,pre,.mono{font-family:var(--font-mono);font-size:.92em}.listing-identity code{font-family:inherit;font-size:inherit}button{font-family:var(--font-body);cursor:pointer;letter-spacing:var(--tr-body);border:none;outline:none}textarea,input,select{font-family:var(--font-body);outline:none}select{background-color:var(--bg-elev-1);color:var(--text-primary);border:1px solid var(--border);border-radius:var(--r-2)}select option,select optgroup{background-color:var(--bg-elevated);color:var(--text-primary)}select option:checked{font-weight:600}.tsel-trigger{background-color:var(--bg-elev-1);color:var(--text-primary);border:1px solid var(--border);border-radius:var(--r-2);font-family:var(--font-body);font-size:inherit;cursor:pointer;text-align:left;justify-content:space-between;align-items:center;gap:8px;padding:6px 10px;display:inline-flex}.tsel-trigger:hover{border-color:var(--border-strong)}.tsel-trigger:focus-visible{outline:2px solid var(--accent);outline-offset:1px}.tsel-trigger:disabled{opacity:.5;cursor:default}.tsel-label{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.tsel-caret{opacity:.7;flex:none;font-size:10px}.tsel-pop{z-index:100100;background:var(--bg-elevated);border:1px solid var(--border);border-radius:var(--r-2);max-height:260px;padding:4px;overflow-y:auto;box-shadow:0 8px 28px #00000047}.tsel-opt{color:var(--text-primary);cursor:pointer;white-space:nowrap;border-radius:6px;align-items:center;gap:6px;padding:6px 8px;font-size:13px;display:flex}.tsel-opt[data-active=true]{background:var(--bg-elev-1)}.tsel-opt[aria-selected=true]{font-weight:600}.tsel-opt[aria-disabled=true]{color:var(--text-muted);cursor:default}.tsel-opt[aria-disabled=true][data-active=true]{background:0 0}.tsel-check{opacity:.8;flex:none;width:12px;font-size:11px}.tsel-opt-label{text-overflow:ellipsis;flex:auto;overflow:hidden}.tsel-note{cursor:help;width:16px;height:16px;color:var(--text-muted);border:1px solid var(--border);border-radius:999px;flex:none;justify-content:center;align-items:center;font-size:11px;display:inline-flex;position:relative}.tsel-note:hover{color:var(--text-primary);border-color:var(--border-strong)}.tsel-tip{z-index:100110;background:var(--text-primary);color:var(--bg-elevated);box-sizing:border-box;white-space:normal;pointer-events:none;border-radius:5px;max-width:min(260px,100vw - 16px);padding:4px 8px;font-size:11px;font-weight:600;box-shadow:0 2px 10px #0000004d}@media (width<=767.98px),(height<=480px) and (pointer:coarse){.tsel-opt{box-sizing:border-box;min-height:44px;font-size:14px}.tsel-note{flex:0 0 44px;width:44px;height:44px;font-size:14px}}.video-editor input,.video-editor select,.video-editor textarea{background:var(--bg-inset);width:100%;min-width:0;color:var(--fg);border:var(--bw-1) solid var(--border-strong);border-radius:var(--r-2);font-family:var(--font-body);accent-color:var(--signal);padding:7px 8px;font-size:12px}.video-editor,.mobile-editor-root{-webkit-user-select:none;user-select:none;-webkit-touch-callout:none}.video-editor input,.video-editor select,.video-editor textarea,.video-editor [contenteditable=true],.mobile-editor-root input,.mobile-editor-root select,.mobile-editor-root textarea,.mobile-editor-root [contenteditable=true]{-webkit-user-select:text;user-select:text;-webkit-touch-callout:default}.video-editor input[type=range]{background:0 0;border:0;padding:0}.video-editor input[type=checkbox]{width:auto;min-width:auto}.video-editor select:focus,.video-editor input:focus,.video-editor textarea:focus{border-color:var(--signal);box-shadow:0 0 0 1px var(--signal-dim)}:focus-visible{outline:2px solid var(--signal);outline-offset:2px}::-webkit-scrollbar{width:6px;height:6px}::-webkit-scrollbar-track{background:0 0}::-webkit-scrollbar-thumb{background:var(--border-strong);border-radius:var(--r-pill)}::-webkit-scrollbar-thumb:hover{background:var(--fg-subtle)}.sm-hide-scrollbar{scrollbar-width:none;-ms-overflow-style:none}.sm-hide-scrollbar::-webkit-scrollbar{width:0;height:0;display:none}.btn{align-items:center;gap:var(--s-2);padding:var(--s-2) var(--s-4);border-radius:var(--r-pill);letter-spacing:var(--tr-body);transition:transform var(--dur-fast) var(--ease-out), background-color var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out), opacity var(--dur-fast) var(--ease-out);font-size:13px;font-weight:500;display:inline-flex}.btn:active{transform:scale(.98)}.btn-primary{background:var(--fg);color:var(--bg)}.btn-primary:hover{background:var(--fg);opacity:.86}.btn-primary:disabled{opacity:.4;cursor:not-allowed}.btn-secondary{color:var(--fg);border:var(--bw-1) solid var(--border-strong);background:0 0}.btn-secondary:hover{background:var(--hover-wash);border-color:var(--fg)}.btn-ghost{color:var(--fg-muted);background:0 0;padding:6px 10px}.btn-ghost:hover{background:var(--hover-wash);color:var(--fg)}.btn-icon{color:var(--fg-muted);border-radius:var(--r-2);transition:background-color var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out), transform var(--dur-fast) var(--ease-out);background:0 0;justify-content:center;align-items:center;padding:5px;display:inline-flex}.btn-icon:hover{background:var(--hover-wash);color:var(--fg)}.btn-icon:active{transform:scale(.98)}.btn-icon:disabled{opacity:.4;cursor:not-allowed}.typing-indicator{align-items:center;gap:4px;padding:4px 0;display:flex}.typing-indicator span{border-radius:var(--r-pill);background:var(--fg-subtle);width:6px;height:6px;animation:1.2s infinite bounce}.typing-indicator span:nth-child(2){animation-delay:.2s}.typing-indicator span:nth-child(3){animation-delay:.4s}@keyframes bounce{0%,60%,to{opacity:.4;transform:translateY(0)}30%{opacity:1;transform:translateY(-5px)}}@keyframes fadeIn{0%{opacity:0;transform:translateY(6px)}to{opacity:1;transform:translateY(0)}}@keyframes spin{to{transform:rotate(360deg)}}@keyframes pulse{0%,to{opacity:.4;transform:scale(.9)}50%{opacity:1;transform:scale(1.1)}}@keyframes toastIn{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}button:focus-visible,[role=button]:focus-visible,a:focus-visible,input:focus-visible,select:focus-visible,textarea:focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-radius:4px}input:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=color]):not([type=file]):not([type=button]):not([type=submit]):not([type=reset]):is(:focus,:focus-visible),textarea:is(:focus,:focus-visible),select:is(:focus,:focus-visible),[contenteditable]:not([contenteditable=false]):is(:focus,:focus-visible){outline:none!important}@keyframes skeletonShimmer{0%{background-position:-200% 0}to{background-position:200% 0}}.skeleton{background:linear-gradient(90deg,#ffffff08 0%,#ffffff1a 50%,#ffffff08 100%) 0 0/200% 100%;border-radius:6px;animation:1.4s ease-in-out infinite skeletonShimmer}.node-state-empty{border-style:dashed!important}.node-state-generating{box-shadow:0 0 0 2px var(--accent);position:relative}.node-state-generating:after{content:"";border-radius:inherit;pointer-events:none;will-change:opacity;animation:1.6s ease-in-out infinite nodePulse;position:absolute;inset:-2px;box-shadow:0 0 18px #f7c8328c}@keyframes nodePulse{0%,to{opacity:.36}50%{opacity:1}}.node-state-warning{position:relative;border-color:var(--attention-warning)!important}.node-state-warning:after{content:"";border-radius:inherit;pointer-events:none;box-shadow:0 0 0 1px var(--attention-warning), 0 0 14px color-mix(in srgb, var(--attention-warning) 40%, transparent);position:absolute;inset:-2px}.node-state-failed{border-top:4px solid #d04848!important}.node-state-failed:before{content:"⚠ ";color:#d04848;z-index:2;pointer-events:none;font-size:12px;font-weight:800;position:absolute;top:6px;left:8px}.react-flow__node{cursor:default}.react-flow__node.dragging,.react-flow__pane.dragging .react-flow__viewport{will-change:transform}.react-flow__node:not(.react-flow__node-sceneGroupNode):not(.react-flow__node-shotLabelNode):not(.react-flow__node-stickyNoteNode):not(.react-flow__node-transitionNode){box-shadow:1px 1px 0 var(--node-side,#d8cfb3), 2px 2px 0 var(--node-side,#d8cfb3), 3px 3px 0 var(--node-side,#d8cfb3), 4px 4px 0 var(--node-side,#d8cfb3), 5px 5px 0 var(--node-side,#d8cfb3), 6px 6px 0 var(--node-side,#d8cfb3), 7px 7px 0 var(--node-edge,#0a0a0a2e), 14px 16px 18px -4px #0a0a0a2e;border-radius:10px}[data-mode=dark] .react-flow__node:not(.react-flow__node-sceneGroupNode):not(.react-flow__node-shotLabelNode):not(.react-flow__node-stickyNoteNode):not(.react-flow__node-transitionNode){--node-side:#1f1d18;--node-edge:#f7f0db2e}.react-flow__controls{grid-template-columns:repeat(2,1fr)!important;display:grid!important}.react-flow__controls button{background:var(--bg-elev-2)!important;border-color:var(--border-tok)!important;color:var(--fg-muted)!important;fill:var(--fg-muted)!important}.react-flow__controls button:hover{background:var(--ink-300)!important}.react-flow__edge-path{stroke:var(--border-strong)}.react-flow.rf-edges-panning .react-flow__edges{display:none}.react-flow__node.node-controls-locked input,.react-flow__node.node-controls-locked textarea,.react-flow__node.node-controls-locked select,.react-flow__node.node-controls-locked button,.react-flow__node.node-controls-locked [role=button]{pointer-events:none!important}.react-flow__node.node-controls-locked{cursor:grab}.react-flow__node.node-controls-locked:active{cursor:grabbing}.react-flow__node.scene-add-available-node{filter:drop-shadow(0 0 10px #f7f0db38)}.react-flow__node.scene-add-available-node>div{outline:1px solid var(--border-strong);outline-offset:4px;border-radius:var(--r-3)}.react-flow__node.scene-add-available-node:after{content:"";border:1px dashed var(--border-strong);border-radius:var(--r-3);pointer-events:none;position:absolute;inset:-8px}.react-flow__node.node-replace-available{filter:drop-shadow(0 0 10px #f7f0db38)}.react-flow__node.node-replace-available>div{outline:1px solid var(--border-strong);outline-offset:4px;border-radius:var(--r-3)}.react-flow__node.node-replace-available:after{content:"";border:1px dashed var(--border-strong);border-radius:var(--r-3);pointer-events:none;position:absolute;inset:-8px}select option{background:var(--bg-elev-2);color:var(--fg)}.spinner{border:2px solid var(--border-tok);border-top-color:var(--fg);border-radius:var(--r-pill);width:16px;height:16px;animation:.7s linear infinite spin}.media-upload-preview:hover .media-upload-overlay{opacity:1!important}@media (prefers-reduced-motion:reduce){*,:before,:after{transition-duration:1ms!important;animation-duration:1ms!important}}.assets-card .assets-card-overlay{opacity:0;transition:opacity .15s}.assets-card:hover .assets-card-overlay{opacity:1}.yt-video-row{transition:background .12s,box-shadow .12s,color .12s}.yt-video-row:hover{box-shadow:inset 2px 0 0 var(--accent), inset 0 0 14px var(--accent-glow);color:var(--text-primary);background:var(--hover-wash)!important}.chat-bubble ::selection{background:var(--signal);color:#0a0a0a}.chat-bubble ::selection{background:var(--signal);color:#0a0a0a}.chat-bubble:hover .chat-bubble-copy,.chat-bubble:focus-within .chat-bubble-copy,.chat-bubble-copy:hover,.chat-bubble-copy:focus{opacity:1!important}.chat-bubble-copy:hover{background:var(--bg-elevated-2,var(--bg-elevated));color:var(--text-primary)!important}.chat-bubble-copy:active{transform:scale(.94)}.sm-brand .sm-brandmark{transform-origin:50%;transition:transform .76s cubic-bezier(.2,.8,.2,1),filter .28s}.sm-brand:hover .sm-brandmark{filter:drop-shadow(0 0 7px color-mix(in srgb, currentColor 45%, transparent));will-change:transform, filter;transform:perspective(340px)rotateY(360deg)scale(1.06)}.sm-brand .sm-word{transition:transform .3s cubic-bezier(.2,.8,.2,1);transform:translate(-4px)}.sm-brand:hover .sm-word{will-change:transform;transform:translate(-4px,-1px)}@media (prefers-reduced-motion:reduce){.sm-brand .sm-brandmark{transition:transform .24s,filter .24s}.sm-brand:hover .sm-brandmark{transform:scale(1.06)}.sm-brand:hover .sm-word{transform:translate(-4px)}}body.sm-mobile-ui input,body.sm-mobile-ui textarea,body.sm-mobile-ui select{font-size:16px!important}body.sm-mobile-ui button,body.sm-mobile-ui select,body.sm-mobile-ui [role=button]{min-height:44px}body.sm-mobile-ui select:not([multiple]):not([size]){height:max(44px,2.75em)}body.sm-mobile-ui button.btn-icon,body.sm-mobile-ui button[data-compact],body.sm-mobile-ui .no-min-tap{min-height:0}body.sm-mobile-ui .yt-video-row{min-width:560px}:where(html:has(.sm-block)){container-type:inline-size}:where(.sm-block){gap:var(--s-5,24px);min-width:0;display:grid}:where(.sm-block,.sm-block *){box-sizing:border-box}:where(.sm-block)>:where(*){min-width:0}@container (width>=720px){:where(.sm-block[data-layout=split]){grid-template-columns:minmax(0,1.6fr) minmax(260px,1fr);align-items:start}:where(.sm-block[data-layout=split])>:where(.sm-stage){grid-row:span 6}}@container (width>=860px){:where(.sm-block:not([data-layout]):has(>.sm-stage)){align-items:start;column-gap:var(--s-5,24px);row-gap:var(--s-4,16px);grid-template-columns:minmax(300px,380px) minmax(0,1fr)}:where(.sm-block:not([data-layout]):has(>.sm-stage))>:where(*){grid-column:1}:where(.sm-block:not([data-layout]):has(>.sm-stage))>:where(.sm-stage){grid-area:1/2/span 12;align-self:stretch;min-height:min(70vh,640px)}:where(.sm-block:not([data-layout]):has(>.sm-stage))>:where(.sm-stage[data-ratio]){align-self:start;min-height:0}}:where(.sm-row){align-items:center;gap:var(--s-2,8px);flex-wrap:wrap;display:flex}@container (width<=519px){:where(.sm-clamp:not([data-open])){-webkit-line-clamp:var(--sm-lines,5);-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}}:where(.sm-stage){min-height:220px;padding:var(--s-3,12px);border-radius:var(--r-3,8px);background:var(--bg-inset);place-items:center;display:grid;position:relative;overflow:hidden}:where(.sm-stage[data-ratio="16:9"]){aspect-ratio:16/9;min-height:0}:where(.sm-stage[data-ratio="9:16"]){aspect-ratio:9/16;justify-self:center;width:auto;height:min(70vh,640px);min-height:0;max-height:70vh}:where(.sm-stage[data-ratio="1:1"]){aspect-ratio:1;min-height:0}:where(.sm-stage)>:where(img,video,canvas){object-fit:contain;border-radius:var(--r-2,4px);max-width:100%;max-height:100%;display:block}:where(.sm-stage[data-fill])>:where(img,video,canvas){object-fit:cover;width:100%;height:100%}:where(.sm-empty){justify-items:center;gap:var(--s-3,12px);padding:var(--s-5,24px) var(--s-4,16px);text-align:center;color:var(--fg-muted);display:grid}:where(.sm-empty)>:where(.sm-glyph){--sm-glyph-size:40px;color:var(--fg-subtle)}:where(.sm-empty)>:where(p){max-width:24ch;font-size:var(--fs-body,1rem);margin:0}:where(.sm-drop){outline:1px dashed var(--border-tok);outline-offset:-8px}:where(.sm-drop[data-over]){outline-color:var(--fg);background:var(--hover-wash)}:where(.sm-actions){align-items:center;gap:var(--s-2,8px);flex-wrap:wrap;display:flex}:where(.sm-actions)>:where(.sm-status){margin-inline-start:var(--s-2,8px)}:where(.sm-primary,.sm-quiet){justify-content:center;align-items:center;gap:var(--s-2,8px);min-height:var(--touch-target,44px);border-radius:var(--r-pill,999px);font:inherit;cursor:pointer;text-align:center;overflow-wrap:anywhere;max-width:100%;padding:10px 20px;font-weight:500;display:inline-flex}:where(.sm-primary){border:1px solid var(--fg);background:var(--fg);color:var(--bg)}:where(.sm-quiet){border:1px solid var(--border-tok);color:var(--fg);background:0 0}:where(.sm-quiet):hover{background:var(--hover-wash)}:where(.sm-icon){min-width:var(--touch-target,44px);min-height:var(--touch-target,44px);border-radius:var(--r-2,4px);color:var(--fg-muted);cursor:pointer;background:0 0;border:0;place-items:center;padding:0;display:inline-grid}:where(.sm-icon):hover{color:var(--fg);background:var(--hover-wash)}:where(.sm-icon[aria-pressed=true]){color:var(--fg);background:var(--press-wash)}:where(.sm-primary,.sm-quiet,.sm-icon):disabled{opacity:.45;cursor:default}:where(.sm-primary,.sm-quiet,.sm-icon):focus-visible{outline:2px solid var(--signal);outline-offset:3px}@container (width<=420px){:where(.sm-actions)>:where(.sm-primary){flex:100%}}:where(.sm-field){gap:6px;min-width:0;display:grid}:where(.sm-field)>:where(label,.sm-label){align-items:center;gap:var(--s-1,4px);font-size:var(--fs-body-s,.875rem);color:var(--fg-muted);min-height:24px;display:flex}:where(.sm-field) :where(input:not([type=checkbox]):not([type=radio]):not([type=range]),select,textarea){width:100%;min-height:var(--touch-target,44px);border:1px solid var(--border-tok);border-radius:var(--r-3,8px);background:var(--bg-elev-1);color:var(--fg);font:inherit;padding:10px 12px}:where(.sm-field) :where(textarea){resize:vertical;min-height:96px}:where(.sm-fields){gap:var(--s-4,16px);grid-template-columns:repeat(auto-fit,minmax(min(100%,200px),1fr));display:grid}:where(.sm-field:not(:has(textarea,.sm-choices,.sm-gallery,.sm-list))){align-items:center;gap:6px var(--s-2,8px);flex-wrap:wrap;display:flex}:where(.sm-field:not(:has(textarea,.sm-choices,.sm-gallery,.sm-list)))>:where(label,.sm-label){flex:0 auto}:where(.sm-field:not(:has(textarea,.sm-choices,.sm-gallery,.sm-list)))>:where(:not(label,.sm-label)){flex:1 1 var(--sm-control,10rem);min-width:0}@container (width>=520px){:where(.sm-field:not(:has(textarea,.sm-choices,.sm-gallery,.sm-list))){align-items:center;gap:var(--s-2,8px);grid-template-columns:minmax(5rem,9rem) minmax(0,1fr);display:grid}:where(.sm-field) :where(input:not([type=checkbox]):not([type=radio]):not([type=range]),select){min-height:36px;padding:6px 10px}:where(.sm-fields){gap:var(--s-2,8px) var(--s-4,16px)}}:where(.sm-choices){gap:var(--s-2,8px);grid-template-columns:repeat(auto-fill,minmax(var(--sm-tile,96px),1fr));border:0;min-width:0;margin:0;padding:0;display:grid}:where(.sm-choices[data-size=wide]){--sm-tile:160px}:where(.sm-choices)>:where(legend){font-size:var(--fs-body-s,.875rem);color:var(--fg-muted);margin-bottom:6px;padding:0}:where(.sm-choices)>:where(label){padding:var(--s-2,8px);border:1px solid var(--border-tok);border-radius:var(--r-3,8px);background:var(--bg-elev-1);color:var(--fg-muted);font-size:var(--fs-caption,.8125rem);text-align:center;cursor:pointer;align-content:start;justify-items:center;gap:6px;display:grid;position:relative}:where(.sm-choices)>:where(label):hover{border-color:var(--border-strong);color:var(--fg)}:where(.sm-choices) :where(input[type=radio],input[type=checkbox]){opacity:0;cursor:pointer;width:100%;height:100%;margin:0;position:absolute;inset:0}:where(.sm-choices)>:where(label:has(input:checked)){border-color:var(--fg);color:var(--fg);box-shadow:inset 0 0 0 1px var(--fg)}:where(.sm-choices)>:where(label:has(input:focus-visible)){outline:2px solid var(--signal);outline-offset:2px}:where(.sm-choices) :where(img,video,canvas,.sm-swatch){width:100%;aspect-ratio:var(--sm-tile-ratio,1);object-fit:cover;border-radius:var(--r-2,4px);background:var(--bg-inset);display:block}:where(.sm-choices) :where(.sm-glyph){--sm-glyph-size:28px;margin:10px 0 4px}:where(.sm-choices[data-ratio="16:9"]){--sm-tile-ratio:16/9}:where(.sm-choices[data-ratio="9:16"]){--sm-tile-ratio:9/16}:where(.sm-choices[data-compact])>:where(label){min-height:var(--touch-target,44px);grid-auto-flow:column;place-content:center;align-items:center;padding:6px 12px}:where(.sm-choices[data-compact]) :where(.sm-glyph){--sm-glyph-size:18px;margin:0}:where(.sm-choices) :where(audio){z-index:1;width:100%;height:32px;position:relative}:where(.sm-shape){box-sizing:border-box;width:calc(var(--sm-shape-size,32px) * min(1,var(--r,1)));aspect-ratio:var(--r,1);border:2px solid;border-radius:3px;max-width:100%;margin:4px auto;display:block}:where(.sm-choices)>:where(label:has(.sm-shape)){grid-template-rows:1fr auto;align-content:stretch}:where(.sm-choices) :where(.sm-shape){--sm-shape-size:36px;align-self:center;margin:6px auto 2px}:where(.sm-choices[data-compact]) :where(.sm-shape){--sm-shape-size:20px;margin:0}:where(.sm-choices[data-segmented]){gap:0;width:fit-content;max-width:100%;display:flex}:where(.sm-choices[data-segmented])>:where(label){min-height:36px;font-size:var(--fs-body-s,.875rem);white-space:nowrap;border-radius:0;justify-content:center;align-items:center;gap:6px;padding:4px 12px;display:inline-flex}:where(.sm-choices[data-segmented])>:where(label+label){margin-inline-start:-1px}:where(.sm-choices[data-segmented])>:where(label:first-of-type){border-start-start-radius:var(--r-pill,999px);border-end-start-radius:var(--r-pill,999px)}:where(.sm-choices[data-segmented])>:where(label:last-of-type){border-start-end-radius:var(--r-pill,999px);border-end-end-radius:var(--r-pill,999px)}:where(.sm-choices[data-segmented])>:where(label:has(input:checked)){z-index:1;background:var(--bg-elev-2);position:relative}:where(.sm-choices[data-segmented]) :where(.sm-glyph){--sm-glyph-size:16px;margin:0}:where(.sm-settings){gap:var(--s-3,12px) var(--s-4,16px);grid-template-columns:repeat(auto-fill,minmax(min(100%,11rem),1fr));align-items:start;min-width:0;display:grid}:where(.sm-settings)>:where(.sm-field){grid-template-columns:minmax(0,1fr);gap:4px}:where(.sm-settings)>:where(.sm-field:not(:has(textarea,.sm-choices,.sm-gallery,.sm-list))){--sm-control:6rem;align-items:center;gap:4px var(--s-2,8px);flex-wrap:wrap;display:flex}:where(.sm-settings)>:where(.sm-choices)>:where(legend),:where(.sm-settings) :where(.sm-label){font-size:var(--fs-caption,.8125rem)}@container (width<=519px){:where(.sm-choices[data-compact],.sm-choices[data-segmented]){scrollbar-width:none;flex-wrap:nowrap;width:auto;display:flex;overflow-x:auto}:where(.sm-choices[data-compact])>:where(label),:where(.sm-choices[data-segmented])>:where(label){flex:none}}:where(.sm-list){border:1px solid var(--border-tok);border-radius:var(--r-3,8px);background:var(--bg-elev-1);min-width:0;margin:0;padding:0;list-style:none;display:grid;overflow:hidden}:where(.sm-list)>:where(li){align-items:center;gap:var(--s-2,8px);min-width:0;min-height:40px;padding:4px var(--s-2,8px);border-bottom:1px solid var(--divider);display:flex}:where(.sm-list)>:where(li:last-child){border-bottom:0}:where(.sm-list)>:where(li:has(>details)){padding:0;display:block}:where(.sm-list)>:where(li)>:where(details)>:where(summary){align-items:center;gap:var(--s-2,8px);min-height:40px;padding:4px var(--s-2,8px);cursor:pointer;list-style:none;display:flex}:where(.sm-list)>:where(li)>:where(details)>:where(summary)::-webkit-details-marker{display:none}:where(.sm-list)>:where(li)>:where(details[open]){background:var(--bg-inset)}:where(.sm-list)>:where(li)>:where(details)>:where(:not(summary)){padding:var(--s-2,8px) var(--s-3,12px) var(--s-3,12px)}:where(.sm-list) :where(img,video,canvas,.sm-swatch){object-fit:cover;border-radius:var(--r-2,4px);background:var(--bg-inset);flex:none;width:40px;height:26px}:where(.sm-list) :where(.sm-line){white-space:nowrap;text-overflow:ellipsis;flex:1;min-width:0;overflow:hidden}:where(.sm-list) :where(small){color:var(--fg-subtle);font-size:var(--fs-caption,.8125rem);white-space:nowrap;font-variant-numeric:tabular-nums;flex:none}:where(.sm-list)>:where(.sm-list-more){display:none}@container (width<=519px){:where(.sm-list[data-peek]:not([data-open]))>:where(li:nth-child(n+4):not(.sm-list-more)){display:none}:where(.sm-list[data-peek]:not([data-open]))>:where(.sm-list-more){color:var(--fg-muted);display:flex}}:where(.sm-gallery){gap:var(--s-2,8px);grid-template-columns:repeat(auto-fill,minmax(min(100%,var(--sm-tile,140px)),1fr));margin:0;padding:0;list-style:none;display:grid}:where(.sm-gallery)>:where(*){border-radius:var(--r-3,8px);background:var(--bg-inset);margin:0;position:relative;overflow:hidden}:where(.sm-gallery) :where(img,video,canvas){width:100%;aspect-ratio:var(--sm-tile-ratio,1);object-fit:cover;display:block}:where(.sm-gallery) :where(figcaption){font-size:var(--fs-caption,.8125rem);color:var(--fg-muted);padding:6px 8px}:where(.sm-gallery) :where([aria-selected=true],.is-selected){box-shadow:inset 0 0 0 2px var(--fg)}:where(.sm-gallery[data-size=small]){--sm-tile:92px;--sm-tile-ratio:3/2}:where(.sm-status){align-items:center;gap:var(--s-2,8px);min-height:24px;font-size:var(--fs-body-s,.875rem);color:var(--fg-muted);display:inline-flex}:where(.sm-status):empty{display:none}:where(.sm-status[data-state]):before{content:"";background:currentColor;border-radius:50%;flex:none;width:14px;height:14px}:where(.sm-status[data-state=loading]):before{background:0 0;border:2px solid;border-right-color:#0000;animation:.8s linear infinite sm-spin}:where(.sm-status[data-state=success]){color:var(--attention-ok,var(--fg))}:where(.sm-status[data-state=success]):before{background:0 0;border:2px solid;border-width:0 2px 2px 0;border-radius:0;width:6px;height:11px;margin:0 4px 3px;transform:rotate(45deg)}:where(.sm-status[data-state=waiting]){color:var(--attention-warning,var(--signal))}:where(.sm-status[data-state=waiting]):before{width:8px;height:8px;margin:0 3px}:where(.sm-status[data-state=error]){color:var(--attention-error,var(--signal))}:where(.sm-status[data-state=error]):before{clip-path:polygon(50% 0,100% 100%,0 100%);border-radius:0}:where(.sm-progress){border-radius:var(--r-pill,999px);background:var(--bg-elev-2);width:100%;height:4px;accent-color:var(--fg);appearance:none;border:0;overflow:hidden}:where(.sm-progress)::-webkit-progress-bar{background:var(--bg-elev-2)}:where(.sm-progress)::-webkit-progress-value{background:var(--fg)}:where(.sm-progress)::-moz-progress-bar{background:var(--fg)}:where(.sm-more){border-top:1px solid var(--divider)}:where(.sm-more)>:where(summary){align-items:center;gap:var(--s-2,8px);min-height:var(--touch-target,44px);color:var(--fg-muted);font-size:var(--fs-body-s,.875rem);cursor:pointer;padding:0;list-style:none;display:flex}:where(.sm-more)>:where(summary)::-webkit-details-marker{display:none}:where(.sm-more)>:where(summary):after{content:"";width:7px;height:7px;transition:transform var(--dur-fast,.16s) var(--ease-out,ease);border:1.5px solid;border-width:0 1.5px 1.5px 0;margin-inline-start:2px;transform:translateY(-2px)rotate(45deg)}:where(.sm-more[open])>:where(summary):after{transform:translateY(2px)rotate(-135deg)}:where(.sm-more)>:where(summary):hover{color:var(--fg)}:where(.sm-more)>:where(:not(summary)){margin-top:var(--s-2,8px)}:where(.sm-more[open]){padding-bottom:var(--s-3,12px)}:where(.sm-label){position:relative}:where(.sm-hint){vertical-align:middle;display:inline-block}:where(:not(.sm-label))>:where(.sm-hint){position:relative}:where(.sm-hint)>:where(summary){width:var(--touch-target,44px);height:var(--touch-target,44px);min-height:0;color:var(--fg-subtle);cursor:pointer;border:0;place-items:center;margin:-8px;padding:0;list-style:none;display:inline-grid}:where(.sm-hint)>:where(summary)::-webkit-details-marker{display:none}:where(.sm-hint)>:where(summary):before{content:"i";width:15px;height:15px;font:600 10px/1 var(--font-body);border:1.5px solid;border-radius:50%;place-items:center;font-style:normal;display:grid}:where(.sm-hint)>:where(summary):hover,:where(.sm-hint[open])>:where(summary){color:var(--fg)}:where(.sm-hint)>:where(:not(summary)){z-index:5;inset-inline-start:0;border:1px solid var(--border-tok);border-radius:var(--r-3,8px);background:var(--bg-elev-2);width:max-content;max-width:min(280px,100cqi - 32px);color:var(--fg);font-size:var(--fs-body-s,.875rem);box-shadow:var(--shadow-1);margin:4px 0 0;padding:10px 12px;line-height:1.45;position:absolute;top:100%}:where(:not(.sm-label))>:where(.sm-hint[open]){display:block}:where(:not(.sm-label))>:where(.sm-hint)>:where(:not(summary)){width:auto;max-width:60ch;position:static}:where(.sm-glyph){--g:var(--sm-glyph-size,20px);width:var(--g);height:var(--g);color:inherit;vertical-align:middle;flex:none;display:inline-block;position:relative}:where(.sm-glyph):before,:where(.sm-glyph):after{content:"";box-sizing:border-box;position:absolute}:where(.sm-glyph[data-glyph=play]):before{clip-path:polygon(0 0,100% 50%,0 100%);background:currentColor;inset:15% 15% 15% 25%}:where(.sm-glyph[data-glyph=pause]):before{border:solid;border-width:0 calc(var(--g)*.14);inset:18% 25%}:where(.sm-glyph[data-glyph=plus]):before,:where(.sm-glyph[data-glyph=close]):before{background:linear-gradient(currentColor,currentColor) 50%/100% 2px no-repeat,linear-gradient(currentColor,currentColor) 50%/2px 100% no-repeat;inset:15%}:where(.sm-glyph[data-glyph=close]):before{transform:rotate(45deg)}:where(.sm-glyph[data-glyph=check]):before{border:2px solid;border-width:0 2px 2px 0;width:30%;height:58%;top:12%;left:36%;transform:rotate(45deg)}:where(.sm-glyph[data-glyph=chevron]):before{border:2px solid;border-width:0 2px 2px 0;width:40%;height:40%;top:22%;left:30%;transform:rotate(45deg)}:where(.sm-glyph[data-glyph=upload]):before{border:2px solid;border-top:0;border-radius:0 0 3px 3px;height:35%;bottom:10%;left:10%;right:10%}:where(.sm-glyph[data-glyph=upload]):after{clip-path:polygon(50% 0,100% 45%,60% 45%,60% 100%,40% 100%,40% 45%,0 45%);background:currentColor;height:62%;top:4%;left:25%;right:25%}:where(.sm-glyph[data-glyph=image]):before{border:2px solid;border-radius:3px;inset:12%}:where(.sm-glyph[data-glyph=image]):after{clip-path:polygon(0 100%,35% 20%,60% 65%,75% 45%,100% 100%);background:currentColor;height:34%;bottom:24%;left:22%;right:22%}:where(.sm-glyph[data-glyph=video]):before{border:2px solid;border-radius:3px;inset:22% 28% 22% 8%}:where(.sm-glyph[data-glyph=video]):after{clip-path:polygon(0 35%,100% 0,100% 100%,0 65%);background:currentColor;width:20%;top:30%;bottom:30%;right:8%}:where(.sm-glyph[data-glyph=audio]):before{background:linear-gradient(currentColor,currentColor) 0 60%/2px 40% no-repeat,linear-gradient(currentColor,currentColor) 33%/2px 80% no-repeat,linear-gradient(currentColor,currentColor) 66%/2px 100% no-repeat,linear-gradient(currentColor,currentColor) 100% 60%/2px 50% no-repeat;inset:15% 12%}:where(.sm-glyph[data-glyph=text]):before{background:linear-gradient(currentColor,currentColor) 0 0/100% 2px no-repeat,linear-gradient(currentColor,currentColor) 0/100% 2px no-repeat,linear-gradient(currentColor,currentColor) 0 100%/60% 2px no-repeat;inset:20% 12%}:where(.sm-glyph[data-glyph=spark]):before{clip-path:polygon(50% 0,60% 40%,100% 50%,60% 60%,50% 100%,40% 60%,0 50%,40% 40%);background:currentColor;inset:8%}:where(.sm-glyph[data-glyph=info]):before{border:2px solid;border-radius:50%;inset:10%}:where(.sm-glyph[data-glyph=info]):after{background:linear-gradient(currentColor,currentColor) 0 0/2px 2px no-repeat,linear-gradient(currentColor,currentColor) 0 100%/2px 60% no-repeat;width:2px;height:46%;margin-left:-1px;top:28%;left:50%}:where(.sm-glyph[data-glyph=warning]):before{clip-path:polygon(50% 0,100% 100%,0 100%);background:currentColor;inset:8% 4% 10%}:where(.sm-glyph[data-glyph=warning]):after{background:linear-gradient(var(--bg),var(--bg)) 0 0/2px 60% no-repeat,linear-gradient(var(--bg),var(--bg)) 0 100%/2px 2px no-repeat;width:2px;height:38%;margin-left:-1px;top:38%;left:50%}:where(.sm-glyph[data-glyph=spinner]):before{border:2px solid;border-right-color:#0000;border-radius:50%;animation:.8s linear infinite sm-spin;inset:12%}:where([class*=sm-][hidden]){display:none!important}:where(.sm-sr){clip-path:inset(50%);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}@keyframes sm-spin{to{transform:rotate(360deg)}}@media (prefers-reduced-motion:reduce){:where(.sm-status[data-state=loading]):before,:where(.sm-glyph[data-glyph=spinner]):before{opacity:.6;border-right-color:currentColor;animation:none}:where(.sm-more)>:where(summary):after{transition:none}}.sm-quiet{border:1px solid var(--border-tok)}html,body{background:var(--bg);color:var(--fg);margin:0}.dh-page{min-height:100dvh;font-family:var(--font-body);flex-direction:column;display:flex}.dh-header{justify-content:space-between;align-items:flex-end;gap:var(--s-4,16px);padding:var(--s-5,20px) var(--s-6,24px) var(--s-4,16px);border-bottom:var(--bw-1,1px) solid var(--border-tok);flex-wrap:wrap;display:flex}.dh-title{gap:var(--s-1,4px);min-width:0;display:grid}.dh-wordmark{color:var(--fg-muted);font-size:13px}.dh-title h1{font-family:var(--font-display);letter-spacing:var(--tr-display);overflow-wrap:anywhere;margin:0;font-size:30px;font-style:italic;font-weight:200;line-height:1.1}.dh-meta{color:var(--fg-muted);overflow-wrap:anywhere;font-size:12px}.dh-controls{align-items:flex-end;gap:var(--s-3,12px);flex-wrap:wrap;display:flex}.dh-controls label{gap:var(--s-1,4px);color:var(--fg-muted);font-size:12px;display:grid}.dh-controls select{min-height:36px;padding:0 var(--s-3,12px);border:var(--bw-1,1px) solid var(--border-tok);border-radius:var(--r-2);background:var(--bg-elev-1);color:var(--fg);font:inherit;font-size:13px}.dh-controls select:focus-visible,.dh-input textarea:focus-visible,.dh-page .btn:focus-visible{outline:2px solid var(--signal);outline-offset:2px}.dh-problem{margin:var(--s-4,16px) var(--s-6,24px) 0;padding:var(--s-3,12px) var(--s-4,16px);border:var(--bw-1,1px) solid var(--border-strong);border-radius:var(--r-2);background:var(--bg-elev-1);white-space:pre-wrap;overflow-wrap:anywhere}.dh-main{gap:var(--s-6,24px);padding:var(--s-6,24px);flex:1;grid-template-columns:minmax(0,1fr) minmax(280px,380px);display:grid}.dh-stage{min-width:0}.dh-frame{min-width:0;margin:0 auto}.dh-noview{gap:var(--s-4,16px);padding:var(--s-6,24px);border:var(--bw-1,1px) solid var(--border-tok);border-radius:var(--r-3);justify-items:start;display:grid}.dh-noview p{color:var(--fg-muted);margin:0}.dh-panel{gap:var(--s-5,20px);flex-direction:column;min-width:0;display:flex}.dh-section-head{justify-content:space-between;align-items:center;gap:var(--s-2,8px);margin-bottom:var(--s-2,8px);display:flex}.dh-section-head h2{letter-spacing:var(--tr-body);margin:0;font-size:13px;font-weight:600}.dh-input textarea{box-sizing:border-box;width:100%;padding:var(--s-3,12px);border:var(--bw-1,1px) solid var(--border-tok);border-radius:var(--r-2);background:var(--bg-elev-1);color:var(--fg);font-family:var(--font-mono);resize:vertical;font-size:12px;line-height:1.5}.dh-input p,.dh-muted{margin:var(--s-2,8px) 0 0;color:var(--fg-muted);font-size:13px}.dh-json{max-height:240px;padding:var(--s-3,12px);border:var(--bw-1,1px) solid var(--border-tok);border-radius:var(--r-2);background:var(--bg-elev-1);font-family:var(--font-mono);white-space:pre-wrap;overflow-wrap:anywhere;margin:0;font-size:12px;line-height:1.5;overflow:auto}.dh-console{flex-direction:column;min-height:0;display:flex}.dh-console ol{border-top:var(--bw-1,1px) solid var(--divider);max-height:360px;margin:0;padding:0;list-style:none;overflow:auto}.dh-entry{gap:var(--s-2,8px);padding:var(--s-2,8px) 0;border-bottom:var(--bw-1,1px) solid var(--divider);grid-template-columns:auto auto minmax(0,1fr);align-items:baseline;font-size:12px;display:grid}.dh-entry time{color:var(--fg-muted);font-variant-numeric:tabular-nums}.dh-kind{color:var(--fg-muted);font-size:11px}.dh-entry pre{font-family:var(--font-mono);white-space:pre-wrap;overflow-wrap:anywhere;margin:0}.dh-error .dh-kind,.dh-error pre{color:var(--fg);font-weight:600}.dh-stand-in .dh-kind{color:var(--signal)}@media (width<=900px){.dh-main{padding:var(--s-4,16px);grid-template-columns:minmax(0,1fr)}.dh-header{padding:var(--s-4,16px)}}
1
+ @font-face{font-family:Hanken Grotesk;font-style:normal;font-weight:100 900;font-display:swap;src:url(/fonts/HankenGrotesk-Variable.ttf)format("truetype")}@font-face{font-family:Hanken Grotesk;font-style:italic;font-weight:100 900;font-display:swap;src:url(/fonts/HankenGrotesk-Italic-Variable.ttf)format("truetype")}:root{--cream-50:#fdfaf1;--cream-100:#f7f0db;--cream-200:#ece3c9;--cream-300:#dcd2b5;--cream-600:#4e483a;--ink-50:#f2f0ec;--ink-100:#c9c6be;--ink-300:#4a4843;--ink-400:#26241f;--ink-500:#16140f;--ink-600:#0a0a0a;--signal:#e8b23a;--signal-dim:#e8b23a26;--signal-glow:#e8b23a4d;--font-display:"Balgin", "Hanken Grotesk", ui-sans-serif, sans-serif;--font-body:"Hanken Grotesk", ui-sans-serif, sans-serif;--font-mono:ui-monospace, "SF Mono", Menlo, Consolas, monospace;--font:var(--font-body);--fs-display-xl:clamp(72px, 14vw, 220px);--fs-display-l:clamp(56px, 9vw, 144px);--fs-display-m:clamp(40px, 6vw, 96px);--fs-h4:1.25rem;--fs-body:1rem;--fs-body-s:.875rem;--fs-caption:.8125rem;--fs-micro:.75rem;--tr-display:-.02em;--tr-body:-.005em;--tr-micro:.08em;--lh-display:.92;--lh-tight:1.05;--s-1:4px;--s-2:8px;--s-3:12px;--s-4:16px;--s-5:24px;--s-6:32px;--s-7:48px;--s-8:64px;--s-9:96px;--s-10:128px;--s-11:192px;--page-gutter:clamp(20px, 3vw, 48px);--page-gutter-compact:clamp(16px, 5vw, 24px);--page-section-gap:clamp(32px, 5vw, 64px);--content-readable:720px;--content-wide:1440px;--control-height:40px;--touch-target:44px;--mobile-dock-height:66px;--z-nav:30;--hover:var(--hover-wash);--r-0:0;--r-1:2px;--r-2:4px;--r-3:8px;--r-pill:999px;--radius-sm:var(--r-2);--radius:var(--r-3);--radius-lg:var(--r-3);--bw-hair:.5px;--bw-1:1px;--bw-2:2px;--ease-out:cubic-bezier(.22, 1, .36, 1);--ease-in-out:cubic-bezier(.65, 0, .35, 1);--ease-spring:cubic-bezier(.34, 1.56, .64, 1);--dur-instant:80ms;--dur-fast:.16s;--dur-base:.24s;--dur-slow:.48s;--dur-reveal:.72s}:root,[data-mode=dark]{--lightningcss-light: ;--lightningcss-dark:initial;color-scheme:dark;--bg:#101010;--bg-elev-1:#232221;--bg-elev-2:#33312f;--bg-inset:#171716;--fg:#f3eee5;--fg-muted:#bbb2a7;--fg-subtle:#f3eee594;--fg-on-accent:#191716;--border-tok:#58534d;--border-strong:#8b7e6f;--divider:#393735;--signal:#d0ae77;--signal-dim:#d0ae7726;--signal-glow:#d0ae774d;--hover-wash:#f7f0db0f;--press-wash:#f7f0db1f;--selection-bg:var(--cream-100);--selection-fg:var(--ink-600);--shadow-1:0 1px 0 #f7f0db0a inset, 0 4px 12px #00000080;--shadow-2:0 1px 0 #f7f0db0f inset, 0 16px 48px #0009;--scrim-top:linear-gradient(to bottom, #0a0a0ad9, #0a0a0a00);--scrim-bottom:linear-gradient(to top, #0a0a0ad9, #0a0a0a00);--bg-primary:var(--bg);--bg-secondary:var(--bg-elev-1);--bg-tertiary:var(--bg-elev-2);--bg-elevated:var(--bg-elev-1);--bg-hover:#403d3a;--border:var(--border-tok);--border-light:var(--border-strong);--accent:var(--fg);--accent-hover:var(--cream-50);--accent-dim:var(--hover-wash);--accent-glow:#f7f0db38;--accent-contrast:var(--bg);--editor-standout-bg:var(--fg);--editor-standout-fg:var(--bg);--text-primary:var(--fg);--text-secondary:var(--fg-muted);--text-muted:var(--fg-subtle);--success:var(--fg);--warning:var(--signal);--danger:var(--signal);--attention-warning:#e0b24f;--attention-error:#e26a5f;--attention-ok:#6bc38e;--attention-info:#7da7e8;--shadow:var(--shadow-1);--shadow-lg:var(--shadow-2)}[data-mode=light]{--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light;--signal:#e8b23a;--signal-dim:#e8b23a26;--signal-glow:#e8b23a4d;--bg:var(--cream-100);--bg-elev-1:var(--cream-50);--bg-elev-2:var(--cream-200);--bg-inset:var(--cream-200);--fg:var(--ink-600);--fg-muted:var(--cream-600);--fg-subtle:#0a0a0a61;--fg-on-accent:var(--cream-100);--border-tok:#0a0a0a1a;--border-strong:#0a0a0a38;--divider:#0a0a0a0f;--hover-wash:#0a0a0a0d;--press-wash:#0a0a0a1a;--selection-bg:var(--ink-600);--selection-fg:var(--cream-100);--shadow-1:0 1px 0 #ffffff80 inset, 0 4px 12px #0a0a0a0f;--shadow-2:0 1px 0 #fff9 inset, 0 16px 48px #0a0a0a1a;--scrim-top:linear-gradient(to bottom, #f7f0dbe6, #f7f0db00);--scrim-bottom:linear-gradient(to top, #f7f0dbe6, #f7f0db00);--bg-primary:var(--bg);--bg-secondary:var(--bg-elev-1);--bg-tertiary:var(--bg-elev-2);--bg-elevated:var(--bg-elev-2);--bg-hover:var(--cream-300);--border:var(--border-tok);--border-light:var(--border-strong);--accent:var(--fg);--accent-hover:var(--ink-500);--accent-dim:var(--hover-wash);--accent-glow:#0a0a0a38;--accent-contrast:var(--bg);--editor-standout-bg:var(--ink-300);--editor-standout-fg:var(--cream-100);--text-primary:var(--fg);--text-secondary:var(--fg-muted);--text-muted:var(--fg-subtle);--success:var(--fg);--warning:var(--signal);--danger:var(--signal);--attention-warning:#b98200;--attention-error:#c2453d;--attention-ok:#2e7d4f;--attention-info:#2f67b8;--shadow:var(--shadow-1);--shadow-lg:var(--shadow-2)}[data-mode=white]{--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light;--bg:#fff;--bg-elev-1:#fff;--bg-elev-2:#f5f5f5;--bg-inset:#fafafa;--fg:#0e0e0e;--fg-muted:#5c5c5c;--fg-subtle:#767676;--fg-on-accent:#fff;--border-tok:#e4e4e4;--border-strong:#c9c9c9;--divider:#ededed;--hover-wash:#0000000d;--press-wash:#00000017;--selection-bg:#0e0e0e;--selection-fg:#fff;--signal:#8e6b46;--signal-dim:#8e6b461a;--signal-glow:#8e6b4638;--shadow-1:0 1px 3px #0000000d;--shadow-2:0 12px 40px #0000001a;--scrim-top:linear-gradient(to bottom,#ffffffe6,#fff0);--scrim-bottom:linear-gradient(to top,#ffffffe6,#fff0);--bg-primary:var(--bg);--bg-secondary:var(--bg-elev-1);--bg-tertiary:var(--bg-elev-2);--bg-elevated:var(--bg-elev-1);--bg-hover:#f2f2f2;--border:var(--border-tok);--border-light:var(--border-strong);--accent:var(--fg);--accent-hover:#2a2a2a;--accent-dim:var(--hover-wash);--accent-glow:#0000001f;--accent-contrast:var(--bg);--editor-standout-bg:#24211d;--editor-standout-fg:#fff;--text-primary:var(--fg);--text-secondary:var(--fg-muted);--text-muted:var(--fg-subtle);--success:var(--fg);--warning:var(--signal);--danger:var(--signal);--attention-warning:#b98200;--attention-error:#c2453d;--attention-ok:#2e7d4f;--attention-info:#2f67b8;--shadow:var(--shadow-1);--shadow-lg:var(--shadow-2)}button[data-icon-action=true]{cursor:pointer;flex-shrink:0;line-height:1;min-width:36px!important;min-height:36px!important;box-shadow:none!important;color:var(--text-secondary,var(--fg))!important;background:0 0!important;border:0!important;border-radius:0!important;justify-content:center!important;align-items:center!important;padding:8px!important;display:inline-flex!important}button[data-icon-action=true]:hover:not(:disabled){color:var(--text-primary,var(--fg))!important}button[data-icon-action=true]:focus-visible{outline-offset:2px;outline:2px solid var(--signal)!important}button[data-icon-action=true]:disabled{cursor:not-allowed;opacity:.4!important}button[data-icon-action=true][aria-pressed=true]{text-underline-offset:5px;text-decoration:underline;color:var(--text-primary,var(--fg))!important}@media (pointer:coarse){button[data-icon-action=true]{min-width:44px!important;min-height:44px!important}}*,:before,:after{box-sizing:border-box;margin:0;padding:0}:root[data-anim-theme="1"]:not([data-mode=white]){--lightningcss-light: ;--lightningcss-dark:initial;color-scheme:dark;--bg:#0a0d13;--bg-elev-1:#0f141c;--bg-elev-2:#1a2230;--bg-inset:#0f141c;--fg:#d8dee8;--fg-muted:#8b98ac;--fg-subtle:#64748b;--fg-on-accent:#0a0d13;--border-tok:#232d3d;--border-strong:#303d50;--divider:#1a2230;--hover-wash:#8b98ac14;--press-wash:#8b98ac29;--selection-bg:#303d50;--selection-fg:#d8dee8;--bg-primary:#0a0d13;--bg-secondary:#0f141c;--bg-tertiary:#1a2230;--bg-elevated:#1a2230;--bg-hover:#232d3d;--border:#232d3d;--border-light:#303d50;--accent:#8b98ac;--accent-hover:#b6c0ce;--accent-dim:#8b98ac1f;--accent-glow:#8b98ac40;--accent-contrast:#0a0d13;--editor-standout-bg:#d8dee8;--editor-standout-fg:#0a0d13;--text-primary:#d8dee8;--text-secondary:#8b98ac;--text-muted:#64748b;--success:#8b98ac;--warning:#b6c0ce;--danger:#b6c0ce}html,body{background:var(--bg);width:100%;height:100%;color:var(--fg);font-family:var(--font-body);letter-spacing:var(--tr-body);-webkit-font-smoothing:antialiased;text-rendering:optimizelegibility;font-size:14px;line-height:1.6;overflow:hidden}#root{width:100%;height:100%;overflow:hidden}.sm-app-shell{height:100dvh}@keyframes sm-navmenu-in{0%{opacity:0;transform:translateY(10px)}to{opacity:1;transform:none}}.mobile-nav-menu{animation:.2s cubic-bezier(.22,1,.36,1) sm-navmenu-in}::selection{background:var(--selection-bg);color:var(--selection-fg)}.display,h1,h2{font-family:var(--font-display);font-style:italic;font-weight:200;line-height:var(--lh-tight);letter-spacing:var(--tr-display);margin:0}.display{line-height:var(--lh-display)}h3,h4{font-family:var(--font-body);font-weight:500;line-height:var(--lh-tight);letter-spacing:-.01em;margin:0}.eyebrow{color:var(--fg-muted);font-size:11px;font-weight:500}code,pre,.mono{font-family:var(--font-mono);font-size:.92em}.listing-identity code{font-family:inherit;font-size:inherit}button{font-family:var(--font-body);cursor:pointer;letter-spacing:var(--tr-body);border:none;outline:none}textarea,input,select{font-family:var(--font-body);outline:none}select{background-color:var(--bg-elev-1);color:var(--text-primary);border:1px solid var(--border);border-radius:var(--r-2)}select option,select optgroup{background-color:var(--bg-elevated);color:var(--text-primary)}select option:checked{font-weight:600}.tsel-trigger{background-color:var(--bg-elev-1);color:var(--text-primary);border:1px solid var(--border);border-radius:var(--r-2);font-family:var(--font-body);font-size:inherit;cursor:pointer;text-align:left;justify-content:space-between;align-items:center;gap:8px;padding:6px 10px;display:inline-flex}.tsel-trigger:hover{border-color:var(--border-strong)}.tsel-trigger:focus-visible{outline:2px solid var(--accent);outline-offset:1px}.tsel-trigger:disabled{opacity:.5;cursor:default}.tsel-label{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.tsel-caret{opacity:.7;flex:none;font-size:10px}.tsel-pop{z-index:100100;background:var(--bg-elevated);border:1px solid var(--border);border-radius:var(--r-2);max-height:260px;padding:4px;overflow-y:auto;box-shadow:0 8px 28px #00000047}.tsel-opt{color:var(--text-primary);cursor:pointer;white-space:nowrap;border-radius:6px;align-items:center;gap:6px;padding:6px 8px;font-size:13px;display:flex}.tsel-opt[data-active=true]{background:var(--bg-elev-1)}.tsel-opt[aria-selected=true]{font-weight:600}.tsel-opt[aria-disabled=true]{color:var(--text-muted);cursor:default}.tsel-opt[aria-disabled=true][data-active=true]{background:0 0}.tsel-check{opacity:.8;flex:none;width:12px;font-size:11px}.tsel-opt-label{text-overflow:ellipsis;flex:auto;overflow:hidden}.tsel-note{cursor:help;width:16px;height:16px;color:var(--text-muted);border:1px solid var(--border);border-radius:999px;flex:none;justify-content:center;align-items:center;font-size:11px;display:inline-flex;position:relative}.tsel-note:hover{color:var(--text-primary);border-color:var(--border-strong)}.tsel-tip{z-index:100110;background:var(--text-primary);color:var(--bg-elevated);box-sizing:border-box;white-space:normal;pointer-events:none;border-radius:5px;max-width:min(260px,100vw - 16px);padding:4px 8px;font-size:11px;font-weight:600;box-shadow:0 2px 10px #0000004d}@media (width<=767.98px),(height<=480px) and (pointer:coarse){.tsel-opt{box-sizing:border-box;min-height:44px;font-size:14px}.tsel-note{flex:0 0 44px;width:44px;height:44px;font-size:14px}}.video-editor input,.video-editor select,.video-editor textarea{background:var(--bg-inset);width:100%;min-width:0;color:var(--fg);border:var(--bw-1) solid var(--border-strong);border-radius:var(--r-2);font-family:var(--font-body);accent-color:var(--signal);padding:7px 8px;font-size:12px}.video-editor,.mobile-editor-root{-webkit-user-select:none;user-select:none;-webkit-touch-callout:none}.video-editor input,.video-editor select,.video-editor textarea,.video-editor [contenteditable=true],.mobile-editor-root input,.mobile-editor-root select,.mobile-editor-root textarea,.mobile-editor-root [contenteditable=true]{-webkit-user-select:text;user-select:text;-webkit-touch-callout:default}.video-editor input[type=range]{background:0 0;border:0;padding:0}.video-editor input[type=checkbox]{width:auto;min-width:auto}.video-editor select:focus,.video-editor input:focus,.video-editor textarea:focus{border-color:var(--signal);box-shadow:0 0 0 1px var(--signal-dim)}:focus-visible{outline:2px solid var(--signal);outline-offset:2px}::-webkit-scrollbar{width:6px;height:6px}::-webkit-scrollbar-track{background:0 0}::-webkit-scrollbar-thumb{background:var(--border-strong);border-radius:var(--r-pill)}::-webkit-scrollbar-thumb:hover{background:var(--fg-subtle)}.sm-hide-scrollbar{scrollbar-width:none;-ms-overflow-style:none}.sm-hide-scrollbar::-webkit-scrollbar{width:0;height:0;display:none}.btn{align-items:center;gap:var(--s-2);padding:var(--s-2) var(--s-4);border-radius:var(--r-pill);letter-spacing:var(--tr-body);transition:transform var(--dur-fast) var(--ease-out), background-color var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out), opacity var(--dur-fast) var(--ease-out);font-size:13px;font-weight:500;display:inline-flex}.btn:active{transform:scale(.98)}.btn-primary{background:var(--fg);color:var(--bg)}.btn-primary:hover{background:var(--fg);opacity:.86}.btn-primary:disabled{opacity:.4;cursor:not-allowed}.btn-secondary{color:var(--fg);border:var(--bw-1) solid var(--border-strong);background:0 0}.btn-secondary:hover{background:var(--hover-wash);border-color:var(--fg)}.btn-ghost{color:var(--fg-muted);background:0 0;padding:6px 10px}.btn-ghost:hover{background:var(--hover-wash);color:var(--fg)}.btn-icon{color:var(--fg-muted);border-radius:var(--r-2);transition:background-color var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out), transform var(--dur-fast) var(--ease-out);background:0 0;justify-content:center;align-items:center;padding:5px;display:inline-flex}.btn-icon:hover{background:var(--hover-wash);color:var(--fg)}.btn-icon:active{transform:scale(.98)}.btn-icon:disabled{opacity:.4;cursor:not-allowed}.typing-indicator{align-items:center;gap:4px;padding:4px 0;display:flex}.typing-indicator span{border-radius:var(--r-pill);background:var(--fg-subtle);width:6px;height:6px;animation:1.2s infinite bounce}.typing-indicator span:nth-child(2){animation-delay:.2s}.typing-indicator span:nth-child(3){animation-delay:.4s}@keyframes bounce{0%,60%,to{opacity:.4;transform:translateY(0)}30%{opacity:1;transform:translateY(-5px)}}@keyframes fadeIn{0%{opacity:0;transform:translateY(6px)}to{opacity:1;transform:translateY(0)}}@keyframes spin{to{transform:rotate(360deg)}}@keyframes pulse{0%,to{opacity:.4;transform:scale(.9)}50%{opacity:1;transform:scale(1.1)}}@keyframes toastIn{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}button:focus-visible,[role=button]:focus-visible,a:focus-visible,input:focus-visible,select:focus-visible,textarea:focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-radius:4px}input:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=color]):not([type=file]):not([type=button]):not([type=submit]):not([type=reset]):is(:focus,:focus-visible),textarea:is(:focus,:focus-visible),select:is(:focus,:focus-visible),[contenteditable]:not([contenteditable=false]):is(:focus,:focus-visible){outline:none!important}@keyframes skeletonShimmer{0%{background-position:-200% 0}to{background-position:200% 0}}.skeleton{background:linear-gradient(90deg,#ffffff08 0%,#ffffff1a 50%,#ffffff08 100%) 0 0/200% 100%;border-radius:6px;animation:1.4s ease-in-out infinite skeletonShimmer}.node-state-empty{border-style:dashed!important}.node-state-generating{box-shadow:0 0 0 2px var(--accent);position:relative}.node-state-generating:after{content:"";border-radius:inherit;pointer-events:none;will-change:opacity;animation:1.6s ease-in-out infinite nodePulse;position:absolute;inset:-2px;box-shadow:0 0 18px #f7c8328c}@keyframes nodePulse{0%,to{opacity:.36}50%{opacity:1}}.node-state-warning{position:relative;border-color:var(--attention-warning)!important}.node-state-warning:after{content:"";border-radius:inherit;pointer-events:none;box-shadow:0 0 0 1px var(--attention-warning), 0 0 14px color-mix(in srgb, var(--attention-warning) 40%, transparent);position:absolute;inset:-2px}.node-state-failed{border-top:4px solid #d04848!important}.node-state-failed:before{content:"⚠ ";color:#d04848;z-index:2;pointer-events:none;font-size:12px;font-weight:800;position:absolute;top:6px;left:8px}.react-flow__node{cursor:default}.react-flow__node.dragging,.react-flow__pane.dragging .react-flow__viewport{will-change:transform}.react-flow__node:not(.react-flow__node-sceneGroupNode):not(.react-flow__node-shotLabelNode):not(.react-flow__node-stickyNoteNode):not(.react-flow__node-transitionNode){box-shadow:1px 1px 0 var(--node-side,#d8cfb3), 2px 2px 0 var(--node-side,#d8cfb3), 3px 3px 0 var(--node-side,#d8cfb3), 4px 4px 0 var(--node-side,#d8cfb3), 5px 5px 0 var(--node-side,#d8cfb3), 6px 6px 0 var(--node-side,#d8cfb3), 7px 7px 0 var(--node-edge,#0a0a0a2e), 14px 16px 18px -4px #0a0a0a2e;border-radius:10px}[data-mode=dark] .react-flow__node:not(.react-flow__node-sceneGroupNode):not(.react-flow__node-shotLabelNode):not(.react-flow__node-stickyNoteNode):not(.react-flow__node-transitionNode){--node-side:#1f1d18;--node-edge:#f7f0db2e}.react-flow__controls{grid-template-columns:repeat(2,1fr)!important;display:grid!important}.react-flow__controls button{background:var(--bg-elev-2)!important;border-color:var(--border-tok)!important;color:var(--fg-muted)!important;fill:var(--fg-muted)!important}.react-flow__controls button:hover{background:var(--ink-300)!important}.react-flow__edge-path{stroke:var(--border-strong)}.react-flow.rf-edges-panning .react-flow__edges{display:none}.react-flow__node.node-controls-locked input,.react-flow__node.node-controls-locked textarea,.react-flow__node.node-controls-locked select,.react-flow__node.node-controls-locked button,.react-flow__node.node-controls-locked [role=button]{pointer-events:none!important}.react-flow__node.node-controls-locked{cursor:grab}.react-flow__node.node-controls-locked:active{cursor:grabbing}.react-flow__node.scene-add-available-node{filter:drop-shadow(0 0 10px #f7f0db38)}.react-flow__node.scene-add-available-node>div{outline:1px solid var(--border-strong);outline-offset:4px;border-radius:var(--r-3)}.react-flow__node.scene-add-available-node:after{content:"";border:1px dashed var(--border-strong);border-radius:var(--r-3);pointer-events:none;position:absolute;inset:-8px}.react-flow__node.node-replace-available{filter:drop-shadow(0 0 10px #f7f0db38)}.react-flow__node.node-replace-available>div{outline:1px solid var(--border-strong);outline-offset:4px;border-radius:var(--r-3)}.react-flow__node.node-replace-available:after{content:"";border:1px dashed var(--border-strong);border-radius:var(--r-3);pointer-events:none;position:absolute;inset:-8px}select option{background:var(--bg-elev-2);color:var(--fg)}.spinner{border:2px solid var(--border-tok);border-top-color:var(--fg);border-radius:var(--r-pill);width:16px;height:16px;animation:.7s linear infinite spin}.media-upload-preview:hover .media-upload-overlay{opacity:1!important}@media (prefers-reduced-motion:reduce){*,:before,:after{transition-duration:1ms!important;animation-duration:1ms!important}}.assets-card .assets-card-overlay{opacity:0;transition:opacity .15s}.assets-card:hover .assets-card-overlay{opacity:1}.yt-video-row{transition:background .12s,box-shadow .12s,color .12s}.yt-video-row:hover{box-shadow:inset 2px 0 0 var(--accent), inset 0 0 14px var(--accent-glow);color:var(--text-primary);background:var(--hover-wash)!important}.chat-bubble ::selection{background:var(--signal);color:#0a0a0a}.chat-bubble ::selection{background:var(--signal);color:#0a0a0a}.chat-bubble:hover .chat-bubble-copy,.chat-bubble:focus-within .chat-bubble-copy,.chat-bubble-copy:hover,.chat-bubble-copy:focus{opacity:1!important}.chat-bubble-copy:hover{background:var(--bg-elevated-2,var(--bg-elevated));color:var(--text-primary)!important}.chat-bubble-copy:active{transform:scale(.94)}.sm-brand .sm-brandmark{transform-origin:50%;transition:transform .76s cubic-bezier(.2,.8,.2,1),filter .28s}.sm-brand:hover .sm-brandmark{filter:drop-shadow(0 0 7px color-mix(in srgb, currentColor 45%, transparent));will-change:transform, filter;transform:perspective(340px)rotateY(360deg)scale(1.06)}.sm-brand .sm-word{transition:transform .3s cubic-bezier(.2,.8,.2,1);transform:translate(-4px)}.sm-brand:hover .sm-word{will-change:transform;transform:translate(-4px,-1px)}@media (prefers-reduced-motion:reduce){.sm-brand .sm-brandmark{transition:transform .24s,filter .24s}.sm-brand:hover .sm-brandmark{transform:scale(1.06)}.sm-brand:hover .sm-word{transform:translate(-4px)}}body.sm-mobile-ui input,body.sm-mobile-ui textarea,body.sm-mobile-ui select{font-size:16px!important}body.sm-mobile-ui button,body.sm-mobile-ui select,body.sm-mobile-ui [role=button]{min-height:44px}body.sm-mobile-ui select:not([multiple]):not([size]){height:max(44px,2.75em)}body.sm-mobile-ui button.btn-icon,body.sm-mobile-ui button[data-compact],body.sm-mobile-ui .no-min-tap{min-height:0}body.sm-mobile-ui .yt-video-row{min-width:560px}:where(html:has(.sm-block)){container-type:inline-size}:where(.sm-block){gap:var(--s-5,24px);min-width:0;display:grid}:where(.sm-block,.sm-block *){box-sizing:border-box}:where(.sm-block)>:where(*){min-width:0}@container (width>=720px){:where(.sm-block[data-layout=split]){grid-template-columns:minmax(0,1.6fr) minmax(260px,1fr);align-items:start}:where(.sm-block[data-layout=split])>:where(.sm-stage){grid-row:span 6}}@container (width>=860px){:where(.sm-block:not([data-layout]):has(>.sm-stage)){align-items:start;column-gap:var(--s-5,24px);row-gap:var(--s-4,16px);grid-template-columns:minmax(300px,380px) minmax(0,1fr)}:where(.sm-block:not([data-layout]):has(>.sm-stage))>:where(*){grid-column:1}:where(.sm-block:not([data-layout]):has(>.sm-stage))>:where(.sm-stage){grid-area:1/2/span 12;align-self:stretch;min-height:min(70vh,640px)}:where(.sm-block:not([data-layout]):has(>.sm-stage))>:where(.sm-stage[data-ratio]){align-self:start;min-height:0}}:where(.sm-row){align-items:center;gap:var(--s-2,8px);flex-wrap:wrap;display:flex}@container (width<=519px){:where(.sm-clamp:not([data-open])){-webkit-line-clamp:var(--sm-lines,5);-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}}:where(.sm-stage){min-height:220px;padding:var(--s-3,12px);border-radius:var(--r-3,8px);background:var(--bg-inset);place-items:center;display:grid;position:relative;overflow:hidden}:where(.sm-stage[data-ratio="16:9"]){aspect-ratio:16/9;min-height:0}:where(.sm-stage[data-ratio="9:16"]){aspect-ratio:9/16;justify-self:center;width:auto;height:min(70vh,640px);min-height:0;max-height:70vh}:where(.sm-stage[data-ratio="1:1"]){aspect-ratio:1;min-height:0}:where(.sm-stage)>:where(img,video,canvas){object-fit:contain;border-radius:var(--r-2,4px);max-width:100%;max-height:100%;display:block}:where(.sm-stage[data-fill])>:where(img,video,canvas){object-fit:cover;width:100%;height:100%}:where(.sm-empty){justify-items:center;gap:var(--s-3,12px);padding:var(--s-5,24px) var(--s-4,16px);text-align:center;color:var(--fg-muted);display:grid}:where(.sm-empty)>:where(.sm-glyph){--sm-glyph-size:40px;color:var(--fg-subtle)}:where(.sm-empty)>:where(p){max-width:24ch;font-size:var(--fs-body,1rem);margin:0}:where(.sm-drop){outline:1px dashed var(--border-tok);outline-offset:-8px}:where(.sm-drop[data-over]){outline-color:var(--fg);background:var(--hover-wash)}:where(.sm-actions){align-items:center;gap:var(--s-2,8px);flex-wrap:wrap;display:flex}:where(.sm-actions)>:where(.sm-status){margin-inline-start:var(--s-2,8px)}:where(.sm-primary,.sm-quiet){justify-content:center;align-items:center;gap:var(--s-2,8px);min-height:var(--touch-target,44px);border-radius:var(--r-pill,999px);font:inherit;cursor:pointer;text-align:center;overflow-wrap:anywhere;max-width:100%;padding:10px 20px;font-weight:500;display:inline-flex}:where(.sm-primary){border:1px solid var(--fg);background:var(--fg);color:var(--bg)}:where(.sm-quiet){border:1px solid var(--border-tok);color:var(--fg);background:0 0}:where(.sm-quiet):hover{background:var(--hover-wash)}:where(.sm-icon){min-width:var(--touch-target,44px);min-height:var(--touch-target,44px);border-radius:var(--r-2,4px);color:var(--fg-muted);cursor:pointer;background:0 0;border:0;place-items:center;padding:0;display:inline-grid}:where(.sm-icon):hover{color:var(--fg);background:var(--hover-wash)}:where(.sm-icon[aria-pressed=true]){color:var(--fg);background:var(--press-wash)}:where(.sm-primary,.sm-quiet,.sm-icon):disabled{opacity:.45;cursor:default}:where(.sm-primary,.sm-quiet,.sm-icon):focus-visible{outline:2px solid var(--signal);outline-offset:3px}@container (width<=420px){:where(.sm-actions)>:where(.sm-primary){flex:100%}}:where(.sm-field){gap:6px;min-width:0;display:grid}:where(.sm-field)>:where(label,.sm-label){align-items:center;gap:var(--s-1,4px);font-size:var(--fs-body-s,.875rem);color:var(--fg-muted);min-height:24px;display:flex}:where(.sm-field) :where(input:not([type=checkbox]):not([type=radio]):not([type=range]),select,textarea){width:100%;min-height:var(--touch-target,44px);border:1px solid var(--border-tok);border-radius:var(--r-3,8px);background:var(--bg-elev-1);color:var(--fg);font:inherit;padding:10px 12px}:where(.sm-field) :where(textarea){resize:vertical;min-height:96px}:where(.sm-fields){gap:var(--s-4,16px);grid-template-columns:repeat(auto-fit,minmax(min(100%,200px),1fr));display:grid}:where(.sm-field:not(:has(textarea,.sm-choices,.sm-gallery,.sm-list))){align-items:center;gap:6px var(--s-2,8px);flex-wrap:wrap;display:flex}:where(.sm-field:not(:has(textarea,.sm-choices,.sm-gallery,.sm-list)))>:where(label,.sm-label){flex:0 auto}:where(.sm-field:not(:has(textarea,.sm-choices,.sm-gallery,.sm-list)))>:where(:not(label,.sm-label)){flex:1 1 var(--sm-control,10rem);min-width:0}@container (width>=520px){:where(.sm-field:not(:has(textarea,.sm-choices,.sm-gallery,.sm-list))){align-items:center;gap:var(--s-2,8px);grid-template-columns:minmax(5rem,9rem) minmax(0,1fr);display:grid}:where(.sm-field) :where(input:not([type=checkbox]):not([type=radio]):not([type=range]),select){min-height:36px;padding:6px 10px}:where(.sm-fields){gap:var(--s-2,8px) var(--s-4,16px)}}:where(.sm-choices){gap:var(--s-2,8px);grid-template-columns:repeat(auto-fill,minmax(var(--sm-tile,96px),1fr));border:0;min-width:0;margin:0;padding:0;display:grid}:where(.sm-choices[data-size=wide]){--sm-tile:160px}:where(.sm-choices)>:where(legend){font-size:var(--fs-body-s,.875rem);color:var(--fg-muted);margin-bottom:6px;padding:0}:where(.sm-choices)>:where(label){padding:var(--s-2,8px);border:1px solid var(--border-tok);border-radius:var(--r-3,8px);background:var(--bg-elev-1);color:var(--fg-muted);font-size:var(--fs-caption,.8125rem);text-align:center;cursor:pointer;align-content:start;justify-items:center;gap:6px;display:grid;position:relative}:where(.sm-choices)>:where(label):hover{border-color:var(--border-strong);color:var(--fg)}:where(.sm-choices) :where(input[type=radio],input[type=checkbox]){opacity:0;cursor:pointer;width:100%;height:100%;margin:0;position:absolute;inset:0}:where(.sm-choices)>:where(label:has(input:checked)){border-color:var(--fg);color:var(--fg);box-shadow:inset 0 0 0 1px var(--fg)}:where(.sm-choices)>:where(label:has(input:focus-visible)){outline:2px solid var(--signal);outline-offset:2px}:where(.sm-choices) :where(img,video,canvas,.sm-swatch){width:100%;aspect-ratio:var(--sm-tile-ratio,1);object-fit:cover;border-radius:var(--r-2,4px);background:var(--bg-inset);display:block}:where(.sm-choices) :where(.sm-glyph){--sm-glyph-size:28px;margin:10px 0 4px}:where(.sm-choices[data-ratio="16:9"]){--sm-tile-ratio:16/9}:where(.sm-choices[data-ratio="9:16"]){--sm-tile-ratio:9/16}:where(.sm-choices[data-compact])>:where(label){min-height:var(--touch-target,44px);grid-auto-flow:column;place-content:center;align-items:center;padding:6px 12px}:where(.sm-choices[data-compact]) :where(.sm-glyph){--sm-glyph-size:18px;margin:0}:where(.sm-choices) :where(audio){z-index:1;width:100%;height:32px;position:relative}:where(.sm-shape){box-sizing:border-box;width:calc(var(--sm-shape-size,32px) * min(1,var(--r,1)));aspect-ratio:var(--r,1);border:2px solid;border-radius:3px;max-width:100%;margin:4px auto;display:block}:where(.sm-choices)>:where(label:has(.sm-shape)){grid-template-rows:1fr auto;align-content:stretch}:where(.sm-choices) :where(.sm-shape){--sm-shape-size:36px;align-self:center;margin:6px auto 2px}:where(.sm-choices[data-compact]) :where(.sm-shape){--sm-shape-size:20px;margin:0}:where(.sm-choices[data-segmented]){gap:0;width:fit-content;max-width:100%;display:flex}:where(.sm-choices[data-segmented])>:where(label){min-height:36px;font-size:var(--fs-body-s,.875rem);white-space:nowrap;border-radius:0;justify-content:center;align-items:center;gap:6px;padding:4px 12px;display:inline-flex}:where(.sm-choices[data-segmented])>:where(label+label){margin-inline-start:-1px}:where(.sm-choices[data-segmented])>:where(label:first-of-type){border-start-start-radius:var(--r-pill,999px);border-end-start-radius:var(--r-pill,999px)}:where(.sm-choices[data-segmented])>:where(label:last-of-type){border-start-end-radius:var(--r-pill,999px);border-end-end-radius:var(--r-pill,999px)}:where(.sm-choices[data-segmented])>:where(label:has(input:checked)){z-index:1;background:var(--bg-elev-2);position:relative}:where(.sm-choices[data-segmented]) :where(.sm-glyph){--sm-glyph-size:16px;margin:0}:where(.sm-settings){gap:var(--s-3,12px) var(--s-4,16px);grid-template-columns:repeat(auto-fill,minmax(min(100%,11rem),1fr));align-items:start;min-width:0;display:grid}:where(.sm-settings)>:where(.sm-field){grid-template-columns:minmax(0,1fr);gap:4px}:where(.sm-settings)>:where(.sm-field:not(:has(textarea,.sm-choices,.sm-gallery,.sm-list))){--sm-control:6rem;align-items:center;gap:4px var(--s-2,8px);flex-wrap:wrap;display:flex}:where(.sm-settings)>:where(.sm-choices)>:where(legend),:where(.sm-settings) :where(.sm-label){font-size:var(--fs-caption,.8125rem)}@container (width<=519px){:where(.sm-choices[data-compact],.sm-choices[data-segmented]){scrollbar-width:none;flex-wrap:nowrap;width:auto;display:flex;overflow-x:auto}:where(.sm-choices[data-compact])>:where(label),:where(.sm-choices[data-segmented])>:where(label){flex:none}}:where(.sm-list){border:1px solid var(--border-tok);border-radius:var(--r-3,8px);background:var(--bg-elev-1);min-width:0;margin:0;padding:0;list-style:none;display:grid;overflow:hidden}:where(.sm-list)>:where(li){align-items:center;gap:var(--s-2,8px);min-width:0;min-height:40px;padding:4px var(--s-2,8px);border-bottom:1px solid var(--divider);display:flex}:where(.sm-list)>:where(li:last-child){border-bottom:0}:where(.sm-list)>:where(li:has(>details)){padding:0;display:block}:where(.sm-list)>:where(li)>:where(details)>:where(summary){align-items:center;gap:var(--s-2,8px);min-height:40px;padding:4px var(--s-2,8px);cursor:pointer;list-style:none;display:flex}:where(.sm-list)>:where(li)>:where(details)>:where(summary)::-webkit-details-marker{display:none}:where(.sm-list)>:where(li)>:where(details[open]){background:var(--bg-inset)}:where(.sm-list)>:where(li)>:where(details)>:where(:not(summary)){padding:var(--s-2,8px) var(--s-3,12px) var(--s-3,12px)}:where(.sm-list) :where(img,video,canvas,.sm-swatch){object-fit:cover;border-radius:var(--r-2,4px);background:var(--bg-inset);flex:none;width:40px;height:26px}:where(.sm-list) :where(.sm-line){white-space:nowrap;text-overflow:ellipsis;flex:1;min-width:0;overflow:hidden}:where(.sm-list) :where(small){color:var(--fg-subtle);font-size:var(--fs-caption,.8125rem);white-space:nowrap;font-variant-numeric:tabular-nums;flex:none}:where(.sm-list)>:where(.sm-list-more){display:none}@container (width<=519px){:where(.sm-list[data-peek]:not([data-open]))>:where(li:nth-child(n+4):not(.sm-list-more)){display:none}:where(.sm-list[data-peek]:not([data-open]))>:where(.sm-list-more){color:var(--fg-muted);display:flex}}:where(.sm-gallery){gap:var(--s-2,8px);grid-template-columns:repeat(auto-fill,minmax(min(100%,var(--sm-tile,140px)),1fr));margin:0;padding:0;list-style:none;display:grid}:where(.sm-gallery)>:where(*){border-radius:var(--r-3,8px);background:var(--bg-inset);margin:0;position:relative;overflow:hidden}:where(.sm-gallery) :where(img,video,canvas){width:100%;aspect-ratio:var(--sm-tile-ratio,1);object-fit:cover;display:block}:where(.sm-gallery) :where(figcaption){font-size:var(--fs-caption,.8125rem);color:var(--fg-muted);padding:6px 8px}:where(.sm-gallery) :where([aria-selected=true],.is-selected){box-shadow:inset 0 0 0 2px var(--fg)}:where(.sm-gallery[data-size=small]){--sm-tile:92px;--sm-tile-ratio:3/2}:where(.sm-status){align-items:center;gap:var(--s-2,8px);min-height:24px;font-size:var(--fs-body-s,.875rem);color:var(--fg-muted);display:inline-flex}:where(.sm-status):empty{display:none}:where(.sm-status[data-state]):before{content:"";background:currentColor;border-radius:50%;flex:none;width:14px;height:14px}:where(.sm-status[data-state=loading]):before{background:0 0;border:2px solid;border-right-color:#0000;animation:.8s linear infinite sm-spin}:where(.sm-status[data-state=success]){color:var(--attention-ok,var(--fg))}:where(.sm-status[data-state=success]):before{background:0 0;border:2px solid;border-width:0 2px 2px 0;border-radius:0;width:6px;height:11px;margin:0 4px 3px;transform:rotate(45deg)}:where(.sm-status[data-state=waiting]){color:var(--attention-warning,var(--signal))}:where(.sm-status[data-state=waiting]):before{width:8px;height:8px;margin:0 3px}:where(.sm-status[data-state=error]){color:var(--attention-error,var(--signal))}:where(.sm-status[data-state=error]):before{clip-path:polygon(50% 0,100% 100%,0 100%);border-radius:0}:where(.sm-progress){border-radius:var(--r-pill,999px);background:var(--bg-elev-2);width:100%;height:4px;accent-color:var(--fg);appearance:none;border:0;overflow:hidden}:where(.sm-progress)::-webkit-progress-bar{background:var(--bg-elev-2)}:where(.sm-progress)::-webkit-progress-value{background:var(--fg)}:where(.sm-progress)::-moz-progress-bar{background:var(--fg)}:where(.sm-more){border-top:1px solid var(--divider)}:where(.sm-more)>:where(summary){align-items:center;gap:var(--s-2,8px);min-height:var(--touch-target,44px);color:var(--fg-muted);font-size:var(--fs-body-s,.875rem);cursor:pointer;padding:0;list-style:none;display:flex}:where(.sm-more)>:where(summary)::-webkit-details-marker{display:none}:where(.sm-more)>:where(summary):after{content:"";width:7px;height:7px;transition:transform var(--dur-fast,.16s) var(--ease-out,ease);border:1.5px solid;border-width:0 1.5px 1.5px 0;margin-inline-start:2px;transform:translateY(-2px)rotate(45deg)}:where(.sm-more[open])>:where(summary):after{transform:translateY(2px)rotate(-135deg)}:where(.sm-more)>:where(summary):hover{color:var(--fg)}:where(.sm-more)>:where(:not(summary)){margin-top:var(--s-2,8px)}:where(.sm-more[open]){padding-bottom:var(--s-3,12px)}:where(.sm-label){position:relative}:where(.sm-hint){vertical-align:middle;display:inline-block}:where(:not(.sm-label))>:where(.sm-hint){position:relative}:where(.sm-hint)>:where(summary){width:var(--touch-target,44px);height:var(--touch-target,44px);min-height:0;color:var(--fg-subtle);cursor:pointer;border:0;place-items:center;margin:-8px;padding:0;list-style:none;display:inline-grid}:where(.sm-hint)>:where(summary)::-webkit-details-marker{display:none}:where(.sm-hint)>:where(summary):before{content:"i";width:15px;height:15px;font:600 10px/1 var(--font-body);border:1.5px solid;border-radius:50%;place-items:center;font-style:normal;display:grid}:where(.sm-hint)>:where(summary):hover,:where(.sm-hint[open])>:where(summary){color:var(--fg)}:where(.sm-hint)>:where(:not(summary)){z-index:5;inset-inline-start:0;border:1px solid var(--border-tok);border-radius:var(--r-3,8px);background:var(--bg-elev-2);width:max-content;max-width:min(280px,100cqi - 32px);color:var(--fg);font-size:var(--fs-body-s,.875rem);box-shadow:var(--shadow-1);margin:4px 0 0;padding:10px 12px;line-height:1.45;position:absolute;top:100%}:where(:not(.sm-label))>:where(.sm-hint[open]){display:block}:where(:not(.sm-label))>:where(.sm-hint)>:where(:not(summary)){width:auto;max-width:60ch;position:static}:where(.sm-glyph){--g:var(--sm-glyph-size,20px);width:var(--g);height:var(--g);color:inherit;vertical-align:middle;flex:none;display:inline-block;position:relative}:where(.sm-glyph):before,:where(.sm-glyph):after{content:"";box-sizing:border-box;position:absolute}:where(.sm-glyph[data-glyph=play]):before{clip-path:polygon(0 0,100% 50%,0 100%);background:currentColor;inset:15% 15% 15% 25%}:where(.sm-glyph[data-glyph=pause]):before{border:solid;border-width:0 calc(var(--g)*.14);inset:18% 25%}:where(.sm-glyph[data-glyph=plus]):before,:where(.sm-glyph[data-glyph=close]):before{background:linear-gradient(currentColor,currentColor) 50%/100% 2px no-repeat,linear-gradient(currentColor,currentColor) 50%/2px 100% no-repeat;inset:15%}:where(.sm-glyph[data-glyph=close]):before{transform:rotate(45deg)}:where(.sm-glyph[data-glyph=check]):before{border:2px solid;border-width:0 2px 2px 0;width:30%;height:58%;top:12%;left:36%;transform:rotate(45deg)}:where(.sm-glyph[data-glyph=chevron]):before{border:2px solid;border-width:0 2px 2px 0;width:40%;height:40%;top:22%;left:30%;transform:rotate(45deg)}:where(.sm-glyph[data-glyph=upload]):before{border:2px solid;border-top:0;border-radius:0 0 3px 3px;height:35%;bottom:10%;left:10%;right:10%}:where(.sm-glyph[data-glyph=upload]):after{clip-path:polygon(50% 0,100% 45%,60% 45%,60% 100%,40% 100%,40% 45%,0 45%);background:currentColor;height:62%;top:4%;left:25%;right:25%}:where(.sm-glyph[data-glyph=image]):before{border:2px solid;border-radius:3px;inset:12%}:where(.sm-glyph[data-glyph=image]):after{clip-path:polygon(0 100%,35% 20%,60% 65%,75% 45%,100% 100%);background:currentColor;height:34%;bottom:24%;left:22%;right:22%}:where(.sm-glyph[data-glyph=video]):before{border:2px solid;border-radius:3px;inset:22% 28% 22% 8%}:where(.sm-glyph[data-glyph=video]):after{clip-path:polygon(0 35%,100% 0,100% 100%,0 65%);background:currentColor;width:20%;top:30%;bottom:30%;right:8%}:where(.sm-glyph[data-glyph=audio]):before{background:linear-gradient(currentColor,currentColor) 0 60%/2px 40% no-repeat,linear-gradient(currentColor,currentColor) 33%/2px 80% no-repeat,linear-gradient(currentColor,currentColor) 66%/2px 100% no-repeat,linear-gradient(currentColor,currentColor) 100% 60%/2px 50% no-repeat;inset:15% 12%}:where(.sm-glyph[data-glyph=text]):before{background:linear-gradient(currentColor,currentColor) 0 0/100% 2px no-repeat,linear-gradient(currentColor,currentColor) 0/100% 2px no-repeat,linear-gradient(currentColor,currentColor) 0 100%/60% 2px no-repeat;inset:20% 12%}:where(.sm-glyph[data-glyph=spark]):before{clip-path:polygon(50% 0,60% 40%,100% 50%,60% 60%,50% 100%,40% 60%,0 50%,40% 40%);background:currentColor;inset:8%}:where(.sm-glyph[data-glyph=info]):before{border:2px solid;border-radius:50%;inset:10%}:where(.sm-glyph[data-glyph=info]):after{background:linear-gradient(currentColor,currentColor) 0 0/2px 2px no-repeat,linear-gradient(currentColor,currentColor) 0 100%/2px 60% no-repeat;width:2px;height:46%;margin-left:-1px;top:28%;left:50%}:where(.sm-glyph[data-glyph=warning]):before{clip-path:polygon(50% 0,100% 100%,0 100%);background:currentColor;inset:8% 4% 10%}:where(.sm-glyph[data-glyph=warning]):after{background:linear-gradient(var(--bg),var(--bg)) 0 0/2px 60% no-repeat,linear-gradient(var(--bg),var(--bg)) 0 100%/2px 2px no-repeat;width:2px;height:38%;margin-left:-1px;top:38%;left:50%}:where(.sm-glyph[data-glyph=spinner]):before{border:2px solid;border-right-color:#0000;border-radius:50%;animation:.8s linear infinite sm-spin;inset:12%}:where([class*=sm-][hidden]){display:none!important}:where(.sm-sr){clip-path:inset(50%);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}@keyframes sm-spin{to{transform:rotate(360deg)}}@media (prefers-reduced-motion:reduce){:where(.sm-status[data-state=loading]):before,:where(.sm-glyph[data-glyph=spinner]):before{opacity:.6;border-right-color:currentColor;animation:none}:where(.sm-more)>:where(summary):after{transition:none}}.sm-quiet{border:1px solid var(--border-tok)}html,body{background:var(--bg);color:var(--fg);margin:0}.dh-page{min-height:100dvh;font-family:var(--font-body);flex-direction:column;display:flex}.dh-header{justify-content:space-between;align-items:flex-end;gap:var(--s-4,16px);padding:var(--s-5,20px) var(--s-6,24px) var(--s-4,16px);border-bottom:var(--bw-1,1px) solid var(--border-tok);flex-wrap:wrap;display:flex}.dh-title{gap:var(--s-1,4px);min-width:0;display:grid}.dh-wordmark{color:var(--fg-muted);font-size:13px}.dh-title h1{font-family:var(--font-display);letter-spacing:var(--tr-display);overflow-wrap:anywhere;margin:0;font-size:30px;font-style:italic;font-weight:200;line-height:1.1}.dh-meta{color:var(--fg-muted);overflow-wrap:anywhere;font-size:12px}.dh-controls{align-items:flex-end;gap:var(--s-3,12px);flex-wrap:wrap;display:flex}.dh-controls label{gap:var(--s-1,4px);color:var(--fg-muted);font-size:12px;display:grid}.dh-controls select{min-height:36px;padding:0 var(--s-3,12px);border:var(--bw-1,1px) solid var(--border-tok);border-radius:var(--r-2);background:var(--bg-elev-1);color:var(--fg);font:inherit;font-size:13px}.dh-controls select:focus-visible,.dh-input textarea:focus-visible,.dh-page .btn:focus-visible{outline:2px solid var(--signal);outline-offset:2px}.dh-problem{margin:var(--s-4,16px) var(--s-6,24px) 0;padding:var(--s-3,12px) var(--s-4,16px);border:var(--bw-1,1px) solid var(--border-strong);border-radius:var(--r-2);background:var(--bg-elev-1);white-space:pre-wrap;overflow-wrap:anywhere}.dh-main{gap:var(--s-6,24px);padding:var(--s-6,24px);flex:1;grid-template-columns:minmax(0,1fr) minmax(280px,380px);display:grid}.dh-stage{min-width:0}.dh-frame{min-width:0;margin:0 auto}.dh-noview{gap:var(--s-4,16px);padding:var(--s-6,24px);border:var(--bw-1,1px) solid var(--border-tok);border-radius:var(--r-3);justify-items:start;display:grid}.dh-noview p{color:var(--fg-muted);margin:0}.dh-panel{gap:var(--s-5,20px);flex-direction:column;min-width:0;display:flex}.dh-section-head{justify-content:space-between;align-items:center;gap:var(--s-2,8px);margin-bottom:var(--s-2,8px);display:flex}.dh-section-head h2{letter-spacing:var(--tr-body);margin:0;font-size:13px;font-weight:600}.dh-input textarea{box-sizing:border-box;width:100%;padding:var(--s-3,12px);border:var(--bw-1,1px) solid var(--border-tok);border-radius:var(--r-2);background:var(--bg-elev-1);color:var(--fg);font-family:var(--font-mono);resize:vertical;font-size:12px;line-height:1.5}.dh-input p,.dh-muted{margin:var(--s-2,8px) 0 0;color:var(--fg-muted);font-size:13px}.dh-json{max-height:240px;padding:var(--s-3,12px);border:var(--bw-1,1px) solid var(--border-tok);border-radius:var(--r-2);background:var(--bg-elev-1);font-family:var(--font-mono);white-space:pre-wrap;overflow-wrap:anywhere;margin:0;font-size:12px;line-height:1.5;overflow:auto}.dh-console{flex-direction:column;min-height:0;display:flex}.dh-console ol{border-top:var(--bw-1,1px) solid var(--divider);max-height:360px;margin:0;padding:0;list-style:none;overflow:auto}.dh-entry{gap:var(--s-2,8px);padding:var(--s-2,8px) 0;border-bottom:var(--bw-1,1px) solid var(--divider);grid-template-columns:auto auto minmax(0,1fr);align-items:baseline;font-size:12px;display:grid}.dh-entry time{color:var(--fg-muted);font-variant-numeric:tabular-nums}.dh-kind{color:var(--fg-muted);font-size:11px}.dh-entry pre{font-family:var(--font-mono);white-space:pre-wrap;overflow-wrap:anywhere;margin:0}.dh-error .dh-kind,.dh-error pre{color:var(--fg);font-weight:600}.dh-stand-in .dh-kind{color:var(--signal)}@media (width<=900px){.dh-main{padding:var(--s-4,16px);grid-template-columns:minmax(0,1fr)}.dh-header{padding:var(--s-4,16px)}}
@@ -1,6 +1,7 @@
1
1
  <!doctype html>
2
2
  <html lang="en" data-mode="light">
3
3
  <head>
4
+ <meta http-equiv="Content-Security-Policy" content="frame-src 'self' blob:" />
4
5
  <meta charset="utf-8">
5
6
  <meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover">
6
7
  <meta name="referrer" content="no-referrer">
@@ -18,6 +18,8 @@ Build and check
18
18
  --live: review each saved version in your StillMade account)
19
19
  types [folder] Write stillmade-env.d.ts: TypeScript types for the ports and globals
20
20
  inspect [folder] Show the manifest, runtime and operations
21
+ permissions [folder] What the Block can do, and what its source uses but does not declare
22
+ (or declares and never uses); --fix corrects stillmade.block.json
21
23
  ready [folder] The readiness check StillMade runs before a release
22
24
  gate [folder] Run ready on every new or edited Block in a folder; a Block is not
23
25
  ready until this passes
@@ -84,6 +86,12 @@ export function formatResult(args,result){
84
86
  if(command==='pack'&&result?.file)return lines(`Packed ${result.file}`,`Digest ${result.digest}`,result.tests!==undefined?`${result.tests} tests passed`:null,notes(result),result.message||'Import it in StillMade: Create, then Import.');
85
87
  if(command==='dev'&&result?.dev)return lines(`Previewing ${result.folder} at ${result.dev}`,result.build?`Running "${result.build}" when a source file changes.`:null,'Saving a file reloads the preview. Hosted calls use stand-ins. Press Ctrl+C to stop.',result.live?'':null,result.live?'Live link: open this address while signed in to StillMade. Each saved version is offered there for the usual import review; runs use your account and paid steps ask first.':null,result.live?` ${result.live}`:null);
86
88
  if(command==='types'&&result?.types)return lines(`Wrote ${result.types}`,'It types StillMade in your view, input in src/run.js, and BlockRun for a module entry. Run types again after changing ports.');
89
+ if(command==='permissions'&&result?.summary){
90
+ const item=entry=>` ${entry.family==='network'?'connects to':'desktop'} ${entry.scope}`;
91
+ return lines(result.fixed?`Updated ${result.file}`:null,`${result.summary.headline}:`,...result.summary.lines.map(line=>` ${line.text}`),
92
+ ...(result.fixed?[result.added.length?['Declared:',...result.added.map(item)]:null,result.removed.length?['Removed:',...result.removed.map(item)]:null].flat().filter(Boolean)
93
+ :[result.usage.undeclared.length?['Used but not declared (StillMade refuses these):',...result.usage.undeclared.map(item)]:null,result.usage.unused.length?['Declared but never used:',...result.usage.unused.map(item)]:null,(result.usage.undeclared.length||result.usage.unused.length)?'Run permissions --fix to correct stillmade.block.json.':null].flat().filter(Boolean)));
94
+ }
87
95
  if(command==='remix'&&result?.file)return lines(`Remix created: ${result.file}`,result.message);
88
96
  return `${pretty(result)}\n`;
89
97
  }
@@ -135,7 +135,15 @@ export const UNDISCOVERABLE_SDK_BLOCKS=new Set(['sm.connect-app','sm.photo-notes
135
135
  // Writer and Voiceover are Narration), a second Chat, tools that belong inside their app (the
136
136
  // assistants, the Editor's AI edits, references, compare, image editor, scoring) and thin results.
137
137
  // Every release stays in BUILTIN_BLOCKS: saved projects and built-in Types keep their pins and run.
138
- export const HIDDEN_BLOCKS=new Set(['sm.script','sm.voiceover','sm.chat','sm.thumbnail','sm.shot-plan-assistant','sm.blueprint-assistant','sm.animated-scene-assistant','sm.whiteboard-assistant','sm.final-edit','sm.edit-assistant','sm.character','sm.location','sm.style','sm.compare','sm.image-editor','sm.score','sm.research','sm.quality-audit','sm.rough-cut']);
138
+ // October 22, 2026 (owner: "those aren't blocks, those are things that can happen inside a block"):
139
+ // exporting, captions, transitions, color and motion graphics are the Editor's own tools and built-in
140
+ // calls any Block makes (media.transform timeline.render), never Blocks of their own.
141
+ export const BLOCK_TOOLS=new Set(['sm.export-video','sm.export-audio','sm.export-timeline','sm.captions','sm.transition','sm.grade','sm.motion-graphics']);
142
+ // The Automation (When 1.1.0) starts from an app's events or checks it every few minutes, handing each new item on
143
+ // once, and its Only if lets through only the items wanted (October 22, 2026, owner-approved mockup): finding new
144
+ // items and gating on a decision are no longer Blocks of their own. Saved projects keep running them.
145
+ export const AUTOMATION_PARTS=new Set(['sm.new-items','sm.continue-if']);
146
+ export const HIDDEN_BLOCKS=new Set(['sm.script','sm.voiceover','sm.chat','sm.thumbnail','sm.shot-plan-assistant','sm.blueprint-assistant','sm.animated-scene-assistant','sm.whiteboard-assistant','sm.final-edit','sm.edit-assistant','sm.character','sm.location','sm.style','sm.compare','sm.image-editor','sm.score','sm.research','sm.quality-audit','sm.rough-cut',...BLOCK_TOOLS,...AUTOMATION_PARTS]);
139
147
  // Exact releases to hide (id@version) when one release of a shown Block should not be offered; none today.
140
148
  export const HIDDEN_RELEASES=new Set([]);
141
149
  export const isHiddenBlock=block=>HIDDEN_BLOCKS.has(block.manifest.id)||HIDDEN_RELEASES.has(`${block.manifest.id}@${block.manifest.version}`);
@@ -154,6 +162,14 @@ export const DISCOVERABLE_BLOCKS = [STARTING_CHAT_BLOCK,...WORKSPACE_BLOCKS,...A
154
162
  // builds then fold it into the Editor (compact-project.js), so people still get
155
163
  // the Editor at its Export.
156
164
  const newestRelease=id=>SDK_BUILTIN_BLOCKS.filter(block=>block.manifest.id===id).reduce((best,block)=>!best||newer(block.manifest.version,best.manifest.version)?block:best,null);
165
+ // What people browse (Add Block, the Marketplace): one card per Block. The Editor is the full video
166
+ // editor (its host release); its portable releases stay discoverable for planning only (October 22, 2026:
167
+ // Add Block listed Video editor, Editor 2.2.11 and Editor 2.2.12).
168
+ export const LISTED_BLOCKS=Object.freeze([...DISCOVERABLE_BLOCKS.reduce((map,block)=>{
169
+ const id=block.manifest.id,kept=map.get(id);
170
+ if(!kept||(MAIN_HOST_WORKSPACES.has(id)?block.manifest.runtime==='host'&&kept.manifest.runtime!=='host':kept.manifest.runtime!=='host'&&newer(block.manifest.version,kept.manifest.version)))map.set(id,block);
171
+ return map;
172
+ },new Map()).values()]);
157
173
  export const PLANNING_BLOCKS = DISCOVERABLE_BLOCKS.map(block=>DISCOVERED_AS_HOST.get(block.manifest.id)===block?newestRelease(block.manifest.id)||block:block);
158
174
  export const BUILTIN_BLOCKS = [STARTING_CHAT_BLOCK,...WORKSPACE_BLOCKS,...HISTORICAL_WORKSPACE_BLOCKS,DESCRIBE_MEDIA_HOST_1_1,YOUTUBE_HOST_1_1,...GENERATION_HOST_RELEASES,...SDK_BUILTIN_BLOCKS.filter(block=>!WORKSPACE_BLOCKS.some(current=>current.manifest.id===block.manifest.id&&current.manifest.version===block.manifest.version)),...LEGACY_BLOCKS.filter(block=>!WORKSPACE_BLOCKS.some(current=>current.manifest.id===block.manifest.id&&current.manifest.version===block.manifest.version))];
159
175
  const flows={guided:['voiceover','shot-plan','blueprint','pipeline','editor'],blank:['pipeline','editor'],imagePanel:['voiceover','panel-strip','editor'],whiteboard:['voiceover','board-editor','editor'],animated:['voiceover','shot-plan','animated-scene','editor']};
@@ -40,6 +40,11 @@ const unattendedConnection=pkg=>pkg?.manifest?.runtime==='capability'&&(pkg.capa
40
40
  const textGeneration=pkg=>pkg?.manifest?.runtime==='capability'&&pkg.capability?.operation==='text.generate';
41
41
  const imageGeneration=pkg=>pkg?.manifest?.runtime==='capability'&&pkg.capability?.operation==='image.generate';
42
42
  const unattendedStep=(pkg,plan)=>unattendedConnection(pkg)||plan?.unattendedText===true&&textGeneration(pkg)||plan?.unattendedImage===true&&imageGeneration(pkg);
43
+ // A module Step an automation may hand to the owner's StillMade Desktop: it runs
44
+ // there unattended, so it may not need a person's approval (hosted operations,
45
+ // generated media) or a device grant. Others still wait for the owner.
46
+ const desktopModuleStep=pkg=>pkg?.manifest?.runtime==='module'&&!(pkg.manifest.permissions?.capabilities||[]).length&&!(pkg.manifest.permissions?.actions||[]).includes('media.generate')&&!(pkg.manifest.permissions?.desktop||[]).length;
47
+ export const isDesktopModuleStep=desktopModuleStep;
43
48
  const hasInteractiveRequirements=pkg=>Object.values(pkg?.manifest.inputs || {}).some(port=>port.semantic && !port.context && (!port.sources || port.sources.includes('user') || port.sources.includes('chat')));
44
49
  const edgeKey=edge=>stableStringify({source:edge.source,target:edge.target,sourceHandle:edge.sourceHandle,targetHandle:edge.targetHandle,adapter:edge.data?.stillmadeAdapter ?? null});
45
50
  function frozen(value){if(value && typeof value==='object'){Object.freeze(value);for(const child of Object.values(value))frozen(child);}return value;}
@@ -53,12 +58,13 @@ function localDependencyOrder(placements,type) {
53
58
  }
54
59
 
55
60
  /** Capture the production definition and current placement settings, never fixtures. */
56
- export async function planSegment(projectType,canvas,startStepId,{interactive=false,background=false,unattended=false,unattendedText=false,unattendedImage=false,endStepId,includeIndependent=false,reuseCurrentSources=false,batchUpdate=false,hostedUpdate=false,experimentalBrowser,experimentalBrowserValidation,applicationOperations={}}={}) {
61
+ export async function planSegment(projectType,canvas,startStepId,{interactive=false,background=false,unattended=false,unattendedText=false,unattendedImage=false,desktopModules=false,endStepId,includeIndependent=false,reuseCurrentSources=false,batchUpdate=false,hostedUpdate=false,experimentalBrowser,experimentalBrowserValidation,applicationOperations={}}={}) {
57
62
  if(experimentalBrowser!==undefined)assertExperimentalBrowserExecutionAuthority(experimentalBrowser);
58
63
  assert(!(interactive&&background),'SEGMENT_APPROVAL','Choose interactive or background execution');
59
64
  assert(typeof unattended==='boolean'&&(!unattended||background&&!interactive&&!includeIndependent&&!reuseCurrentSources),'SEGMENT_APPROVAL','Unattended actions require the explicit background connection host.');
60
65
  assert(typeof unattendedText==='boolean'&&(!unattendedText||unattended),'SEGMENT_APPROVAL','Unattended text generation requires an unattended background host.');
61
66
  assert(typeof unattendedImage==='boolean'&&(!unattendedImage||unattended),'SEGMENT_APPROVAL','Unattended image generation requires an unattended background host.');
67
+ assert(typeof desktopModules==='boolean'&&(!desktopModules||background&&!interactive&&!includeIndependent&&!reuseCurrentSources),'SEGMENT_APPROVAL','Module Steps run on StillMade Desktop only in an automatic background plan.');
62
68
  assert(typeof includeIndependent==='boolean' && (!includeIndependent || interactive && !background),'SEGMENT_APPROVAL','Independent Steps require an explicit interactive workflow run');
63
69
  assert(typeof reuseCurrentSources==='boolean' && (!reuseCurrentSources || interactive && !background && !includeIndependent && endStepId===startStepId),'SEGMENT_APPROVAL','Reusing current sources requires an interactive single-Step update.');
64
70
  assert(typeof batchUpdate==='boolean' && (!batchUpdate || reuseCurrentSources),'SEGMENT_APPROVAL','A batch update needs an explicit current-source update plan.');
@@ -94,7 +100,7 @@ export async function planSegment(projectType,canvas,startStepId,{interactive=fa
94
100
  const applicationOperation=isBrowserApplication(pkg)?experimentalBrowserOperation(experimentalBrowser,pkg,operations[placement.id]):undefined;
95
101
  if(requiresDesktopExecution(pkg)&&(!interactive||background||includeIndependent)){nextStageId=placement.id;stopReason='runtime-approval';break;}
96
102
  if(pkg?.manifest.state&&!interactive&&!background){nextStageId=placement.id;stopReason='runtime-approval';break;}
97
- if(!pkg || !(runtimes.has(pkg.manifest.runtime)||unattended&&(unattendedConnection(pkg)||unattendedText&&textGeneration(pkg)||unattendedImage&&imageGeneration(pkg))||isBrowserApplication(pkg)&&interactive||interactive&&!includeIndependent&&(!reuseCurrentSources||hostedUpdate&&pkg.manifest.runtime==='capability')&&remoteRuntime(pkg.manifest.runtime))){nextStageId=placement.id;stopReason=pkg?'runtime-approval':'host-workspace';break;}
103
+ if(!pkg || !(runtimes.has(pkg.manifest.runtime)||unattended&&(unattendedConnection(pkg)||unattendedText&&textGeneration(pkg)||unattendedImage&&imageGeneration(pkg))||desktopModules&&desktopModuleStep(pkg)&&!placement.failurePolicy||isBrowserApplication(pkg)&&interactive||interactive&&!includeIndependent&&(!reuseCurrentSources||hostedUpdate&&pkg.manifest.runtime==='capability')&&remoteRuntime(pkg.manifest.runtime))){nextStageId=placement.id;stopReason=pkg?'runtime-approval':'host-workspace';break;}
98
104
  const incoming=await Promise.all((type.connections || []).filter(edge=>edge.to.stage===placement.id).map(async edge=>{
99
105
  // External converted values arrive raw. Pin the producing contract so the
100
106
  // executor can check its type/role without trusting a caller's descriptor.
@@ -136,7 +142,7 @@ export async function planSegment(projectType,canvas,startStepId,{interactive=fa
136
142
  if(endStepId!==undefined && stopReason==='complete' && active[end+1]){nextStageId=active[end+1].id;stopReason='selection-boundary';}
137
143
  const semanticManifests=active.map(stage=>packageForStage(type,stage,BUILTIN_BLOCKS)?.manifest).filter(manifest=>manifest?.semantics?.length).map(manifest=>({id:manifest.id,version:manifest.version,semantics:structuredClone(manifest.semantics)}));
138
144
  // A whole-job run keeps `interactive`, so re-planning it before each part covers the same job.
139
- const plan={schemaVersion:1,workflowDigest:compiled.digest,startStepId,steps,nextStageId,stopReason,...(includeIndependent?{includeIndependent:true}:{}),...(unattended?{unattended:true}:{}),...(unattendedText?{unattendedText:true}:{}),...(unattendedImage?{unattendedImage:true}:{}),...(background&&steps.some(step=>step.package.manifest.state)?{background:true}:{}),...(interactive&&(jobParts.length||includeIndependent||steps.some(step=>isBrowserApplication(step.package)||requiresDesktopExecution(step.package)||remoteRuntime(step.package.manifest.runtime)||step.package.manifest.state||hasInteractiveRequirements(step.package)||hasInteractiveRequirements(step.fallbackPackage)))?{interactive:true}:{})};
145
+ const plan={schemaVersion:1,workflowDigest:compiled.digest,startStepId,steps,nextStageId,stopReason,...(includeIndependent?{includeIndependent:true}:{}),...(unattended?{unattended:true}:{}),...(unattendedText?{unattendedText:true}:{}),...(unattendedImage?{unattendedImage:true}:{}),...(desktopModules&&steps.some(step=>step.package.manifest.runtime==='module')?{desktopModules:true}:{}),...(background&&steps.some(step=>step.package.manifest.state)?{background:true}:{}),...(interactive&&(jobParts.length||includeIndependent||steps.some(step=>isBrowserApplication(step.package)||requiresDesktopExecution(step.package)||remoteRuntime(step.package.manifest.runtime)||step.package.manifest.state||hasInteractiveRequirements(step.package)||hasInteractiveRequirements(step.fallbackPackage)))?{interactive:true}:{})};
140
146
  if(semanticManifests.length)plan.semanticManifests=semanticManifests;
141
147
  if(type.projectInputs?.length){plan.projectInputs=structuredClone(type.projectInputs);plan.projectTypeIdentity={id:type.id,version:type.version};}
142
148
  if(reuseCurrentSources){plan.reuseCurrentSources=true;plan.interactive=true;}
@@ -228,7 +234,7 @@ async function runSegmentInternal(suppliedPlan,{runId=crypto.randomUUID(),source
228
234
  await fence(step);
229
235
  const pkg=step.package,m=pkg.manifest;
230
236
  const startingState=m.state?validateBlockState(m,memory[step.id]??step.state):undefined,stateSource=memorySources[step.id]??step.stateSource;
231
- assert((runtimes.has(m.runtime)||unattendedHost&&unattendedStep(pkg,plan)||interactive&&plan.interactive&&(remoteRuntime(m.runtime)||isBrowserApplication(pkg))) && await wait(digest(pkg))===step.packageDigest,'SEGMENT_PIN','The Step package pin changed');
237
+ assert((runtimes.has(m.runtime)||unattendedHost&&unattendedStep(pkg,plan)||plan.desktopModules===true&&background===true&&!interactive&&desktopModuleStep(pkg)||interactive&&plan.interactive&&(remoteRuntime(m.runtime)||isBrowserApplication(pkg))) && await wait(digest(pkg))===step.packageDigest,'SEGMENT_PIN','The Step package pin changed');
232
238
  if(step.failurePolicy?.action==='fallback_block'){validateFailurePolicy(pinnedFailurePolicy(step.failurePolicy),m,[step.fallbackPackage]);assert(await wait(digest(step.fallbackPackage))===step.fallbackPackageDigest,'SEGMENT_PIN','The fallback Block package pin changed');}
233
239
  else assert(step.fallbackPackage===undefined&&step.fallbackPackageDigest===undefined,'SEGMENT_PIN','Unexpected fallback package');
234
240
  const applicationOptions=isBrowserApplication(pkg)?{operation:step.applicationOperation,stepId:step.id,runId,planDigest:plan.planDigest,workflowDigest:plan.workflowDigest,signal:controller.signal,check:()=>fence(step)}:null;
@@ -28,6 +28,9 @@ export function automaticPort(manifest,direction) {
28
28
  }
29
29
  function manifestFor(type,step,catalog) {const pkg=packageForStage(type,step,catalog);return pkg?.manifest || pkg;}
30
30
  function sequenceStep(manifest) {return manifest.kind!=='workspace'&&(manifest.runtime!=='host'||!!automaticPort(manifest,'inputs')||!!automaticPort(manifest,'outputs'));}
31
+ // What a person gives a Step on its own page (October 22, 2026: Verse finder takes a sermon recording, so
32
+ // adding it after an Automation, which hands on an email or lead, was refused as "do not fit here").
33
+ const MEDIA=new Set(['image','video','audio','asset','image[]','video[]','audio[]','asset[]']),GIVEN_ON_ITS_PAGE=new Set([...MEDIA,'text','script']);
31
34
  function connection(type,from,to,catalog) {
32
35
  const a=manifestFor(type,from,catalog),b=manifestFor(type,to,catalog);
33
36
  assert(a&&b,'MISSING_BLOCK','A pinned Block is missing from this workflow');
@@ -38,6 +41,9 @@ function connection(type,from,to,catalog) {
38
41
  if(!Object.keys(a.outputs).length||!Object.keys(b.inputs).length)return null;
39
42
  assert(out&&input,'PRIMARY_PORT',`${!out?a.name:b.name} needs a declared primary port before automatic insertion.`);
40
43
  const match=connectionCompatibility(out,input);
44
+ // The Step before hands on no media (an Automation's email, AI write's words) and the new Step takes what the
45
+ // person gives it directly: it starts unconnected. A media chain still refuses a Step that breaks it.
46
+ if(!match.compatible&&GIVEN_ON_ITS_PAGE.has(input.type)&&!MEDIA.has(out.type))return null;
41
47
  assert(match.compatible,'STEP_INCOMPATIBLE',`${a.name} and ${b.name} do not fit here: ${match.reason}`);
42
48
  validateConnectionAdapter(out,input,{permissions:b.permissions?.project || [],...(match.adapter?{adapter:match.adapter}:{})});
43
49
  return {from:{stage:from.id,port:out.name},to:{stage:to.id,port:input.name},...(match.adapter?{adapter:match.adapter}:{})};
@@ -124,13 +124,20 @@ export function scanAdmissionPackage(pkg){
124
124
  if(requiresDesktopExecution(pkg))results.push({name:'Native desktop execution contract',passed:true,runtimeVerified:false,execution:'not-run',reviewRequired:true,contract:'desktop-execution/1',message:pkg.desktop?'Inputs and outputs map to an approved native action. Source checks and passthrough fixtures do not prove native execution; a trusted desktop executor and bound receipt are required.':'This legacy package declares a native tool without an execution mapping. Its bytes remain unchanged, but generic JavaScript/recipe execution is unavailable; use its reviewed interactive flow or a new mapped release.'});
125
125
  return {...scan,...summary(results)};
126
126
  }
127
- export async function admitPackage(pkg,{test=testPackage,run,sampleInput,platformProbe,desktop}={}) {
128
- safeDocument(pkg);const scan=scanAdmissionPackage(pkg);if(scan.results.some(row=>!row.passed && !['Appearance compatibility','Task interface design'].includes(row.name)))return {...scan,digest:await digest(pkg)};
127
+ // onProgress hears each stage as it ends ({stage:'read'|'tests'|'sample', state}) and each fixture test (onTest
128
+ // events with stage:'tests'), so a review can show the real progress; the returned report stays the only verdict.
129
+ export async function admitPackage(pkg,{test=testPackage,run,sampleInput,platformProbe,desktop,onProgress}={}) {
130
+ const tell=event=>{try{onProgress?.(event);}catch{}},onTest=event=>tell({stage:'tests',...event});
131
+ safeDocument(pkg);const scan=scanAdmissionPackage(pkg),scanFailed=scan.results.some(row=>!row.passed && !['Appearance compatibility','Task interface design'].includes(row.name));
132
+ tell({stage:'read',state:scanFailed?'failed':'done',count:scan.results.length});
133
+ if(scanFailed)return {...scan,digest:await digest(pkg)};
129
134
  const native=requiresDesktopExecution(pkg);
130
135
  if(native&&!desktop){const sourceDigest=await digest(pkg),platformChecks=await testPlatforms(pkg,{runtimePassed:false,sourceDigest,runtimeMessage:'Native desktop execution has not run. An approved desktop executor must run the actual native action.'});return {...scan,digest:sourceDigest,platformChecks,execution:'not-run',liveVerified:false,reviewRequired:true,tests:0,fixtureContracts:pkg.tests.length};}
131
- const tested=await (native?testPackage(pkg,{desktop}):test(pkg));const results=[...scan.results,...tested.results.map(result=>({...result,name:`${native?'Desktop host':'Sandbox'} contract test: ${result.name}`}))];
136
+ tell({stage:'tests',state:'running',total:pkg.tests.length});
137
+ const tested=await (native?testPackage(pkg,{desktop,onTest}):test(pkg,{onTest}));
138
+ tell({stage:'tests',state:tested.passed?'done':'failed'});const results=[...scan.results,...tested.results.map(result=>({...result,name:`${native?'Desktop host':'Sandbox'} contract test: ${result.name}`}))];
132
139
  let preview;
133
- if(tested.passed && (run||desktop)){try{preview=native?await runPackageAsync(pkg,sampleInput || pkg.tests[0].input,{desktop}):await run(pkg,sampleInput || pkg.tests[0].input,pkg.manifest.state?{state:initialBlockState(pkg.manifest)}:{});if(pkg.manifest.state)validateBlockState(pkg.manifest,preview.state);results.push({name:'Sample execution and input/output validation',passed:true});}catch(error){results.push({name:'Sample execution and input/output validation',passed:false,message:error.message});}}
140
+ if(tested.passed && (run||desktop)){tell({stage:'sample',state:'running'});try{preview=native?await runPackageAsync(pkg,sampleInput || pkg.tests[0].input,{desktop}):await run(pkg,sampleInput || pkg.tests[0].input,pkg.manifest.state?{state:initialBlockState(pkg.manifest)}:{});if(pkg.manifest.state)validateBlockState(pkg.manifest,preview.state);results.push({name:'Sample execution and input/output validation',passed:true});tell({stage:'sample',state:'done',outputs:preview.outputs});}catch(error){results.push({name:'Sample execution and input/output validation',passed:false,message:error.message});tell({stage:'sample',state:'failed',message:error.message});}}
134
141
  const sourceDigest=await digest(pkg);
135
142
  const platformChecks=await testPlatforms(pkg,{probe:platformProbe,runtimePassed:tested.passed===true && results.every(result=>result.passed),sourceDigest});
136
143
  return {...summary(results),digest:sourceDigest,platformChecks,...(preview ? {preview} : {}),...(native?{execution:'desktop-host-executor',liveVerified:false,reviewRequired:true}:pkg.manifest.runtime==='capability'?{liveVerified:false,reviewRequired:true}:{})};
@@ -9,7 +9,7 @@
9
9
  import {HOSTED_OPERATION_NAMES} from './contracts.js';
10
10
 
11
11
  export const BUILDER_CONTRACT_ID='stillmade.block-builder';
12
- export const BUILDER_CONTRACT_VERSION='1.7.1';
12
+ export const BUILDER_CONTRACT_VERSION='1.10.0';
13
13
  export const BUILDER_CONTRACT_STAMP=`${BUILDER_CONTRACT_ID}@${BUILDER_CONTRACT_VERSION}`;
14
14
  export const BUILDER_CONTRACT_DOCS_PATH='/docs/build/block-builder-contract';
15
15
  // Block names appear in step bars, cards and pickers; new releases keep them short.
@@ -40,9 +40,14 @@ const SECTIONS=Object.freeze([
40
40
  rule('block-tier','both',
41
41
  'Three sizes: an app a person works in, a job that makes one deliverable from the hosted operations it needs (what Chat picks), an ingredient (one hosted operation) that only a job is made of.',
42
42
  'Blocks come in three sizes and the manifest decides which (packages/block-sdk/block-tier.js): an app is a workspace a person works in (Editor, Canvas, Shots), placed in a Type and never wired automatically; a job is one deliverable a person would name (the narration, the captioned clip), built from the hosted operations it needs (`permissions.capabilities`) and reviewed in one place, the unit Chat picks for a Step; an ingredient is one hosted operation with nothing of its own (a declarative capability Block), what a job is made of. When two ingredients would stand in a row to make one deliverable (writing the words, then voicing them), build or pick the job that does both and keep the review point inside it. A job declares a second output when another Block consumes the intermediate (the script beside the voice); split into two jobs only when the intermediate has a life of its own or the halves have different owners or payment.'),
43
+ // Docs only (no compact line): StillMade's own builder prompts keep their runtime rules (October 22, 2026:
44
+ // a coding agent given a missing Block's brief took the GitHub-adaptation guide's runtime list as a ban
45
+ // on module Blocks and refused a job that needs one).
46
+ rule('runtime-choice','block',null,
47
+ 'Pick the runtime from the job. No hosted AI: a recipe or isolated `javascript`, both able to run unattended. One text, script or brief prompt returning text, script or JSON: a `capability` Block. Hosted operations on other values (a transcript, media, structured data), several hosted operations in a row, or a result shaped in code: one `module` Block, `module/main.js` calling `stillmade.hosted.run(operation, request)` for each operation in `permissions.capabilities` (MODULES.md), never one Block per operation; it runs in the person\'s browser while StillMade is open. Build a new Block in a folder with `stillmade-block create`, `test`, `ready` and `pack`, and import the file `pack` writes (a module Block writes `<id>-<version>.stillmade-module.json`) through Create, then Import Block. A `.stillmade-block` archive is only for adapting a GitHub repository.'),
43
48
  rule('block-scope','both',
44
49
  'One Step per job: start one only where a person works with the result, where finding new items turns into handling each, or where the result leaves StillMade; all else is a part.',
45
- 'A Step is one job: the work between two moments where a person or the outside world must see the result. Start a new Step only at one of three points; everything else is a part of the neighboring Step.\n\n1. **A person works with the result**: reads, edits, picks or approves it (a workspace Block such as a script, a Canvas or an Editor, or an approval). That is why Chat, Bible, Canvas and Editor are four Steps.\n2. **Finding new things turns into handling each one**: the trigger (a schedule or an app event), the app read, its filter and "only new" are the first job; what is done with each item is the next job. A trigger is a setting of the first job, never a Step of its own.\n3. **The result leaves StillMade**: it is sent, posted, published, paid or written into another app. Consecutive writes to the same app, with the glue that shapes data for them, are one job.\n\nIn a production the Steps are the workspaces a person opens: work feeding the next workspace is its part, else it joins the one before; neighbouring workspaces with one main result type are one Step unless another part feeds the second. A video ends in the Editor (1.0), which has captions and export built in, so Export video is never a Step beside it.\n\nA part that only runs in the desktop app cannot share a Step with cloud parts. Before adding a Step, name which of the three reasons makes it separate; if none applies, make it a part of a neighbor. A Step nobody would ever open must not exist on its own. Prefer an app task\'s own filter (a Gmail query such as `has:attachment filename:png`) to an AI check, and use AI decide only for judgement.\n\nInside one job, each capability stays its own part: a capability Block performs exactly one hosted operation and a connector one app task, so every part keeps its own permission, approval and credit rules, and StillMade groups the parts of one job into one Step (`groups` in the Project Type). Never invent a combined runtime. When one Block can do the whole job with documented primitives (a JavaScript Block doing several local transforms, a workspace owning its whole document), build it as one Block.\n\nExamples: "every new email with an image becomes a generated image" is two Steps, "Gmail · New emails with images" (When, fetch with an image filter, only new) and "Image from email" (AI write, Generate image); listing When, Fetch, Only new, AI decide, Continue only if, AI write and Generate image as seven Steps splits one job into its parts. "A new Meta lead goes to HubSpot" is three Steps: "Meta Ads · New leads", "Qualify lead" and "HubSpot · Add contact".'),
50
+ 'A Step is one job: the work between two moments where a person or the outside world must see the result. Start a new Step only at one of three points; everything else is a part of the neighboring Step.\n\n1. **A person works with the result**: reads, edits, picks or approves it (a workspace Block such as a script, a Canvas or an Editor, or an approval). That is why Chat, Bible, Canvas and Editor are four Steps.\n2. **Finding new things turns into handling each one**: the trigger (a schedule or an app event), the app read, its filter and "only new" are the first job; what is done with each item is the next job. A trigger is a setting of the first job, never a Step of its own.\n3. **The result leaves StillMade**: it is sent, posted, published, paid or written into another app. Consecutive writes to the same app, with the glue that shapes data for them, are one job.\n\nIn a production the Steps are the workspaces a person opens: work feeding the next workspace is its part, else it joins the one before; neighbouring workspaces with one main result type are one Step unless another part feeds the second. A video ends in the Editor (1.0), which has captions and export built in, so Export video is never a Step beside it.\n\nA part that only runs in the desktop app cannot share a Step with cloud parts. Before adding a Step, name which of the three reasons makes it separate; if none applies, make it a part of a neighbor. A Step nobody would ever open must not exist on its own. Prefer an app task\'s own filter (a Gmail query such as `has:attachment filename:png`) to an AI check, and use AI decide only for judgement.\n\nBuild one Block for a job one Block can do: a module Block calls every hosted operation the job needs (it transcribes a sermon, then finds the verses said in it), a JavaScript Block does several local transforms, a workspace owns its whole document. A Block being built never takes an ingredient\'s result only so the ingredient can be its own part or Step (Transcribe in front of a verse finder): it calls that operation itself. Parts in one Step (`groups` in the Project Type) are for jobs made of Blocks that already exist and app tasks, each keeping its own permission, approval and credit rules. Never invent a combined runtime.\n\nExamples: "every new email with an image becomes a generated image" is two Steps, "Gmail · New emails with images" (When, fetch with an image filter, only new) and "Image from email" (AI write, Generate image); listing When, Fetch, Only new, AI decide, Continue only if, AI write and Generate image as seven Steps splits one job into its parts. "A new Meta lead goes to HubSpot" is three Steps: "Meta Ads · New leads", "Qualify lead" and "HubSpot · Add contact".'),
46
51
  rule('capability-contract','block',
47
52
  'Define the contract first: typed inputs (optional with safe defaults where possible), outputs, state, permissions, UI, failures and cancellation. Keep the interface small; keep the job whole.',
48
53
  'Translate the request into an explicit capability contract before implementation: what the Block does; each input with its type, meaning and whether it is required (prefer optional settings with safe defaults over extra required questions); each output with its type and meaning; persistent state; project context it reads; permissions; UI; long-running behavior; expected failures; cancellation; determinism; and whether it needs hosted models, media, desktop tools or shared editing. Keep the public interface as small and general as possible (few required inputs, one primary output) and keep implementation details out of it; small means a small interface, never a fragment of a job. A Block that generates images, video or speech declares only its prompt and output: every Step that runs it gets the host’s generation settings (model, quality, length and resolution, voice, speed, how many), saved with the Step, changed on its page or by Chat, and shown with their cost before each run.'),
@@ -69,13 +74,13 @@ const SECTIONS=Object.freeze([
69
74
  'Projects are multiplayer. A custom view subscribes through `StillMade.onShared(callback)` and publishes every editable field: give static controls a unique `id` and `data-stillmade-share="field"` (the host owns their synchronization), use `StillMade.bindShared(field, elementId)` for controls created dynamically, and `StillMade.updateShared(values, before, options)` for structured values; private variables and DOM values are not shared. Mark a truly device-only control (playback, hover, device selection) with `data-stillmade-local`. Observe shared outputs as well as state, honor `canEdit` in every mutating callback (read-only participants cannot write), never run, click or call a paid integration in response to a remote update, and preserve conflicting drafts. Shared background work uses `sharedWork` with the `work.shared` permission.'),
70
75
  rule('external-services','block',
71
76
  'External services only via hosted operations or `connection.execute`; the host owns providers, keys, BYOK, credits and consent.',
72
- 'Reach external services only through StillMade\'s hosted operations, the connections service (`connection.execute`), or, in a module Block, `stillmade.net.fetch` to origins declared in `permissions.network` (with the key each needs) and `stillmade.connections.call` to adapters or catalog apps in `permissions.connections`; never put keys in code, inputs or headers: StillMade adds each person\'s own key on its servers. The host owns the model and provider choice, BYOK keys, StillMade credits, spending consent, authorization, token refresh, rate limits, retries and provider errors; a Block never receives a secret and never names a provider unless it must. Treat provider unavailability, timeout, cancellation and partial failure as typed outcomes the Block reports, not crashes. Generated images, video, voices, music and sound effects come from hosted operations or the `media.generate` action, paid with the user\'s StillMade credits at StillMade\'s prices; for structured model output declare a `json` output with `capability.schema` instead of parsing prose. When a Block needs npm libraries, WebAssembly or heavy compute, use `runtime: "module"` and reach hosted operations through `stillmade.hosted.run` with the operations listed in `permissions.capabilities`. Decode, cut, join, re-encode, extract or normalise audio and video with `stillmade.media.transform` on the device; never ask for a server render. For a rich interactive interface (React or another framework, SVG, canvas, drag), use a frame view: `src/view.config.json` with `{"runtime":"frame"}` and `src/view.js` bundled as one IIFE, colored with StillMade\'s theme variables. For files and structured data use the `file`, `document`, `table`, `url`, `date` and `color` types and their generated controls and presentations, not JSON text.'),
77
+ 'Reach external services only through StillMade\'s hosted operations, the connections service (`connection.execute`), or, in a module Block, `stillmade.net.fetch` to origins declared in `permissions.network` (with the key each needs), `stillmade.net.connect` for a live WebSocket to one of them, and `stillmade.connections.call` to adapters or catalog apps in `permissions.connections`; never put keys in code, inputs or headers: StillMade adds each person\'s own key on its servers. The host owns the model and provider choice, BYOK keys, StillMade credits, spending consent, authorization, token refresh, rate limits, retries and provider errors; a Block never receives a secret and never names a provider unless it must. Treat provider unavailability, timeout, cancellation and partial failure as typed outcomes the Block reports, not crashes. Generated images, video, voices, music and sound effects come from hosted operations or the `media.generate` action, paid with the user\'s StillMade credits at StillMade\'s prices; for structured model output declare a `json` output with `capability.schema` instead of parsing prose. When a Block needs npm libraries, WebAssembly (threaded: `"threads": true` in module.json) or heavy compute, use `runtime: "module"` and reach hosted operations through `stillmade.hosted.run` with the operations listed in `permissions.capabilities`. Decode, cut, join, re-encode, extract or normalise audio and video with `stillmade.media.transform` on the device; never ask for a server render. For a rich interactive interface (React or another framework, SVG, canvas, drag), use a frame view: `src/view.config.json` with `{"runtime":"frame"}` and `src/view.js` bundled as one IIFE, colored with StillMade\'s theme variables. For files and structured data use the `file`, `document`, `table`, `url`, `date` and `color` types and their generated controls and presentations, not JSON text.'),
73
78
  rule('dependencies','block',
74
79
  'No npm or system dependencies; inline small licensed code and record its license and notice in the manifest.',
75
80
  'A Block must declare everything it requires and must not assume anything is installed. Portable packages carry no package dependencies (`portable.dependencies` is empty): inline small, license-compatible code and record its license and notice in `manifest.license` and `manifest.provenance`. Desktop-only tools are reached solely through declared desktop permissions and the host desktop bridge. If a dependency cannot be delivered through the Block runtime, report the incompatibility instead of assuming the user\'s machine has it.'),
76
81
  rule('security','both',
77
82
  'Treat inputs, files and responses as untrusted; request minimum permissions; never leak secrets in outputs, state, errors or UI.',
78
- 'Treat Block code, external data, files, repositories, model output and network responses as untrusted. Request the minimum permissions and capabilities. Do not weaken the sandbox (QuickJS has no fetch, DOM, timers, modules or Node APIs). Validate and bound every value crossing a boundary. Never expose secrets through logs, outputs, project context, UI, error messages or serialized state.'),
83
+ 'Treat Block code, external data, files, repositories, model output and network responses as untrusted. Request the minimum permissions and capabilities; `stillmade-block permissions --fix` declares exactly what the source uses. Do not weaken the sandbox (QuickJS has no fetch, DOM, timers, modules or Node APIs). Validate and bound every value crossing a boundary. Never expose secrets through logs, outputs, project context, UI, error messages or serialized state.'),
79
84
  rule('lifecycle','block',
80
85
  'Validate inputs early, return only declared outputs, run long work as a job with monotonic progress, honor cancel and timeout, keep state in `manifest.state` with migrations.',
81
86
  'A successful run means more than a returned value. Validate inputs before work; return only declared output ports with values matching their types; run long work as a cooperative job (`job.pending` with monotonic progress) instead of blocking; honor cancellation and the host timeout; keep persistent state in `manifest.state` (module Blocks: `stillmade.storage`) with migration routes so a reopened project and a repeated run behave the same; never leave orphaned jobs, temporary values or corrupted project state after cancellation or failure.'),
@@ -136,7 +141,7 @@ export const BUILDER_CONTRACT=Object.freeze({
136
141
  gapClasses:GAP_CLASSES,
137
142
  });
138
143
 
139
- const SECTION_TITLES=Object.freeze({'grounded-text':'Only supplied facts','source-of-truth':'Source of truth','reuse-first':'Reuse first','block-tier':'Three sizes of Block','block-scope':'One job per Step','capability-contract':'Define the capability first','feature-map':'Feature map','compose':'Blocks compose','platform-primitives':'Use platform primitives','no-workarounds':'No hidden workarounds','naming':'Name it briefly','ui':'Interface','multiplayer':'Shared editing','external-services':'External services','dependencies':'Dependencies','security':'Security','lifecycle':'Execution lifecycle','errors':'Useful failures','adapt-to-platform':'Adapt to the platform','verification':'What StillMade verifies','project-types':'Project Types','readiness-report':'Readiness report'});
144
+ const SECTION_TITLES=Object.freeze({'runtime-choice':'Choose the runtime','grounded-text':'Only supplied facts','source-of-truth':'Source of truth','reuse-first':'Reuse first','block-tier':'Three sizes of Block','block-scope':'One job per Step','capability-contract':'Define the capability first','feature-map':'Feature map','compose':'Blocks compose','platform-primitives':'Use platform primitives','no-workarounds':'No hidden workarounds','naming':'Name it briefly','ui':'Interface','multiplayer':'Shared editing','external-services':'External services','dependencies':'Dependencies','security':'Security','lifecycle':'Execution lifecycle','errors':'Useful failures','adapt-to-platform':'Adapt to the platform','verification':'What StillMade verifies','project-types':'Project Types','readiness-report':'Readiness report'});
140
145
  const inScope=(scope,wanted)=>wanted==='all'||scope==='both'||scope===wanted;
141
146
 
142
147
  /** Render the contract for a model prompt ('compact'), a guide ('full') or an SDK folder ('agents-md'). */
@@ -556,6 +556,9 @@ export function defaultCapabilityExpectations(manifest){
556
556
  if(operation==='image.generate')return {[name]:{kind:'image',format:'png',minWidth:1,maxWidth:IMAGE_CAPABILITY_LIMITS.maxSide,minHeight:1,maxHeight:IMAGE_CAPABILITY_LIMITS.maxSide,minBytes:67,maxBytes:IMAGE_CAPABILITY_LIMITS.maxBytes}};
557
557
  if(AUDIO_OPERATIONS.includes(operation))return {[name]:{kind:'audio',format:'any',minDuration:0,maxDuration:AUDIO_CAPABILITY_LIMITS[operation==='audio.music'?'music':'sfx'].max+1,minBytes:45,maxBytes:AUDIO_CAPABILITY_LIMITS.bytes}};
558
558
  if(manifest.outputs[name].type==='json')return {[name]:{kind:'json'}};
559
+ // A structured AI result is checked by its declared fields (October 22, 2026: it got text bounds, which its
560
+ // own check refuses, so every AI decide run failed after the model answered).
561
+ if(manifest.outputs[name].type==='object')return {[name]:{kind:'object'}};
559
562
  return {[name]:operation==='audio.speech'?{kind:'audio',format:'wav',minDuration:0,maxDuration:SPEECH_CAPABILITY_LIMITS.duration,minBytes:46,maxBytes:SPEECH_CAPABILITY_LIMITS.bytes}:{kind:manifest.outputs[name].type,minLength:1,maxLength:CAPABILITY_LIMITS.output}};
560
563
  }
561
564
 
@@ -1,6 +1,6 @@
1
1
  import {assert,plain,safeDocument,stableStringify,validateValues,SDK_VERSION} from './contracts.js';
2
2
  import {EXAMPLE_PROVENANCE} from './example-license.js';
3
- import {connectedAppView,connectedAppWorkspace} from '../../blocks/connected-app/versions/1.5.0/view.js';
3
+ import {connectedAppView,connectedAppWorkspace} from '../../blocks/connected-app/versions/1.6.0/view.js';
4
4
 
5
5
  const pinKeys=['backend','id','version','schemaHash','integrationId'];
6
6
  export function validateConnectedCapability(capability,manifest) {
@@ -180,6 +180,7 @@ export function validateManifest(manifest, { builtIn = false } = {}) {
180
180
  check(manifest.platforms?.desktop?.supported===true&&manifest.platforms?.browser?.supported===false&&manifest.platforms?.phone?.supported===false,'desktopTools','Approved native tools require an explicit desktop-only platform declaration');
181
181
  }
182
182
  if(manifest.permissions?.desktop?.includes('native.tools'))check(manifest.desktopTools!==undefined,'permissions.desktop','Declare the exact approved desktop tool adapter and actions');
183
+ if(manifest.permissions?.desktop?.includes('folder.write'))check(manifest.permissions.desktop.includes('folder.read'),'permissions.desktop','folder.write needs folder.read: a Block picks and lists a folder before it changes files in it');
183
184
  if(manifest.operations!==undefined){
184
185
  check(['javascript','recipe'].includes(manifest.runtime)&&Array.isArray(manifest.operations)&&manifest.operations.length>0&&manifest.operations.length<=12,'operations','Declare 1–12 local sandbox operations on a JavaScript or recipe Block');
185
186
  const ids=new Set();
@@ -1,2 +1,2 @@
1
1
  // Versioned, host-mediated desktop UI capabilities. No ambient native APIs.
2
- export const DESKTOP_PERMISSIONS=Object.freeze(['screen.capture','cursor.track','camera.capture','microphone.capture','clipboard.read','clipboard.write','media.pick','notifications.show','power.keep-awake','native.tools']);
2
+ export const DESKTOP_PERMISSIONS=Object.freeze(['screen.capture','cursor.track','camera.capture','microphone.capture','clipboard.read','clipboard.write','media.pick','notifications.show','power.keep-awake','native.tools','folder.read','folder.write']);
@@ -172,6 +172,7 @@ export const FIRST_PARTY_SDK_IDENTITIES=Object.freeze(new Set([
172
172
  "sm.voiceover@2.1.5",
173
173
  "sm.voiceover@2.1.6",
174
174
  "sm.when@1.0.0",
175
+ "sm.when@1.1.0",
175
176
  "sm.whiteboard-assistant@1.0.0",
176
177
  "sm.write-summary@1.0.0",
177
178
  "sm.youtube@2.0.0",
@@ -46,6 +46,7 @@ import thumbnail from '../../blocks/native/sm.thumbnail/versions/2.0.0/package.j
46
46
  import thumbnailV201 from '../../blocks/native/sm.thumbnail/versions/2.0.1/package.js';
47
47
  import writeSummary from '../../blocks/native/sm.write-summary/versions/1.0.0/package.js';
48
48
  import automationWhen from '../../blocks/native/sm.when/versions/1.0.0/package.js';
49
+ import automationV110 from '../../blocks/native/sm.when/versions/1.1.0/package.js';
49
50
  import automationNewItems from '../../blocks/native/sm.new-items/versions/1.0.0/package.js';
50
51
  import automationAiDecide from '../../blocks/native/sm.ai-decide/versions/1.0.0/package.js';
51
52
  import automationContinueIf from '../../blocks/native/sm.continue-if/versions/1.0.0/package.js';
@@ -191,5 +192,5 @@ import pickPhotos from '../../blocks/native/sm.pick-photos/versions/1.0.0/packag
191
192
  // Exact, independently executable first-party releases. Host workspaces do not
192
193
  // belong here. Changing implementation requires a new immutable version.
193
194
  const freeze=value=>{if(value&&typeof value==='object'&&!Object.isFrozen(value)){Object.values(value).forEach(freeze);Object.freeze(value);}return value;};
194
- export const FIRST_PARTY_SDK_PACKAGES = Object.freeze([freeze(productAdVariants),freeze(pickPhotos),freeze(photoNotes),freeze(youtubePublication),freeze(stockSearch),freeze(exportVideoV210),freeze(exportVideo),freeze(exportAudio),freeze(motionGraphics),freeze(audioScore),freeze(shortFormFinish),freeze(screenRecorder),freeze(narration),freeze(shortFormFinishV102),freeze(shortFormFinishV101),freeze(shortFormFinishV100),freeze(occasionStory),freeze(occasionStoryV101),freeze(occasionStoryV100),freeze(releaseVisuals),freeze(releaseVisualsV101),freeze(releaseVisualsV100),freeze(photoReelComposer),freeze(photoReelComposerV101),freeze(photoReelComposerV100),freeze(importMediaV202),freeze(importMediaV201),freeze(editorV2212),freeze(editorV2211),freeze(editorV2210),freeze(editorV229),freeze(editorV228),freeze(panelStripV241),freeze(panelStripV240),freeze(panelStripV237),freeze(panelStripV236),freeze(boardEditorV2210),freeze(boardEditorV229),freeze(animatedSceneV238),freeze(animatedSceneV237),freeze(pipelineV225),freeze(pipelineV224),freeze(blueprintV219),freeze(blueprintV218),freeze(videoAssetsV214),freeze(videoAssetsV213),freeze(chatV203),freeze(chatV202),freeze(chatV201),freeze(shotPlanV221),freeze(shotPlanV220),freeze(shotPlanV213),freeze(shotPlanV212),freeze(voiceoverV216),freeze(voiceoverV215),freeze(voiceoverV214),freeze(voiceoverV213),freeze(scriptV215),freeze(scriptV214),freeze(scriptV213),freeze(roughCutV202),freeze(roughCutV201),freeze(finalEditV202),freeze(finalEditV201),freeze(editAssistantV202),freeze(editAssistantV201),freeze(gradeV202),freeze(gradeV201),freeze(transitionV202),freeze(transitionV201),freeze(qualityAuditV102),freeze(qualityAuditV101),freeze(compareV202),freeze(compareV201),freeze(compare),freeze(grade),freeze(transition),freeze(exportTimeline),freeze(captions),freeze(importMedia),freeze(generateImageV210),freeze(generateImage),freeze(thumbnailV201),freeze(thumbnail),freeze(writeSummary),freeze(automationWhen),freeze(automationNewItems),freeze(automationAiDecide),freeze(automationContinueIf),freeze(connectApp),freeze(automationContactFields),freeze(automationAiWrite),freeze(automationMetaCreative),freeze(voiceover),freeze(voiceoverV210),freeze(voiceoverV211),freeze(voiceoverV212),freeze(script),freeze(scriptV210),freeze(scriptV211),freeze(scriptV212),freeze(generateVideoV201),freeze(generateVideo),freeze(chat),freeze(characterV201),freeze(character),freeze(locationV201),freeze(location),freeze(styleV201),freeze(style),freeze(researchV202),freeze(researchV201),freeze(research),freeze(imageEditorV201),freeze(imageEditor),freeze(describeMediaV201),freeze(describeMedia),freeze(videoAssets),freeze(videoAssetsV210),freeze(videoAssetsV211),freeze(videoAssetsV212),freeze(roughCut),freeze(finalEdit),freeze(editAssistant),freeze(transcribeV201),freeze(transcribe),freeze(blueprint),freeze(blueprintV210),freeze(blueprintV211),freeze(blueprintV212),freeze(blueprintV213),freeze(blueprintV214),freeze(blueprintV215),freeze(blueprintV216),freeze(blueprintV217),freeze(shotPlan),freeze(shotPlanV210),freeze(shotPlanV211),freeze(boardEditor),freeze(boardEditorV210),freeze(boardEditorV220),freeze(boardEditorV221),freeze(boardEditorV222),freeze(boardEditorV223),freeze(boardEditorV224),freeze(boardEditorV225),freeze(boardEditorV226),freeze(boardEditorV227),freeze(boardEditorV228),freeze(panelStripV200),freeze(panelStrip),freeze(panelStripV220),freeze(panelStripV230),freeze(panelStripV231),freeze(panelStripV232),freeze(panelStripV233),freeze(panelStripV234),freeze(panelStripV235),freeze(animatedSceneV200),freeze(animatedScene),freeze(animatedSceneV220),freeze(animatedSceneV230),freeze(animatedSceneV231),freeze(animatedSceneV232),freeze(animatedSceneV233),freeze(animatedSceneV234),freeze(animatedSceneV235),freeze(animatedSceneV236),freeze(editor),freeze(editorV210),freeze(editorV220),freeze(editorV221),freeze(editorV222),freeze(editorV223),freeze(editorV224),freeze(editorV225),freeze(editorV226),freeze(editorV227),freeze(pipelineV200),freeze(pipeline),freeze(pipelineV220),freeze(pipelineV221),freeze(pipelineV223),freeze(pipelineV222),freeze(shotPlanAssistant),freeze(animatedSceneAssistant),freeze(blueprintAssistant),freeze(whiteboardAssistant),freeze(qualityAudit)]);
195
+ export const FIRST_PARTY_SDK_PACKAGES = Object.freeze([freeze(productAdVariants),freeze(pickPhotos),freeze(photoNotes),freeze(youtubePublication),freeze(stockSearch),freeze(exportVideoV210),freeze(exportVideo),freeze(exportAudio),freeze(motionGraphics),freeze(audioScore),freeze(shortFormFinish),freeze(screenRecorder),freeze(narration),freeze(shortFormFinishV102),freeze(shortFormFinishV101),freeze(shortFormFinishV100),freeze(occasionStory),freeze(occasionStoryV101),freeze(occasionStoryV100),freeze(releaseVisuals),freeze(releaseVisualsV101),freeze(releaseVisualsV100),freeze(photoReelComposer),freeze(photoReelComposerV101),freeze(photoReelComposerV100),freeze(importMediaV202),freeze(importMediaV201),freeze(editorV2212),freeze(editorV2211),freeze(editorV2210),freeze(editorV229),freeze(editorV228),freeze(panelStripV241),freeze(panelStripV240),freeze(panelStripV237),freeze(panelStripV236),freeze(boardEditorV2210),freeze(boardEditorV229),freeze(animatedSceneV238),freeze(animatedSceneV237),freeze(pipelineV225),freeze(pipelineV224),freeze(blueprintV219),freeze(blueprintV218),freeze(videoAssetsV214),freeze(videoAssetsV213),freeze(chatV203),freeze(chatV202),freeze(chatV201),freeze(shotPlanV221),freeze(shotPlanV220),freeze(shotPlanV213),freeze(shotPlanV212),freeze(voiceoverV216),freeze(voiceoverV215),freeze(voiceoverV214),freeze(voiceoverV213),freeze(scriptV215),freeze(scriptV214),freeze(scriptV213),freeze(roughCutV202),freeze(roughCutV201),freeze(finalEditV202),freeze(finalEditV201),freeze(editAssistantV202),freeze(editAssistantV201),freeze(gradeV202),freeze(gradeV201),freeze(transitionV202),freeze(transitionV201),freeze(qualityAuditV102),freeze(qualityAuditV101),freeze(compareV202),freeze(compareV201),freeze(compare),freeze(grade),freeze(transition),freeze(exportTimeline),freeze(captions),freeze(importMedia),freeze(generateImageV210),freeze(generateImage),freeze(thumbnailV201),freeze(thumbnail),freeze(writeSummary),freeze(automationV110),freeze(automationWhen),freeze(automationNewItems),freeze(automationAiDecide),freeze(automationContinueIf),freeze(connectApp),freeze(automationContactFields),freeze(automationAiWrite),freeze(automationMetaCreative),freeze(voiceover),freeze(voiceoverV210),freeze(voiceoverV211),freeze(voiceoverV212),freeze(script),freeze(scriptV210),freeze(scriptV211),freeze(scriptV212),freeze(generateVideoV201),freeze(generateVideo),freeze(chat),freeze(characterV201),freeze(character),freeze(locationV201),freeze(location),freeze(styleV201),freeze(style),freeze(researchV202),freeze(researchV201),freeze(research),freeze(imageEditorV201),freeze(imageEditor),freeze(describeMediaV201),freeze(describeMedia),freeze(videoAssets),freeze(videoAssetsV210),freeze(videoAssetsV211),freeze(videoAssetsV212),freeze(roughCut),freeze(finalEdit),freeze(editAssistant),freeze(transcribeV201),freeze(transcribe),freeze(blueprint),freeze(blueprintV210),freeze(blueprintV211),freeze(blueprintV212),freeze(blueprintV213),freeze(blueprintV214),freeze(blueprintV215),freeze(blueprintV216),freeze(blueprintV217),freeze(shotPlan),freeze(shotPlanV210),freeze(shotPlanV211),freeze(boardEditor),freeze(boardEditorV210),freeze(boardEditorV220),freeze(boardEditorV221),freeze(boardEditorV222),freeze(boardEditorV223),freeze(boardEditorV224),freeze(boardEditorV225),freeze(boardEditorV226),freeze(boardEditorV227),freeze(boardEditorV228),freeze(panelStripV200),freeze(panelStrip),freeze(panelStripV220),freeze(panelStripV230),freeze(panelStripV231),freeze(panelStripV232),freeze(panelStripV233),freeze(panelStripV234),freeze(panelStripV235),freeze(animatedSceneV200),freeze(animatedScene),freeze(animatedSceneV220),freeze(animatedSceneV230),freeze(animatedSceneV231),freeze(animatedSceneV232),freeze(animatedSceneV233),freeze(animatedSceneV234),freeze(animatedSceneV235),freeze(animatedSceneV236),freeze(editor),freeze(editorV210),freeze(editorV220),freeze(editorV221),freeze(editorV222),freeze(editorV223),freeze(editorV224),freeze(editorV225),freeze(editorV226),freeze(editorV227),freeze(pipelineV200),freeze(pipeline),freeze(pipelineV220),freeze(pipelineV221),freeze(pipelineV223),freeze(pipelineV222),freeze(shotPlanAssistant),freeze(animatedSceneAssistant),freeze(blueprintAssistant),freeze(whiteboardAssistant),freeze(qualityAudit)]);
195
196
  export const firstPartySdkPackage = manifest => FIRST_PARTY_SDK_PACKAGES.find(pkg => pkg.manifest.id === manifest?.id && pkg.manifest.version === manifest?.version);