{"id":14,"date":"2026-04-22T08:39:39","date_gmt":"2026-04-22T08:39:39","guid":{"rendered":"https:\/\/twainstudio.dev\/?page_id=14"},"modified":"2026-04-24T10:12:17","modified_gmt":"2026-04-24T10:12:17","slug":"director-ai","status":"publish","type":"page","link":"https:\/\/twainstudio.dev\/?page_id=14","title":{"rendered":"DIRECTOR.AI"},"content":{"rendered":"\n<h2 class=\"wp-block-heading alignfull has-text-align-center is-style-text-subtitle has-accent-3-color has-text-color has-background has-link-color wp-elements-ab0b6d4e72aeba3b75d55cd7159070bd is-style-text-subtitle--1\" style=\"background:linear-gradient(135deg,rgb(6,147,227) 0%,rgb(174,133,133) 66%,rgb(155,81,224) 100%)\">DIRECTOR \u2014 The AI Cinematography Studio That Costs You Nothing<\/h2>\n\n\n\n<div class=\"wp-block-media-text alignfull is-image-fill-element\" style=\"grid-template-columns:31% auto\" id=\"DIRECTORSTUDIOS\"><figure class=\"wp-block-media-text__media\"><img decoding=\"async\" src=\"https:\/\/twainstudio.dev\/wp-content\/uploads\/2026\/04\/DALL\u00b7E-2026-04-22-03.59.12-A-refined-premium-logo-on-a-deep-black-background-for-a-brand-named-DIRECTOR.-Create-a-single-unified-symbol-that-seamlessly-merges-a-directors-cha.webp\" alt=\"CAMERA SITTING IN A DIRECTOR CHAIR. \" class=\"wp-image-17 size-full\" style=\"object-position:49% 60%\"\/><\/figure><div class=\"wp-block-media-text__content\">\n<p class=\"has-accent-3-color has-accent-1-background-color has-text-color has-background has-link-color wp-elements-1cbdf4ac3165cac6fa6d8cba1752a5b4 wp-block-paragraph\">Most people think professional video production requires a budget. DIRECTOR proves them wrong.<\/p>\n\n\n\n<p class=\"has-accent-3-color has-accent-1-background-color has-text-color has-background has-link-color wp-elements-8753ec2b094f4ce44a510a484a86bf75 wp-block-paragraph\">DIRECTOR is the first AI-powered video production studio that doesn&#8217;t just help you write scripts \u2014 it thinks like a real cinematographer. It analyzes your content, breaks it into scenes, and intelligently routes each one to the exact free-tier AI platform built to nail that specific shot. Talking head? HeyGen. Cinematic hero reveal? Google Vids. Social ad effect? Pika. Human emotion close-up? HaiLuo. All routed automatically. All free.<\/p>\n\n\n\n<p data-wp-context---core-fit-text=\"core\/fit-text::{&quot;fontSize&quot;:&quot;&quot;}\" data-wp-init---core-fit-text=\"core\/fit-text::callbacks.init\" data-wp-interactive data-wp-style--font-size=\"core\/fit-text::context.fontSize\" class=\"has-fit-text wp-block-paragraph\">Behind the scenes, DIRECTOR is powered by your choice of AI engine \u2014 OpenAI, Claude, Gemini, Groq, DeepSeek, OpenRouter, or even a fully local Ollama model. You bring the idea. DIRECTOR builds the production plan.<\/p>\n<\/div><\/div>\n\n\n\n<div class=\"wp-block-group alignfull has-global-padding is-layout-constrained wp-block-group-is-layout-constrained\">\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"UTF-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n<title>TWAIN Director \u2014 AI Video Production Studio<\/title>\n<link href=\"https:\/\/fonts.googleapis.com\/css2?family=Bebas+Neue&#038;family=DM+Sans:wght@300;400;500;600&#038;family=JetBrains+Mono:wght@400;500;700&#038;display=swap\" rel=\"stylesheet\">\n<style>\n*{margin:0;padding:0;box-sizing:border-box;}\n:root{\n  --bg:#06080c;--surface:#0c0f16;--card:#111620;--border:#1c2333;--border2:#252e42;\n  --gold:#c9a84c;--gold2:#e8c97a;--red:#ff3b3b;--blue:#00aaff;--green:#00e676;\n  --orange:#ff6b35;--text:#e8edf5;--muted:#4a5568;--dim:#1a2030;--accent:#00aaff;\n}\nbody{background:var(--bg);color:var(--text);font-family:'DM Sans',sans-serif;font-weight:300;height:100vh;display:flex;flex-direction:column;overflow:hidden;}\nbody::after{content:'';position:fixed;inset:0;background:repeating-linear-gradient(0deg,transparent,transparent 3px,rgba(0,170,255,0.007) 3px,rgba(0,170,255,0.007) 4px);pointer-events:none;z-index:9998;}\n\n\/* HEADER *\/\n.app-header{display:flex;align-items:center;padding:0 16px;height:48px;border-bottom:1px solid var(--border);background:var(--surface);flex-shrink:0;gap:12px;}\n.brand{font-family:'Bebas Neue',sans-serif;font-size:22px;letter-spacing:4px;color:var(--text);flex-shrink:0;}\n.brand span{color:var(--gold);}\n.verified-badge{font-family:'JetBrains Mono',monospace;font-size:8px;letter-spacing:1px;padding:3px 8px;border:1px solid rgba(255,107,53,0.35);color:#ff9a6b;background:rgba(255,107,53,0.08);border-radius:3px;white-space:nowrap;}\n.header-right{margin-left:auto;font-family:'JetBrains Mono',monospace;font-size:8px;color:var(--muted);letter-spacing:1px;}\n\n\/* WARNING BANNER *\/\n.warning-banner{background:rgba(255,59,59,0.06);border-bottom:1px solid rgba(255,59,59,0.18);padding:7px 16px;display:flex;align-items:flex-start;gap:10px;flex-shrink:0;}\n.w-text{font-family:'JetBrains Mono',monospace;font-size:8.5px;letter-spacing:0.5px;color:#ff9a8a;line-height:1.6;flex:1;}\n.w-text strong{color:var(--orange);}\n.w-dismiss{font-family:'JetBrains Mono',monospace;font-size:8px;color:var(--muted);cursor:pointer;padding:2px 8px;border:1px solid var(--border);border-radius:2px;white-space:nowrap;flex-shrink:0;margin-top:1px;}\n.w-dismiss:hover{color:var(--text);border-color:var(--border2);}\n\n\/* API BAR *\/\n.api-bar{background:var(--surface);border-bottom:1px solid var(--border);padding:7px 16px;display:flex;align-items:center;gap:10px;flex-shrink:0;flex-wrap:wrap;}\n.api-label{font-family:'JetBrains Mono',monospace;font-size:8px;letter-spacing:2px;color:var(--muted);text-transform:uppercase;white-space:nowrap;flex-shrink:0;}\n.api-select,.model-select{background:var(--card);border:1px solid var(--border2);color:var(--text);padding:5px 8px;font-family:'JetBrains Mono',monospace;font-size:10px;border-radius:3px;cursor:pointer;}\n.api-select{min-width:180px;} .model-select{min-width:200px;}\n.api-select:focus,.model-select:focus,.api-key-input:focus{outline:none;border-color:var(--accent);}\n.api-key-wrap{display:flex;align-items:center;gap:6px;flex:1;min-width:220px;}\n.api-key-input{flex:1;background:var(--card);border:1px solid var(--border2);color:var(--text);padding:5px 10px;font-family:'JetBrains Mono',monospace;font-size:10px;border-radius:3px;min-width:0;}\n.api-key-input::placeholder{color:var(--muted);}\n.api-save-btn{background:var(--accent);color:#000;border:none;padding:5px 14px;font-family:'JetBrains Mono',monospace;font-size:9px;letter-spacing:1px;border-radius:3px;cursor:pointer;font-weight:700;white-space:nowrap;}\n.api-save-btn:hover{background:#33bbff;}\n.api-status{font-family:'JetBrains Mono',monospace;font-size:9px;letter-spacing:1px;padding:3px 8px;border-radius:3px;white-space:nowrap;}\n.api-status.on{color:var(--green);border:1px solid rgba(0,230,118,0.3);background:rgba(0,230,118,0.06);}\n.api-status.off{color:var(--muted);border:1px solid var(--border);}\n.api-privacy{font-family:'JetBrains Mono',monospace;font-size:7.5px;color:var(--muted);}\n\n\/* MAIN *\/\n.main{display:flex;flex:1;overflow:hidden;}\n\n\/* PLATFORM PANEL *\/\n.platform-panel{width:215px;border-right:1px solid var(--border);overflow-y:auto;background:var(--surface);flex-shrink:0;padding:10px;}\n.panel-title{font-family:'JetBrains Mono',monospace;font-size:8px;letter-spacing:3px;color:var(--muted);padding:4px 4px 10px;border-bottom:1px solid var(--border);margin-bottom:8px;text-transform:uppercase;}\n.plat-group{font-family:'JetBrains Mono',monospace;font-size:7px;letter-spacing:2px;color:var(--muted);padding:8px 4px 4px;text-transform:uppercase;opacity:0.6;}\n.pc{border:1px solid var(--border);border-radius:4px;padding:7px 9px;margin-bottom:5px;cursor:pointer;transition:all .12s;position:relative;overflow:hidden;}\n.pc::before{content:'';position:absolute;left:0;top:0;bottom:0;width:3px;background:var(--pc,var(--accent));}\n.pc:hover{border-color:var(--border2);background:rgba(255,255,255,0.015);}\n.pc.sel{border-color:var(--pc,var(--accent));background:rgba(0,170,255,0.04);}\n.pc-top{display:flex;align-items:center;gap:5px;margin-bottom:3px;}\n.pc-name{font-size:11px;font-weight:500;}\n.pc-badge{margin-left:auto;font-family:'JetBrains Mono',monospace;font-size:7px;padding:1px 5px;border-radius:2px;}\n.free-b{background:rgba(0,230,118,0.08);color:var(--green);border:1px solid rgba(0,230,118,0.2);}\n.card-b{background:rgba(255,59,59,0.08);color:#ff7a7a;border:1px solid rgba(255,59,59,0.2);}\n.pc-info{font-family:'JetBrains Mono',monospace;font-size:7.5px;color:var(--muted);line-height:1.5;}\n.pc-warn{font-family:'JetBrains Mono',monospace;font-size:7px;color:#ff7a7a;margin-top:2px;}\n\n\/* CENTER *\/\n.center{flex:1;display:flex;flex-direction:column;overflow:hidden;min-width:0;}\n.tabs{display:flex;border-bottom:1px solid var(--border);background:var(--surface);flex-shrink:0;}\n.tab-btn{padding:10px 14px;font-family:'JetBrains Mono',monospace;font-size:9px;letter-spacing:2px;text-transform:uppercase;color:var(--muted);background:none;border:none;border-bottom:2px solid transparent;cursor:pointer;transition:all .12s;}\n.tab-btn:hover{color:var(--text);}\n.tab-btn.active{color:var(--accent);border-bottom-color:var(--accent);}\n.tab-content{display:none;flex:1;overflow:hidden;flex-direction:column;}\n.tab-content.active{display:flex;}\n\n\/* SCRIPT TAB *\/\n.script-controls{padding:10px 12px;border-bottom:1px solid var(--border);display:flex;gap:8px;flex-wrap:wrap;align-items:flex-end;flex-shrink:0;}\n.cg{display:flex;flex-direction:column;gap:3px;}\n.cg label{font-family:'JetBrains Mono',monospace;font-size:7.5px;letter-spacing:2px;color:var(--muted);text-transform:uppercase;}\n.c-sel,.c-inp{background:var(--card);border:1px solid var(--border2);color:var(--text);padding:5px 8px;font-family:'DM Sans',sans-serif;font-size:12px;border-radius:3px;}\n.c-sel:focus,.c-inp:focus{outline:none;border-color:var(--accent);}\n.c-inp{min-width:180px;}\n.btn{padding:6px 14px;border:1px solid var(--border2);background:transparent;color:var(--text);font-family:'JetBrains Mono',monospace;font-size:8px;letter-spacing:2px;text-transform:uppercase;cursor:pointer;border-radius:3px;transition:all .12s;white-space:nowrap;}\n.btn:hover{border-color:var(--accent);color:var(--accent);}\n.btn-pri{background:var(--accent);border-color:var(--accent);color:#000;font-weight:700;}\n.btn-pri:hover{background:#33bbff;color:#000;}\n.btn-gold{background:var(--gold);border-color:var(--gold);color:#000;font-weight:700;}\n.btn-gold:hover{background:var(--gold2);color:#000;}\n.btn-ghost{color:var(--muted);}\n.script-area{flex:1;display:flex;flex-direction:column;overflow:hidden;padding:10px 12px;gap:8px;}\n.script-ta{flex:1;background:var(--card);border:1px solid var(--border);color:var(--text);padding:12px;font-family:'DM Sans',sans-serif;font-size:13px;line-height:1.7;resize:none;border-radius:4px;}\n.script-ta:focus{outline:none;border-color:var(--accent);}\n.script-ta::placeholder{color:var(--muted);}\n.script-footer{display:flex;align-items:center;gap:8px;flex-shrink:0;}\n.script-stats{font-family:'JetBrains Mono',monospace;font-size:8.5px;color:var(--muted);}\n\n\/* SCENE ROUTER *\/\n.router-bar{padding:7px 12px;border-bottom:1px solid var(--border);display:flex;align-items:center;gap:8px;flex-shrink:0;}\n.router-bar span{font-family:'JetBrains Mono',monospace;font-size:8px;color:var(--muted);}\n.scene-count{color:var(--accent) !important;}\n.scene-container{flex:1;overflow-y:auto;padding:10px 12px;display:flex;flex-direction:column;gap:8px;}\n.scene-block{background:var(--card);border:1px solid var(--border);border-radius:4px;overflow:hidden;border-left:3px solid var(--pc,var(--border));}\n.scene-hdr{display:flex;align-items:center;gap:8px;padding:7px 12px;background:rgba(255,255,255,0.018);border-bottom:1px solid var(--border);}\n.scene-num{font-family:'Bebas Neue',sans-serif;font-size:20px;color:var(--pc,var(--muted));line-height:1;}\n.scene-type{font-family:'JetBrains Mono',monospace;font-size:7.5px;letter-spacing:2px;padding:2px 6px;border-radius:2px;background:rgba(255,255,255,0.04);color:var(--muted);}\n.scene-dur{margin-left:auto;font-family:'JetBrains Mono',monospace;font-size:8px;color:var(--muted);}\n.scene-body{padding:9px 12px;display:flex;flex-direction:column;gap:7px;}\n.scene-desc{font-size:12px;color:var(--text);line-height:1.6;}\n.scene-vo{font-family:'JetBrains Mono',monospace;font-size:8.5px;color:var(--muted);padding:5px 8px;background:var(--dim);border-radius:3px;line-height:1.5;}\n.plat-row{display:flex;align-items:center;gap:7px;flex-wrap:wrap;}\n.plat-badge{font-family:'JetBrains Mono',monospace;font-size:8px;padding:2px 8px;border-radius:3px;border:1px solid;letter-spacing:1px;}\n.routing-reason{font-family:'JetBrains Mono',monospace;font-size:7.5px;color:var(--muted);}\n.cost-tag{font-family:'JetBrains Mono',monospace;font-size:7px;padding:2px 6px;border-radius:2px;}\n.ct-free{background:rgba(0,230,118,0.07);color:var(--green);border:1px solid rgba(0,230,118,0.18);}\n.ct-card{background:rgba(255,59,59,0.07);color:#ff7a7a;border:1px solid rgba(255,59,59,0.18);}\n.reroute-sel{background:var(--dim);border:1px solid var(--border);color:var(--text);padding:3px 6px;font-family:'JetBrains Mono',monospace;font-size:8px;border-radius:3px;margin-left:auto;}\n.prompt-out{background:var(--dim);border:1px solid var(--border);border-radius:3px;padding:9px;font-family:'JetBrains Mono',monospace;font-size:9.5px;color:var(--text);line-height:1.65;white-space:pre-wrap;display:none;margin-top:2px;}\n.prompt-out.vis{display:block;}\n.scene-actions{display:flex;gap:5px;flex-wrap:wrap;}\n.bs{padding:3px 9px;border:1px solid var(--border2);background:transparent;color:var(--muted);font-family:'JetBrains Mono',monospace;font-size:7.5px;letter-spacing:1px;cursor:pointer;border-radius:3px;transition:all .12s;}\n.bs:hover{border-color:var(--accent);color:var(--accent);}\n.bs.gen{border-color:var(--gold);color:var(--gold);}\n.bs.gen:hover{background:var(--gold);color:#000;}\n.bs.copy{border-color:var(--green);color:var(--green);}\n.bs.copy:hover{background:var(--green);color:#000;}\n.bs.open{border-color:var(--blue);color:var(--blue);}\n.bs.open:hover{background:var(--blue);color:#000;}\n\n\/* STORYBOARD *\/\n.board-grid{flex:1;overflow-y:auto;padding:12px;display:grid;grid-template-columns:repeat(auto-fill,minmax(155px,1fr));gap:9px;align-content:start;}\n.board-card{background:var(--card);border:1px solid var(--border);border-radius:4px;overflow:hidden;cursor:pointer;transition:all .12s;}\n.board-card:hover{border-color:var(--border2);transform:translateY(-1px);}\n.board-frame{height:86px;display:flex;align-items:center;justify-content:center;position:relative;flex-direction:column;gap:5px;}\n.board-badge{position:absolute;bottom:5px;right:5px;font-family:'JetBrains Mono',monospace;font-size:7px;padding:2px 5px;border-radius:2px;background:rgba(0,0,0,0.65);}\n.board-info{padding:7px 8px;}\n.board-title{font-family:'JetBrains Mono',monospace;font-size:8px;color:var(--muted);margin-bottom:3px;}\n.board-desc{font-size:10px;color:var(--text);line-height:1.4;}\n\n\/* TIMELINE *\/\n.timeline-wrap{flex:1;overflow:auto;padding:12px;}\n.tl-track{display:flex;flex-direction:column;gap:5px;min-width:500px;}\n.tl-row{display:flex;align-items:center;gap:8px;}\n.tl-plat{font-family:'JetBrains Mono',monospace;font-size:8px;color:var(--muted);width:95px;flex-shrink:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}\n.tl-bar{height:26px;border-radius:3px;display:flex;align-items:center;padding:0 8px;font-family:'JetBrains Mono',monospace;font-size:7.5px;color:#fff;white-space:nowrap;overflow:hidden;cursor:pointer;}\n.tl-bar:hover{opacity:0.8;}\n.tl-dur{font-family:'JetBrains Mono',monospace;font-size:8px;color:var(--muted);margin-left:5px;}\n\n\/* CHAT *\/\n.chat-panel{width:290px;border-left:1px solid var(--border);display:flex;flex-direction:column;background:var(--surface);flex-shrink:0;}\n.chat-hdr{padding:9px 14px;border-bottom:1px solid var(--border);display:flex;align-items:center;gap:8px;flex-shrink:0;}\n.chat-title{font-family:'Bebas Neue',sans-serif;font-size:17px;letter-spacing:3px;}\n.chat-dot{width:7px;height:7px;border-radius:50%;background:var(--green);animation:pulse 2s infinite;}\n@keyframes pulse{0%,100%{opacity:1;}50%{opacity:0.35;}}\n.chat-msgs{flex:1;overflow-y:auto;padding:10px;display:flex;flex-direction:column;gap:7px;}\n.msg{padding:8px 10px;border-radius:4px;font-size:11.5px;line-height:1.6;}\n.msg-director{background:var(--card);border:1px solid var(--border);border-left:2px solid var(--accent);}\n.msg-user{background:rgba(201,168,76,0.07);border:1px solid rgba(201,168,76,0.15);border-left:2px solid var(--gold);}\n.msg-label{font-family:'JetBrains Mono',monospace;font-size:7px;letter-spacing:2px;text-transform:uppercase;color:var(--muted);margin-bottom:3px;}\n.typing{display:flex;gap:4px;align-items:center;padding:10px;}\n.dot{width:5px;height:5px;border-radius:50%;background:var(--accent);animation:bounce .8s infinite;}\n.dot:nth-child(2){animation-delay:.15s;} .dot:nth-child(3){animation-delay:.3s;}\n@keyframes bounce{0%,100%{transform:translateY(0);}50%{transform:translateY(-5px);}}\n.chat-inp-wrap{padding:9px;border-top:1px solid var(--border);display:flex;gap:6px;flex-shrink:0;}\n.chat-inp{flex:1;background:var(--card);border:1px solid var(--border2);color:var(--text);padding:7px 10px;font-family:'DM Sans',sans-serif;font-size:12px;border-radius:3px;resize:none;height:36px;}\n.chat-inp:focus{outline:none;border-color:var(--accent);}\n.chat-send{background:var(--accent);color:#000;border:none;padding:7px 12px;font-family:'JetBrains Mono',monospace;font-size:11px;border-radius:3px;cursor:pointer;font-weight:700;}\n.chat-send:hover{background:#33bbff;}\n\n\/* MISC *\/\n.empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:10px;height:100%;color:var(--muted);text-align:center;padding:40px;}\n.empty-icon{font-size:32px;opacity:0.3;}\n.empty-msg{font-family:'JetBrains Mono',monospace;font-size:9.5px;letter-spacing:1px;line-height:1.7;}\n.progress-bar{height:2px;background:var(--border);flex-shrink:0;}\n.progress-fill{height:100%;background:linear-gradient(90deg,var(--accent),var(--gold));width:0%;transition:width .3s;}\n.export-btn{padding:4px 12px;background:rgba(201,168,76,0.08);border:1px solid rgba(201,168,76,0.25);color:var(--gold);font-family:'JetBrains Mono',monospace;font-size:7.5px;letter-spacing:1px;cursor:pointer;border-radius:3px;}\n.export-btn:hover{background:rgba(201,168,76,0.18);}\n::-webkit-scrollbar{width:4px;height:4px;}\n::-webkit-scrollbar-track{background:transparent;}\n::-webkit-scrollbar-thumb{background:var(--border2);border-radius:2px;}\n<\/style>\n<\/head>\n<body>\n\n<!-- HEADER -->\n<div class=\"app-header\">\n  <div class=\"brand\">T.W.A.I.N. <span>DIRECTOR<\/span><\/div>\n  <div class=\"verified-badge\">\u26a0 FREE TIER STATUS VERIFIED APRIL 2026 \u2014 ALWAYS CHECK PLATFORMS DIRECTLY FOR CHANGES<\/div>\n  <div class=\"header-right\">twainstudio.dev<\/div>\n<\/div>\n\n<!-- WARNING BANNER -->\n<div class=\"warning-banner\" id=\"warnBanner\">\n  <div class=\"w-text\">\n    <strong>\u26a0 IMPORTANT \u2014 READ BEFORE YOU GENERATE:<\/strong> Free tiers change without notice. No alarm will go off. What costs $0 today may cost money tomorrow. You may be charged <strong>twice<\/strong> \u2014 once by your AI provider for script\/prompt generation, and again by the video platform for video generation. Platforms marked <strong style=\"color:#ff7a7a;\">CARD REQ<\/strong> require a credit card even for free access. TWAIN is not responsible for third-party pricing changes. Always verify current pricing before generating at scale. Last verified: April 2026.\n  <\/div>\n  <div class=\"w-dismiss\" onclick=\"document.getElementById('warnBanner').style.display='none'\">\u2715 Dismiss<\/div>\n<\/div>\n\n<!-- API BAR -->\n<div class=\"api-bar\">\n  <div class=\"api-label\">Provider<\/div>\n  <select class=\"api-select\" id=\"providerSelect\" onchange=\"updateProviderModels()\">\n    <option value=\"openrouter\">OpenRouter (Recommended \u2014 hundreds of models, one key)<\/option>\n    <option value=\"groq\">Groq (Free tier \u00b7 Fast \u00b7 No card required)<\/option>\n    <option value=\"anthropic\">Anthropic \u2014 Claude<\/option>\n    <option value=\"openai\">OpenAI \u2014 GPT<\/option>\n    <option value=\"gemini\">Google Gemini<\/option>\n    <option value=\"deepseek\">DeepSeek<\/option>\n    <option value=\"xai\">xAI \u2014 Grok<\/option>\n    <option value=\"mistral\">Mistral AI<\/option>\n  <\/select>\n  <select class=\"model-select\" id=\"modelSelect\"><\/select>\n  <div class=\"api-key-wrap\">\n    <div class=\"api-label\">Key<\/div>\n    <input class=\"api-key-input\" id=\"apiKeyInput\" type=\"password\" placeholder=\"Paste your API key \u2014 stored in your browser only, never sent to TWAIN\">\n    <button class=\"api-save-btn\" onclick=\"saveApiKey()\">Save Key<\/button>\n  <\/div>\n  <div class=\"api-status off\" id=\"apiStatus\">No Key<\/div>\n  <div class=\"api-privacy\">\ud83d\udd12 Your key \u00b7 Your bill \u00b7 Your browser<\/div>\n<\/div>\n\n<!-- PROGRESS -->\n<div class=\"progress-bar\"><div class=\"progress-fill\" id=\"progressFill\"><\/div><\/div>\n\n<!-- MAIN -->\n<div class=\"main\">\n\n  <!-- PLATFORM PANEL -->\n  <div class=\"platform-panel\">\n    <div class=\"panel-title\">Video Platforms<\/div>\n\n    <div class=\"plat-group\">\u2705 No Card Required<\/div>\n\n    <div class=\"pc\" style=\"--pc:#a855f7\" onclick=\"selectPlatform('kling',this)\">\n      <div class=\"pc-top\"><span>\ud83c\udfac<\/span><span class=\"pc-name\">Kling AI<\/span><span class=\"pc-badge free-b\">FREE<\/span><\/div>\n      <div class=\"pc-info\">~66 credits\/day \u00b7 up to 10s \u00b7 720p<br>Best: smooth motion &#038; consistency<\/div>\n    <\/div>\n    <div class=\"pc\" style=\"--pc:#f97316\" onclick=\"selectPlatform('pika',this)\">\n      <div class=\"pc-top\"><span>\u26a1<\/span><span class=\"pc-name\">Pika<\/span><span class=\"pc-badge free-b\">FREE<\/span><\/div>\n      <div class=\"pc-info\">Daily credits \u00b7 3\u20134s clips<br>Best: stylized effects, social ads<\/div>\n    <\/div>\n    <div class=\"pc\" style=\"--pc:#22c55e\" onclick=\"selectPlatform('hailuo',this)\">\n      <div class=\"pc-top\"><span>\ud83d\udc64<\/span><span class=\"pc-name\">HaiLuo AI<\/span><span class=\"pc-badge free-b\">FREE<\/span><\/div>\n      <div class=\"pc-info\">Several free\/day \u00b7 up to 6s \u00b7 watermark<br>Best: human faces &#038; movement<\/div>\n    <\/div>\n    <div class=\"pc\" style=\"--pc:#3b82f6\" onclick=\"selectPlatform('googlevids',this)\">\n      <div class=\"pc-top\"><span>\ud83d\udd35<\/span><span class=\"pc-name\">Google Vids<\/span><span class=\"pc-badge free-b\">FREE<\/span><\/div>\n      <div class=\"pc-info\">10 videos\/month \u00b7 ~8s \u00b7 highest quality<br>Best: hero shots \u2014 use sparingly<\/div>\n    <\/div>\n    <div class=\"pc\" style=\"--pc:#f59e0b\" onclick=\"selectPlatform('invideo',this)\">\n      <div class=\"pc-top\"><span>\ud83d\udcf9<\/span><span class=\"pc-name\">InVideo AI<\/span><span class=\"pc-badge free-b\">FREE<\/span><\/div>\n      <div class=\"pc-info\">10 min AI\/week \u00b7 720p watermark<br>Best: full auto-assembled videos<\/div>\n    <\/div>\n\n    <div class=\"plat-group\" style=\"margin-top:6px;\">\ud83d\udcb3 Credit Card Required<\/div>\n\n    <div class=\"pc\" style=\"--pc:#ef4444\" onclick=\"selectPlatform('runway',this)\">\n      <div class=\"pc-top\"><span>\ud83d\ude80<\/span><span class=\"pc-name\">Runway<\/span><span class=\"pc-badge card-b\">CARD REQ<\/span><\/div>\n      <div class=\"pc-info\">125 one-time credits \u00b7 industry leader<br>Best: cinematic quality, effects<\/div>\n      <div class=\"pc-warn\">\u26a0 Card required at signup<\/div>\n    <\/div>\n    <div class=\"pc\" style=\"--pc:#ec4899\" onclick=\"selectPlatform('heygen',this)\">\n      <div class=\"pc-top\"><span>\ud83d\udde3\ufe0f<\/span><span class=\"pc-name\">HeyGen<\/span><span class=\"pc-badge card-b\">CARD REQ<\/span><\/div>\n      <div class=\"pc-info\">3 videos\/month \u00b7 up to 3min \u00b7 watermark<br>Best: talking head presenter<\/div>\n      <div class=\"pc-warn\">\u26a0 Card required at signup<\/div>\n    <\/div>\n    <div class=\"pc\" style=\"--pc:#8b5cf6\" onclick=\"selectPlatform('synthesia',this)\">\n      <div class=\"pc-top\"><span>\ud83e\udd16<\/span><span class=\"pc-name\">Synthesia<\/span><span class=\"pc-badge card-b\">CARD REQ<\/span><\/div>\n      <div class=\"pc-info\">10 min\/month \u00b7 enterprise avatars<br>Best: professional AI presenter<\/div>\n      <div class=\"pc-warn\">\u26a0 Card required at signup<\/div>\n    <\/div>\n  <\/div>\n\n  <!-- CENTER -->\n  <div class=\"center\">\n    <div class=\"tabs\">\n      <button class=\"tab-btn active\" onclick=\"switchTab('script',this)\">Script<\/button>\n      <button class=\"tab-btn\" onclick=\"switchTab('router',this)\">Scene Router<\/button>\n      <button class=\"tab-btn\" onclick=\"switchTab('storyboard',this)\">Storyboard<\/button>\n      <button class=\"tab-btn\" onclick=\"switchTab('timeline',this)\">Timeline<\/button>\n    <\/div>\n\n    <!-- SCRIPT -->\n    <div class=\"tab-content active\" id=\"tab-script\">\n      <div class=\"script-controls\">\n        <div class=\"cg\">\n          <label>Format<\/label>\n          <select class=\"c-sel\" id=\"vidFormat\">\n            <option>Book Trailer<\/option><option>Social Ad (30s)<\/option>\n            <option>YouTube Intro<\/option><option>Brand Story<\/option>\n            <option>Product Demo<\/option><option>Documentary Short<\/option>\n          <\/select>\n        <\/div>\n        <div class=\"cg\">\n          <label>Tone<\/label>\n          <select class=\"c-sel\" id=\"vidTone\">\n            <option>Cinematic &#038; Dramatic<\/option><option>Suspenseful &#038; Tense<\/option>\n            <option>Inspirational<\/option><option>Dark &#038; Psychological<\/option>\n            <option>Energetic &#038; Fast<\/option><option>Emotional &#038; Moving<\/option>\n            <option>Professional &#038; Clean<\/option>\n          <\/select>\n        <\/div>\n        <div class=\"cg\">\n          <label>Length<\/label>\n          <select class=\"c-sel\" id=\"vidLength\">\n            <option>15 seconds<\/option><option>30 seconds<\/option>\n            <option>60 seconds<\/option><option>90 seconds<\/option><option>2 minutes<\/option>\n          <\/select>\n        <\/div>\n        <div class=\"cg\" style=\"flex:1\">\n          <label>Book \/ Project \/ Brand<\/label>\n          <input class=\"c-inp\" id=\"vidProduct\" placeholder=\"e.g. You Were Never Alone \u2014 psychological thriller\" style=\"width:100%\">\n        <\/div>\n        <button class=\"btn btn-pri\" onclick=\"generateScript()\">Generate Script<\/button>\n      <\/div>\n      <div class=\"script-area\">\n        <textarea class=\"script-ta\" id=\"scriptInput\" placeholder=\"Your script appears here after generation \u2014 or paste your own.\n\nDirector will break your script into scenes, route each scene to the best free video platform, and write a production-ready AI prompt for each scene \u2014 ready to paste directly into the platform.\n\n1. Set your AI provider above and save your key\n2. Click Generate Script \u2014 or paste your own\n3. Click Analyze &#038; Route Scenes\n4. Click Generate Prompt on each scene\n5. Copy prompt \u00b7 Open platform \u00b7 Paste \u00b7 Generate\"><\/textarea>\n        <div class=\"script-footer\">\n          <div class=\"script-stats\" id=\"scriptStats\">0 words \u00b7 0 scenes estimated<\/div>\n          <div style=\"margin-left:auto;display:flex;gap:8px;\">\n            <button class=\"btn btn-ghost\" onclick=\"clearScript()\">Clear<\/button>\n            <button class=\"btn btn-gold\" onclick=\"analyzeScript()\">\u26a1 Analyze &#038; Route Scenes<\/button>\n          <\/div>\n        <\/div>\n      <\/div>\n    <\/div>\n\n    <!-- ROUTER -->\n    <div class=\"tab-content\" id=\"tab-router\">\n      <div class=\"router-bar\">\n        <span>SCENE ROUTER<\/span>\n        <span class=\"scene-count\" id=\"sceneCount\">0 scenes<\/span>\n        <button class=\"btn btn-ghost\" style=\"padding:3px 10px;font-size:7.5px;margin-left:auto;\" onclick=\"addManualScene()\">+ Add Scene<\/button>\n        <button class=\"export-btn\" onclick=\"exportPlan()\">Export Plan<\/button>\n      <\/div>\n      <div class=\"scene-container\" id=\"sceneContainer\">\n        <div class=\"empty-state\" id=\"routerEmpty\">\n          <div class=\"empty-icon\">\ud83c\udfac<\/div>\n          <div class=\"empty-msg\">Generate or paste a script on the Script tab<br>then click Analyze &#038; Route Scenes<\/div>\n        <\/div>\n      <\/div>\n    <\/div>\n\n    <!-- STORYBOARD -->\n    <div class=\"tab-content\" id=\"tab-storyboard\">\n      <div class=\"board-grid\" id=\"boardGrid\">\n        <div class=\"empty-state\" style=\"grid-column:1\/-1\">\n          <div class=\"empty-icon\">\ud83d\uddbc\ufe0f<\/div>\n          <div class=\"empty-msg\">Analyze a script to generate the storyboard<\/div>\n        <\/div>\n      <\/div>\n    <\/div>\n\n    <!-- TIMELINE -->\n    <div class=\"tab-content\" id=\"tab-timeline\">\n      <div class=\"timeline-wrap\"><div class=\"tl-track\" id=\"tlTrack\">\n        <div class=\"empty-state\">\n          <div class=\"empty-icon\">\u23f1\ufe0f<\/div>\n          <div class=\"empty-msg\">Analyze a script to see the production timeline<\/div>\n        <\/div>\n      <\/div><\/div>\n    <\/div>\n  <\/div>\n\n  <!-- CHAT -->\n  <div class=\"chat-panel\">\n    <div class=\"chat-hdr\">\n      <div class=\"chat-dot\"><\/div>\n      <div class=\"chat-title\">DIRECTOR<\/div>\n      <div style=\"font-family:'JetBrains Mono',monospace;font-size:7px;color:var(--muted);margin-left:auto;\">AI CINEMATOGRAPHER<\/div>\n    <\/div>\n    <div class=\"chat-msgs\" id=\"chatMsgs\">\n      <div class=\"msg msg-director\">\n        <div class=\"msg-label\">Director<\/div>\n        I&#8217;m your AI Cinematographer. I write scripts, break them into scenes, route each to the best platform, and write production-ready prompts you paste directly into each platform.<br><br>\n        <strong>To start:<\/strong> Pick your AI provider above, paste your key, save it \u2014 then describe your project or click Generate Script.\n      <\/div>\n    <\/div>\n    <div class=\"chat-inp-wrap\">\n      <textarea class=\"chat-inp\" id=\"chatInp\" placeholder=\"Ask Director anything...\" onkeydown=\"handleKey(event)\"><\/textarea>\n      <button class=\"chat-send\" onclick=\"sendChat()\">\u2192<\/button>\n    <\/div>\n  <\/div>\n\n<\/div>\n\n<script>\n\/\/ \u2500\u2500 PROVIDERS \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst PROVIDERS = {\n  openrouter:{name:'OpenRouter',endpoint:'https:\/\/openrouter.ai\/api\/v1\/chat\/completions',ph:'sk-or-v1-...',\n    models:[\n      {id:'meta-llama\/llama-3.3-70b-instruct:free',label:'Llama 3.3 70B \u2014 Free'},\n      {id:'deepseek\/deepseek-r1:free',label:'DeepSeek R1 \u2014 Free'},\n      {id:'mistralai\/mistral-7b-instruct:free',label:'Mistral 7B \u2014 Free'},\n      {id:'qwen\/qwen-2.5-72b-instruct:free',label:'Qwen 2.5 72B \u2014 Free'},\n      {id:'google\/gemini-flash-1.5',label:'Gemini Flash 1.5 \u2014 Cheap'},\n      {id:'anthropic\/claude-3.5-sonnet',label:'Claude 3.5 Sonnet'},\n      {id:'openai\/gpt-4o',label:'GPT-4o'},\n      {id:'x-ai\/grok-beta',label:'xAI Grok Beta'},\n    ]},\n  groq:{name:'Groq',endpoint:'https:\/\/api.groq.com\/openai\/v1\/chat\/completions',ph:'gsk_...',\n    models:[\n      {id:'llama-3.3-70b-versatile',label:'Llama 3.3 70B \u2014 Free'},\n      {id:'llama-3.1-8b-instant',label:'Llama 3.1 8B Fast \u2014 Free'},\n      {id:'mixtral-8x7b-32768',label:'Mixtral 8x7B \u2014 Free'},\n      {id:'gemma2-9b-it',label:'Gemma 2 9B \u2014 Free'},\n    ]},\n  anthropic:{name:'Anthropic',endpoint:'https:\/\/api.anthropic.com\/v1\/messages',ph:'sk-ant-...',\n    models:[\n      {id:'claude-sonnet-4-20250514',label:'Claude Sonnet 4'},\n      {id:'claude-3-5-haiku-20241022',label:'Claude 3.5 Haiku \u2014 Fast'},\n      {id:'claude-opus-4-20250514',label:'Claude Opus 4 \u2014 Best'},\n    ]},\n  openai:{name:'OpenAI',endpoint:'https:\/\/api.openai.com\/v1\/chat\/completions',ph:'sk-...',\n    models:[\n      {id:'gpt-4o',label:'GPT-4o'},\n      {id:'gpt-4o-mini',label:'GPT-4o Mini \u2014 Cheaper'},\n      {id:'gpt-4-turbo',label:'GPT-4 Turbo'},\n    ]},\n  gemini:{name:'Google Gemini',endpoint:'https:\/\/generativelanguage.googleapis.com\/v1beta\/models\/{model}:generateContent',ph:'AIza...',\n    models:[\n      {id:'gemini-1.5-flash',label:'Gemini 1.5 Flash \u2014 Fast'},\n      {id:'gemini-1.5-pro',label:'Gemini 1.5 Pro'},\n      {id:'gemini-2.0-flash-exp',label:'Gemini 2.0 Flash'},\n    ]},\n  deepseek:{name:'DeepSeek',endpoint:'https:\/\/api.deepseek.com\/v1\/chat\/completions',ph:'sk-...',\n    models:[\n      {id:'deepseek-chat',label:'DeepSeek V3 Chat'},\n      {id:'deepseek-reasoner',label:'DeepSeek R1 Reasoning'},\n    ]},\n  xai:{name:'xAI Grok',endpoint:'https:\/\/api.x.ai\/v1\/chat\/completions',ph:'xai-...',\n    models:[\n      {id:'grok-beta',label:'Grok Beta'},\n      {id:'grok-2-1212',label:'Grok 2'},\n    ]},\n  mistral:{name:'Mistral',endpoint:'https:\/\/api.mistral.ai\/v1\/chat\/completions',ph:'...',\n    models:[\n      {id:'mistral-small-latest',label:'Mistral Small \u2014 Cheap'},\n      {id:'mistral-large-latest',label:'Mistral Large'},\n      {id:'open-mistral-7b',label:'Mistral 7B Open'},\n    ]},\n};\n\n\/\/ \u2500\u2500 PLATFORMS \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst PLATFORMS = {\n  kling:    {name:'Kling AI',   color:'#a855f7',icon:'\ud83c\udfac',free:true, url:'https:\/\/klingai.com'},\n  pika:     {name:'Pika',       color:'#f97316',icon:'\u26a1',free:true, url:'https:\/\/pika.art'},\n  hailuo:   {name:'HaiLuo AI',  color:'#22c55e',icon:'\ud83d\udc64',free:true, url:'https:\/\/hailuoai.video'},\n  googlevids:{name:'Google Vids',color:'#3b82f6',icon:'\ud83d\udd35',free:true,url:'https:\/\/vids.google.com'},\n  invideo:  {name:'InVideo AI', color:'#f59e0b',icon:'\ud83d\udcf9',free:true, url:'https:\/\/invideo.io'},\n  runway:   {name:'Runway',     color:'#ef4444',icon:'\ud83d\ude80',free:false,url:'https:\/\/runwayml.com'},\n  heygen:   {name:'HeyGen',     color:'#ec4899',icon:'\ud83d\udde3\ufe0f',free:false,url:'https:\/\/www.heygen.com'},\n  synthesia:{name:'Synthesia',  color:'#8b5cf6',icon:'\ud83e\udd16',free:false,url:'https:\/\/www.synthesia.io'},\n};\n\nconst PLATFORM_GUIDANCE = {\n  kling:    'Kling: use motion descriptors (slow zoom, camera pan left), lighting details (golden hour, neon glow), cinematic keywords. Avoid text overlays. Max 10s.',\n  pika:     'Pika: stylized visual descriptions, effect keywords (glitch, particles, bloom), dynamic motion direction. Keep under 4s for best results.',\n  hailuo:   'HaiLuo: focus on human subjects \u2014 facial expressions, body language, emotional state, skin detail, natural movement, face lighting. Very strong on close-ups.',\n  googlevids:'Google Vids\/Veo: cinematic scale, landscape establishing shots, wide angles, epic scope. Highest quality \u2014 use for hero shots only. Very responsive to cinematography language.',\n  invideo:  'InVideo: describe as a full video brief \u2014 topic, visual style needed, stock footage direction, voiceover tone, background music mood, pacing.',\n  runway:   'Runway Gen-4: pipe-separated keywords work best \u2014 subject | action | style | camera movement | lighting | quality tier. Technical cinematography terms get excellent results.',\n  heygen:   'HeyGen: provide exact avatar script in quotes. Then specify: avatar style, background setting, delivery tone. This is talking-head only \u2014 write dialogue, not visual description.',\n  synthesia:'Synthesia: provide exact script text for avatar to speak. Natural conversational sentences. Specify avatar and background separately in the Synthesia UI after pasting.',\n};\n\n\/\/ \u2500\u2500 STATE \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nlet scenes = [];\nlet apiKey = '';\nlet provider = 'openrouter';\n\n\/\/ \u2500\u2500 PROVIDER SETUP \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nfunction updateProviderModels() {\n  provider = document.getElementById('providerSelect').value;\n  const sel = document.getElementById('modelSelect');\n  const models = PROVIDERS[provider].models;\n  sel.innerHTML = models.map(m=>`<option value=\"${m.id}\">${m.label}<\/option>`).join('');\n  document.getElementById('apiKeyInput').placeholder = PROVIDERS[provider].ph || 'Paste API key';\n  const saved = localStorage.getItem('twain_dir_key_'+provider);\n  if (saved) { document.getElementById('apiKeyInput').value=saved; apiKey=saved; setStatus(true); }\n  else { document.getElementById('apiKeyInput').value=''; apiKey=''; setStatus(false); }\n}\n\nfunction saveApiKey() {\n  const k = document.getElementById('apiKeyInput').value.trim();\n  if (!k) { alert('Paste your API key first.'); return; }\n  apiKey = k;\n  localStorage.setItem('twain_dir_key_'+provider, k);\n  setStatus(true);\n  addMsg('director','\u2705 Key saved for '+PROVIDERS[provider].name+'. Stored in your browser only \u2014 never sent to TWAIN servers. Ready to direct.');\n}\n\nfunction setStatus(on) {\n  const el = document.getElementById('apiStatus');\n  el.className = 'api-status '+(on?'on':'off');\n  el.textContent = on ? '\u25cf Connected' : 'No Key';\n}\n\n\/\/ \u2500\u2500 UNIVERSAL AI CALL \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nasync function callAI(sys, usr) {\n  if (!apiKey) {\n    addMsg('director','\u26a0\ufe0f No API key set. Paste your key in the bar above and click Save Key.');\n    return null;\n  }\n  const p = PROVIDERS[provider];\n  const model = document.getElementById('modelSelect').value;\n\n  \/\/ Gemini \u2014 different endpoint and body format\n  if (provider === 'gemini') {\n    const url = p.endpoint.replace('{model}',model)+'?key='+apiKey;\n    const body = {contents:[{parts:[{text:sys+'\\n\\n'+usr}]}]};\n    const res = await fetch(url,{method:'POST',headers:{'Content-Type':'application\/json'},body:JSON.stringify(body)});\n    const d = await res.json();\n    if (d.error) throw new Error(d.error.message);\n    return d.candidates?.[0]?.content?.parts?.[0]?.text || null;\n  }\n\n  \/\/ Anthropic \u2014 different auth header and body\n  if (provider === 'anthropic') {\n    const res = await fetch(p.endpoint,{method:'POST',headers:{'Content-Type':'application\/json','x-api-key':apiKey,'anthropic-version':'2023-06-01'},body:JSON.stringify({model,max_tokens:2000,system:sys,messages:[{role:'user',content:usr}]})});\n    const d = await res.json();\n    if (d.error) throw new Error(d.error.message);\n    return d.content?.[0]?.text || null;\n  }\n\n  \/\/ All others \u2014 OpenAI-compatible (Groq, OpenRouter, OpenAI, DeepSeek, xAI, Mistral)\n  const headers = {'Content-Type':'application\/json','Authorization':'Bearer '+apiKey};\n  if (provider==='openrouter') { headers['HTTP-Referer']='https:\/\/twainstudio.dev'; headers['X-Title']='TWAIN Director'; }\n  const res = await fetch(p.endpoint,{method:'POST',headers,body:JSON.stringify({model,max_tokens:2000,messages:[{role:'system',content:sys},{role:'user',content:usr}]})});\n  const d = await res.json();\n  if (d.error) throw new Error(d.error.message||JSON.stringify(d.error));\n  return d.choices?.[0]?.message?.content || null;\n}\n\n\/\/ \u2500\u2500 SCRIPT GENERATION \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nasync function generateScript() {\n  const format=document.getElementById('vidFormat').value;\n  const tone=document.getElementById('vidTone').value;\n  const length=document.getElementById('vidLength').value;\n  const product=document.getElementById('vidProduct').value||'your project';\n  addMsg('director',`Generating a ${length} ${format} script for \"${product}\"...`);\n  showTyping(); setProgress(30);\n  const sys='You are an elite video director and copywriter specializing in book trailers and brand films. You write cinematic, emotionally powerful, conversion-focused scripts.';\n  const usr=`Write a complete production-ready ${format} script.\nProject: ${product}\nTone: ${tone}\nLength: ${length}\n\nFormat each scene exactly like this:\n[SCENE 1 - HOOK - 3s]\nVISUAL: Detailed description of what the camera sees\nVOICEOVER: Exact words spoken (write NONE if silent)\nDIRECTION: Camera movement, shot type, mood notes\n\nWrite all scenes to fill the full ${length}. Make it cinematic, emotionally gripping, and specifically tailored for ${product}.`;\n  try {\n    const script = await callAI(sys,usr);\n    if (script) {\n      document.getElementById('scriptInput').value=script;\n      updateStats(); removeTyping(); setProgress(100);\n      addMsg('director','\u2705 Script generated. Review it, then click **Analyze & Route Scenes** to assign each scene to the best platform and generate paste-ready prompts.');\n      setTimeout(()=>setProgress(0),1500);\n    } else { removeTyping(); setProgress(0); }\n  } catch(e) { removeTyping(); setProgress(0); addMsg('director','Error: '+e.message+'. Check your API key.'); }\n}\n\n\/\/ \u2500\u2500 ANALYSIS & ROUTING \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nasync function analyzeScript() {\n  const script=document.getElementById('scriptInput').value.trim();\n  if(!script){addMsg('director','Write or generate a script first.');return;}\n  showTyping(); setProgress(20);\n  const sys='You are an expert video director. Extract production scenes from scripts as JSON arrays.';\n  const usr=`Analyze this script and return ONLY a JSON array of scenes. No explanation, no markdown, just the array.\n\nSCRIPT:\n${script}\n\nEach scene object:\n{\n  \"num\": 1,\n  \"type\": \"Hook|Problem|Solution|Demo|Testimonial|CTA|Transition|B-Roll|Presenter|Effect\",\n  \"desc\": \"Detailed visual description of what is seen on screen\",\n  \"voiceover\": \"Exact words spoken, or empty string\",\n  \"duration\": 4,\n  \"mood\": \"Cinematic|Energetic|Calm|Dramatic|Conversational|Dark|Suspenseful\",\n  \"shotType\": \"Wide|Medium|Close-up|Extreme Close-up|Aerial|Product|Motion\"\n}\n\nReturn ONLY the raw JSON array starting with [ and ending with ].`;\n  try {\n    let raw = await callAI(sys,usr);\n    if(!raw){removeTyping();setProgress(0);return;}\n    raw=raw.replace(\/```json|```\/g,'').trim();\n    const start=raw.indexOf('['), end=raw.lastIndexOf(']');\n    if(start===-1||end===-1) throw new Error('No JSON array found');\n    scenes=JSON.parse(raw.slice(start,end+1));\n    scenes=scenes.map(s=>{const r=routeScene(s);return{...s,platform:r.platform,routingReason:r.reason,generatedPrompt:null};});\n    removeTyping(); setProgress(70);\n    renderScenes(); buildBoard(); renderTimeline();\n    setProgress(100);\n    document.getElementById('sceneCount').textContent=scenes.length+' scenes';\n    const total=scenes.reduce((sum,s)=>sum+(s.duration||3),0);\n    const plats=[...new Set(scenes.map(s=>s.platform))];\n    addMsg('director',`\ud83c\udfac Routed **${scenes.length} scenes** (${total}s) across ${plats.length} platforms. Go to **Scene Router** \u2192 click **Generate Prompt** on each scene to get a platform-specific prompt ready to paste.`);\n    setTimeout(()=>setProgress(0),1500);\n    switchTab('router',document.querySelectorAll('.tab-btn')[1]);\n  } catch(e) {\n    removeTyping(); setProgress(0);\n    addMsg('director','Could not parse scenes. Make sure your script has clear [SCENE N] markers and try again. Error: '+e.message);\n  }\n}\n\nfunction routeScene(s) {\n  const t=(s.type||'').toLowerCase(), m=(s.mood||'').toLowerCase();\n  if(t.includes('presenter')||t.includes('talking')||t.includes('testimonial')) return{platform:'heygen',reason:'Best free talking-head presenter'};\n  if(t.includes('demo')||t.includes('product')) return{platform:'invideo',reason:'Auto-assembles demos with stock footage'};\n  if(t.includes('effect')||t.includes('transition')) return{platform:'pika',reason:'Best stylized effects & transitions'};\n  if(t.includes('hook')&&(s.duration||4)<=4) return{platform:'pika',reason:'Short punchy hook \u2014 Pika fastest'};\n  if(m.includes('human')||t.includes('face')||t.includes('character')) return{platform:'hailuo',reason:'Best free platform for human faces'};\n  if((s.duration||4)>=8) return{platform:'googlevids',reason:'Longest clips, highest quality \u2014 hero shot'};\n  if((s.duration||4)<=4) return{platform:'pika',reason:'Short clip \u2014 Pika fastest for 3\u20134s'};\n  if((s.duration||4)<=6) return{platform:'hailuo',reason:'Up to 6s, strong motion quality'};\n  return{platform:'kling',reason:'Best all-around motion quality &#038; consistency'};\n}\n\n\/\/ \u2500\u2500 RENDER SCENES \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nfunction renderScenes() {\n  const c=document.getElementById('sceneContainer');\n  const empty=document.getElementById('routerEmpty');\n  if(scenes.length===0){empty.style.display='flex';return;}\n  empty.style.display='none';\n  c.innerHTML=''; c.appendChild(empty);\n  scenes.forEach((scene,i)=>{\n    const p=PLATFORMS[scene.platform]||PLATFORMS.kling;\n    const div=document.createElement('div');\n    div.className='scene-block'; div.style.setProperty('--pc',p.color);\n    div.innerHTML=`\n      <div class=\"scene-hdr\">\n        <div class=\"scene-num\">${scene.num}<\/div>\n        <div class=\"scene-type\">${scene.type}<\/div>\n        <div class=\"scene-dur\">${scene.duration}s<\/div>\n      <\/div>\n      <div class=\"scene-body\">\n        <div class=\"scene-desc\">${scene.desc}<\/div>\n        ${scene.voiceover?`<div class=\"scene-vo\">\"${scene.voiceover}\"<\/div>`:''}\n        <div class=\"plat-row\">\n          <div class=\"plat-badge\" style=\"color:${p.color};border-color:${p.color}44;\">${p.icon} ${p.name}<\/div>\n          <div class=\"routing-reason\">${scene.routingReason}<\/div>\n          ${p.free?'<span class=\"cost-tag ct-free\">\u2705 No card<\/span>':'<span class=\"cost-tag ct-card\">\ud83d\udcb3 Card req\\'d<\/span>'}\n          <select class=\"reroute-sel\" onchange=\"rerouteScene(${i},this.value)\">\n            ${Object.entries(PLATFORMS).map(([k,v])=>`<option value=\"${k}\" ${k===scene.platform?'selected':''}>${v.icon} ${v.name}<\/option>`).join('')}\n          <\/select>\n        <\/div>\n        <div class=\"prompt-out\" id=\"po-${i}\"><\/div>\n        <div class=\"scene-actions\">\n          <button class=\"bs gen\" onclick=\"generatePrompt(${i})\">\u2726 Generate Prompt<\/button>\n          <button class=\"bs copy\" onclick=\"copyPrompt(${i})\">Copy Prompt<\/button>\n          <button class=\"bs open\" onclick=\"window.open('${p.url}','_blank')\">Open ${p.name} \u2192<\/button>\n        <\/div>\n      <\/div>`;\n    c.appendChild(div);\n  });\n}\n\n\/\/ \u2500\u2500 PROMPT GENERATION \u2014 AI writes platform-specific prompt \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nasync function generatePrompt(i) {\n  const scene=scenes[i]; const p=PLATFORMS[scene.platform];\n  const el=document.getElementById('po-'+i);\n  el.textContent='Generating prompt...'; el.classList.add('vis');\n  const sys=`You are an expert AI video prompt engineer. You know exactly how each AI video platform interprets prompts and what language gets the best results from each one. You write prompts that are immediately ready to paste into the platform \u2014 no explanations, no labels, just the prompt.`;\n  const usr=`Write a production-ready video generation prompt for ${p.name}.\n\nScene details:\n- Visual: ${scene.desc}\n- Voiceover: ${scene.voiceover||'None'}\n- Type: ${scene.type}\n- Duration: ${scene.duration} seconds\n- Mood: ${scene.mood||'Cinematic'}\n- Shot: ${scene.shotType||'Medium'}\n\nPlatform guidance for ${p.name}:\n${PLATFORM_GUIDANCE[scene.platform]}\n\nWrite ONLY the prompt. No intro, no label, no explanation. Just the prompt text ready to paste into ${p.name}.`;\n  try {\n    const prompt=await callAI(sys,usr);\n    if(prompt) {\n      scenes[i].generatedPrompt=prompt.trim();\n      el.textContent=scenes[i].generatedPrompt;\n      addMsg('director',`\u2705 Prompt ready for Scene ${scene.num} \u2192 ${p.name}. Click **Copy Prompt** to grab it, then open the platform and paste.`);\n    } else { el.textContent='Generation failed. Check your API key.'; }\n  } catch(e) { el.textContent='Error: '+e.message; }\n}\n\nfunction copyPrompt(i) {\n  const scene=scenes[i]; const p=PLATFORMS[scene.platform];\n  const prompt=scene.generatedPrompt||`${scene.desc}. ${scene.mood||'Cinematic'} mood. ${scene.shotType||'Medium shot'}. ${scene.duration} seconds.`;\n  navigator.clipboard.writeText(prompt);\n  addMsg('director',`\u2705 Copied prompt for Scene ${scene.num}. Paste directly into ${p.name}.`);\n}\n\nfunction rerouteScene(i,platform) {\n  scenes[i].platform=platform; scenes[i].routingReason='Manually reassigned'; scenes[i].generatedPrompt=null;\n  renderScenes(); buildBoard(); renderTimeline();\n}\n\nfunction addManualScene() {\n  scenes.push({num:scenes.length+1,type:'General',desc:'Describe what you see on screen',voiceover:'',duration:4,mood:'Cinematic',shotType:'Medium',platform:'kling',routingReason:'Default',generatedPrompt:null});\n  renderScenes(); buildBoard(); renderTimeline();\n  document.getElementById('sceneCount').textContent=scenes.length+' scenes';\n}\n\n\/\/ \u2500\u2500 STORYBOARD \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nfunction buildBoard() {\n  const g=document.getElementById('boardGrid'); g.innerHTML='';\n  if(scenes.length===0){g.innerHTML='<div class=\"empty-state\" style=\"grid-column:1\/-1\"><div class=\"empty-icon\">\ud83d\uddbc\ufe0f<\/div><div class=\"empty-msg\">No scenes yet<\/div><\/div>';return;}\n  const icons={Hook:'\ud83c\udfaf',Problem:'\ud83d\ude24',Solution:'\u2728',Demo:'\ud83d\udcf1',Testimonial:'\ud83d\udc64',CTA:'\ud83d\ude80',Transition:'\u27a1\ufe0f','B-Roll':'\ud83c\udfac',Presenter:'\ud83d\udde3\ufe0f',Effect:'\u26a1',General:'\ud83c\udfac'};\n  scenes.forEach((s,i)=>{\n    const p=PLATFORMS[s.platform]||PLATFORMS.kling;\n    const card=document.createElement('div'); card.className='board-card';\n    card.onclick=()=>switchTab('router',document.querySelectorAll('.tab-btn')[1]);\n    card.innerHTML=`<div class=\"board-frame\" style=\"background:linear-gradient(135deg,${p.color}18,${p.color}38)\"><span style=\"font-size:28px\">${icons[s.type]||'\ud83c\udfac'}<\/span><div class=\"board-badge\" style=\"color:${p.color}\">${p.icon} ${p.name.split(' ')[0]}<\/div><\/div><div class=\"board-info\"><div class=\"board-title\">Scene ${s.num} \u00b7 ${s.duration}s \u00b7 ${s.type}<\/div><div class=\"board-desc\">${(s.desc||'').substring(0,65)}${(s.desc||'').length>65?'...':''}<\/div><\/div>`;\n    g.appendChild(card);\n  });\n}\n\n\/\/ \u2500\u2500 TIMELINE \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nfunction renderTimeline() {\n  const track=document.getElementById('tlTrack'); track.innerHTML='';\n  if(scenes.length===0){track.innerHTML='<div class=\"empty-state\"><div class=\"empty-icon\">\u23f1\ufe0f<\/div><div class=\"empty-msg\">No scenes yet<\/div><\/div>';return;}\n  const total=scenes.reduce((sum,s)=>sum+(s.duration||3),0);\n  const hdr=document.createElement('div'); hdr.className='tl-row';\n  hdr.innerHTML=`<div class=\"tl-plat\" style=\"font-family:'JetBrains Mono',monospace;font-size:8px;color:var(--muted);\">Total: ${total}s<\/div><div style=\"flex:1;height:2px;background:var(--border);border-radius:2px;\"><\/div>`;\n  track.appendChild(hdr);\n  scenes.forEach(s=>{\n    const p=PLATFORMS[s.platform]||PLATFORMS.kling;\n    const pct=((s.duration||3)\/total*100).toFixed(1);\n    const row=document.createElement('div'); row.className='tl-row';\n    row.innerHTML=`<div class=\"tl-plat\">${p.icon} ${p.name}<\/div><div style=\"flex:1;\"><div class=\"tl-bar\" style=\"width:${pct}%;background:${p.color}cc;max-width:100%;\" title=\"Scene ${s.num}: ${s.desc}\">S${s.num} \u00b7 ${s.duration}s<\/div><\/div><div class=\"tl-dur\">${s.duration}s<\/div>`;\n    track.appendChild(row);\n  });\n}\n\n\/\/ \u2500\u2500 EXPORT PRODUCTION PLAN \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nfunction exportPlan() {\n  if(scenes.length===0){addMsg('director','No scenes to export. Analyze a script first.');return;}\n  const total=scenes.reduce((sum,s)=>sum+(s.duration||3),0);\n  let out='TWAIN DIRECTOR \u2014 PRODUCTION PLAN\\n';\n  out+='Generated: '+new Date().toLocaleDateString()+'\\n';\n  out+='\u26a0 Free tier status verified April 2026 \u2014 always verify current pricing at each platform before generating\\n';\n  out+='='.repeat(60)+'\\n\\n';\n  out+=`Scenes: ${scenes.length}  |  Total duration: ${total}s\\n\\n`;\n  scenes.forEach(s=>{\n    const p=PLATFORMS[s.platform];\n    out+=`SCENE ${s.num} \u2014 ${s.type} (${s.duration}s)\\n`;\n    out+=`Platform: ${p.name} ${!p.free?'\u26a0 REQUIRES CREDIT CARD':'\u2705 No card required'}\\n`;\n    out+=`Open: ${p.url}\\n`;\n    out+=`Visual: ${s.desc}\\n`;\n    if(s.voiceover) out+=`Voiceover: \"${s.voiceover}\"\\n`;\n    out+=`Prompt:\\n${s.generatedPrompt||'(Click Generate Prompt in the Scene Router to generate)'}\\n`;\n    out+='-'.repeat(40)+'\\n\\n';\n  });\n  const a=document.createElement('a');\n  a.href=URL.createObjectURL(new Blob([out],{type:'text\/plain'}));\n  a.download='TWAIN_Director_Production_Plan.txt'; a.click();\n  addMsg('director','\u2705 Production plan exported as a .txt file \u2014 use it as your production checklist.');\n}\n\n\/\/ \u2500\u2500 CHAT \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst DIR_SYS=`You are DIRECTOR, an elite AI Cinematographer for TWAIN Creation Studios. You help authors create book trailers and promotional videos.\n\nPlatform knowledge:\n- Kling AI: free, no card, ~66 credits\/day, up to 10s, best for smooth cinematic motion\n- Pika: free, no card, daily credits, 3-4s, best for stylized social ads  \n- HaiLuo AI: free, no card, several\/day, up to 6s, best for human faces and movement\n- Google Vids (Veo): free, no card, 10\/month, 8s, highest quality \u2014 use sparingly for hero shots\n- InVideo: free, no card, 10min AI\/week, auto-assembles full videos with stock footage\n- Runway: REQUIRES CREDIT CARD, 125 one-time credits, industry-leading quality\n- HeyGen: REQUIRES CREDIT CARD, 3 videos\/month, best talking-head AI presenter\n- Synthesia: REQUIRES CREDIT CARD, 10min\/month, enterprise avatar quality\n\nIMPORTANT: Always warn users that free tiers change without notice. Always flag when a platform requires a credit card. Users may be billed by their AI provider AND the video platform separately \u2014 this is a double billing risk. Be direct and cinematic. Give specific, actionable advice.`;\n\nasync function sendChat() {\n  const inp=document.getElementById('chatInp');\n  const msg=inp.value.trim(); if(!msg)return;\n  inp.value=''; addMsg('user',msg); showTyping();\n  try {\n    const reply=await callAI(DIR_SYS,msg);\n    removeTyping();\n    if(reply) addMsg('director',reply);\n    else addMsg('director','No response. Check your API key and provider selection.');\n  } catch(e) { removeTyping(); addMsg('director','Connection error: '+e.message); }\n}\n\nfunction handleKey(e){if(e.key==='Enter'&&!e.shiftKey){e.preventDefault();sendChat();}}\n\nfunction addMsg(role,text){\n  const c=document.getElementById('chatMsgs');\n  const d=document.createElement('div'); d.className='msg msg-'+role;\n  const label=role==='director'?'Director':'You';\n  d.innerHTML=`<div class=\"msg-label\">${label}<\/div>${text.replace(\/\\*\\*(.*?)\\*\\*\/g,'<strong>$1<\/strong>').replace(\/\\n\/g,'<br>')}`;\n  c.appendChild(d); c.scrollTop=c.scrollHeight;\n}\n\nfunction showTyping(){\n  const c=document.getElementById('chatMsgs');\n  const d=document.createElement('div'); d.id='typing'; d.className='msg msg-director typing';\n  d.innerHTML='<div class=\"dot\"><\/div><div class=\"dot\"><\/div><div class=\"dot\"><\/div>';\n  c.appendChild(d); c.scrollTop=c.scrollHeight;\n}\nfunction removeTyping(){const el=document.getElementById('typing');if(el)el.remove();}\n\n\/\/ \u2500\u2500 UTILITY \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nfunction switchTab(id,btn){\n  document.querySelectorAll('.tab-content').forEach(t=>t.classList.remove('active'));\n  document.querySelectorAll('.tab-btn').forEach(b=>b.classList.remove('active'));\n  document.getElementById('tab-'+id).classList.add('active');\n  if(btn)btn.classList.add('active');\n  if(id==='storyboard')buildBoard();\n  if(id==='timeline')renderTimeline();\n}\n\nfunction setProgress(pct){document.getElementById('progressFill').style.width=pct+'%';}\n\nfunction selectPlatform(key,el){\n  document.querySelectorAll('.pc').forEach(c=>c.classList.remove('sel'));\n  el.classList.add('sel');\n  const p=PLATFORMS[key];\n  addMsg('director',`**${p.name}**\\n\\n${!p.free?'\u26a0\ufe0f REQUIRES A CREDIT CARD at signup.\\n\\n':'\u2705 No credit card required.\\n\\n'}${PLATFORM_GUIDANCE[key]}`);\n}\n\nfunction updateStats(){\n  const t=document.getElementById('scriptInput').value;\n  const w=t.trim()?t.trim().split(\/\\s+\/).length:0;\n  const m=t.match(\/\\[SCENE\/gi);\n  const e=m?m.length:Math.ceil(w\/50);\n  document.getElementById('scriptStats').textContent=`${w} words \u00b7 ~${e} scenes estimated`;\n}\n\nfunction clearScript(){\n  document.getElementById('scriptInput').value=''; updateStats();\n  scenes=[]; renderScenes(); buildBoard(); renderTimeline();\n  document.getElementById('sceneCount').textContent='0 scenes';\n}\n\ndocument.getElementById('scriptInput').addEventListener('input',updateStats);\n\n\/\/ \u2500\u2500 INIT \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nupdateProviderModels();\n<\/script>\n<\/body>\n<\/html>\n<\/div>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n","protected":false},"excerpt":{"rendered":"<p>DIRECTOR \u2014 The AI Cinematography Studio That Costs You Nothing Most people think professional video production requires a budget. DIRECTOR proves them wrong. DIRECTOR is the first AI-powered video production studio that doesn&#8217;t just help you write scripts \u2014 it thinks like a real cinematographer. It analyzes your content, breaks it into scenes, and intelligently [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"_themeisle_gutenberg_block_has_review":false,"footnotes":""},"class_list":["post-14","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/twainstudio.dev\/index.php?rest_route=\/wp\/v2\/pages\/14","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/twainstudio.dev\/index.php?rest_route=\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/twainstudio.dev\/index.php?rest_route=\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/twainstudio.dev\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/twainstudio.dev\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=14"}],"version-history":[{"count":4,"href":"https:\/\/twainstudio.dev\/index.php?rest_route=\/wp\/v2\/pages\/14\/revisions"}],"predecessor-version":[{"id":35,"href":"https:\/\/twainstudio.dev\/index.php?rest_route=\/wp\/v2\/pages\/14\/revisions\/35"}],"wp:attachment":[{"href":"https:\/\/twainstudio.dev\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=14"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}