<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Phantomline Studio</title>
<meta name="description" content="Phantomline Studio: the workspace for generating faceless YouTube videos with local AI. Script, voice, scenes, music, captions, MP4, scheduler, all in one workflow." />
<!-- Auth gate. Synchronous so it can redirect anonymous visitors to
     /account before any studio HTML/CSS paints. Skips on localhost (the
     desktop install) and inside the Capacitor Android shell. See
     static/auth-gate.js for the full rationale. -->
<script src="{{ '/static/auth-gate.js' | versioned }}"></script>
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
<meta name="theme-color" content="#090b0c" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<meta name="apple-mobile-web-app-title" content="Phantomline" />
<meta name="mobile-web-app-capable" content="yes" />
<!-- The studio is a workspace, not a content page. Tell crawlers to skip
     it so SEO authority concentrates on /, /pricing, and future content
     pages. Without this Google would try to index the empty workspace
     and dilute our ranking signals. -->
<meta name="robots" content="noindex, nofollow" />
<meta name="supabase-url" content="{{ supabase_url }}" />
<meta name="supabase-anon-key" content="{{ supabase_anon_key }}" />
<link rel="canonical" href="{{ canonical_url }}" />
<link rel="manifest" href="{{ '/static/manifest.json' | versioned }}" />
<link rel="icon" type="image/png" href="{{ '/static/phantomline-favicon.png' | versioned }}" />
<link rel="apple-touch-icon" href="{{ '/static/phantomline-logo-square.png' | versioned }}" />
<link rel="preconnect" href="https://unpkg.com" />
<link rel="preconnect" href="https://cdn.jsdelivr.net" />
<script src="https://unpkg.com/wavesurfer.js@7/dist/wavesurfer.min.js" defer></script>
<script src="{{ '/static/engines.js' | versioned }}" defer></script>
<link rel="stylesheet" href="{{ '/static/phantomline.css' | versioned }}" />
</head>
<body>
<!-- Mobile interstitial. Phantomline studio is desktop-first.
     On phones the studio layout collapses awkwardly; the render
     pipeline (TTS, video assembly, image gen) needs hardware that
     phones don't have. Rather than dropping mobile users into a
     broken /app, show an honest banner explaining what they CAN do
     here. Same detection pattern as templates/install.html:96-98.
     The .pl-mobile-only display is hidden on desktop; on mobile it's
     a full-page card BEFORE the studio so the layout itself never
     paints. Toggleable via the "Continue anyway" link for the rare
     desktop-class tablet user. -->
<div id="mobileInterstitial" class="pl-mobile-only" hidden>
  <div class="pl-mobile-card">
    <div class="pl-mobile-eyebrow">Phantomline studio</div>
    <h1 class="pl-mobile-h1">Built for the desktop.</h1>
    <p class="pl-mobile-lede">
      Rendering needs a real GPU and a long-running process. Your phone can't
      run that without melting. Open Phantomline on your computer to create.
    </p>
    <div class="pl-mobile-list">
      <div class="pl-mobile-list-head">From your phone right now you can:</div>
      <a class="pl-mobile-link" href="/account">› Sign in &amp; manage your license</a>
      <a class="pl-mobile-link" href="/account">› View your project library</a>
      <a class="pl-mobile-link" href="/pricing">› Compare plans &amp; upgrade</a>
      <a class="pl-mobile-link" href="/download">› Get the desktop install link</a>
    </div>
    <a class="pl-mobile-cta" href="/download">Open install guide →</a>
    <button id="mobileBypassBtn" class="pl-mobile-bypass" type="button">
      Continue to studio anyway
    </button>
  </div>
</div>
<style>
  .pl-mobile-only { display: none; }
  @media (max-width: 720px) {
    .pl-mobile-only { display: block; }
    .pl-mobile-only:not([hidden]) ~ .wrap { display: none; }
    html.pl-mobile-bypass-on .pl-mobile-only ~ .wrap { display: grid; }
  }
  .pl-mobile-card {
    min-height: 100vh;
    padding: 64px 24px 48px;
    display: flex;
    flex-direction: column;
    justify-content: center;
    max-width: 540px;
    margin: 0 auto;
  }
  .pl-mobile-eyebrow {
    font-family: "Geist Mono", ui-monospace, monospace;
    font-size: 12px;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: #22E7F5;
    margin-bottom: 24px;
    display: inline-flex;
    align-items: center;
    gap: 10px;
  }
  .pl-mobile-eyebrow::before {
    content: ""; width: 24px; height: 1px; background: #22E7F5;
  }
  .pl-mobile-h1 {
    font-family: "Geist", system-ui, sans-serif;
    font-size: clamp(40px, 11vw, 64px);
    font-weight: 600;
    line-height: 0.96;
    letter-spacing: -0.025em;
    color: #E6EAEC;
    margin: 0 0 20px 0;
  }
  .pl-mobile-lede {
    font-family: "Geist", system-ui, sans-serif;
    font-size: 17px;
    line-height: 1.55;
    color: #94A3B0;
    margin: 0 0 36px 0;
  }
  .pl-mobile-list {
    border-top: 1px solid rgba(230, 234, 236,0.14);
    border-bottom: 1px solid rgba(230, 234, 236,0.14);
    padding: 20px 0;
    margin-bottom: 32px;
  }
  .pl-mobile-list-head {
    font-family: "Geist Mono", ui-monospace, monospace;
    font-size: 11px;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: #8c8a82;
    margin-bottom: 14px;
  }
  .pl-mobile-link {
    display: block;
    padding: 10px 0;
    color: #E6EAEC;
    font-family: "Geist", system-ui, sans-serif;
    font-size: 16px;
    text-decoration: none;
    border-bottom: 1px solid rgba(230, 234, 236,0.08);
  }
  .pl-mobile-link:last-child { border-bottom: 0; }
  .pl-mobile-link:hover, .pl-mobile-link:active { color: #22E7F5; }
  .pl-mobile-cta {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    min-height: 48px;
    padding: 0 24px;
    background: #22E7F5;
    color: #0B0F11;
    font-family: "Geist Mono", ui-monospace, monospace;
    font-size: 13px;
    font-weight: 600;
    text-decoration: none;
    text-transform: lowercase;
    letter-spacing: 0.02em;
    border: 1px solid #22E7F5;
    border-radius: 0;
  }
  .pl-mobile-cta:hover, .pl-mobile-cta:active { background: #0AA7B5; border-color: #0AA7B5; color: #E6EAEC; }
  .pl-mobile-bypass {
    background: transparent;
    border: 0;
    color: #5e5d57;
    font-family: "Geist Mono", ui-monospace, monospace;
    font-size: 12px;
    letter-spacing: 0.04em;
    margin-top: 24px;
    padding: 8px 0;
    cursor: pointer;
    text-align: left;
    text-decoration: underline;
  }
  .pl-mobile-bypass:hover, .pl-mobile-bypass:active { color: #8c8a82; }
</style>
<script src="{{ '/static/mobile-gate.js' | versioned }}"></script>

<!-- Onboarding overlay. Full-page flow shown once to new users.
     Three paths: existing channel, new creator, explorer.
     Controlled by localStorage 'phantomline-onboarding-complete'.
     Replayable from Settings. -->
<div id="onboardingOverlay" class="ob-overlay" hidden>
  <div class="ob-container">
    <div class="ob-progress" id="obProgress">
      <div class="ob-progress-fill" id="obProgressFill"></div>
    </div>

    <!-- Screen 1: Intent -->
    <div class="ob-screen active" data-ob-screen="intent">
      <div class="ob-eyebrow">Welcome to Phantomline</div>
      <h2 class="ob-heading">What brings you here?</h2>
      <p class="ob-subtext">This helps us tailor the studio to your workflow.</p>
      <div class="ob-cards">
        <button class="ob-card" data-ob-path="existing" type="button">
          <svg class="ob-card-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg>
          <strong class="ob-card-title">I have a YouTube channel</strong>
          <span class="ob-card-desc">Plug in your channel and start creating</span>
        </button>
        <button class="ob-card" data-ob-path="new" type="button">
          <svg class="ob-card-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/></svg>
          <strong class="ob-card-title">I want to start a channel</strong>
          <span class="ob-card-desc">We'll help you pick a niche and get set up</span>
        </button>
        <button class="ob-card" data-ob-path="explorer" type="button">
          <svg class="ob-card-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polygon points="16.24 7.76 14.12 14.12 7.76 16.24 9.88 9.88 16.24 7.76"/></svg>
          <strong class="ob-card-title">Just exploring</strong>
          <span class="ob-card-desc">Jump in and look around</span>
        </button>
      </div>
    </div>

    <!-- Screen A2: Connect YouTube (existing channel path) -->
    <div class="ob-screen" data-ob-screen="connect">
      <button class="ob-back" type="button" data-ob-goto="intent" aria-label="Go back">← Back</button>
      <div class="ob-eyebrow">Existing channel</div>
      <h2 class="ob-heading">Let's plug into your channel</h2>
      <p class="ob-subtext">We'll pull your niche, recent topics, and audience data to personalize every suggestion.</p>
      <div class="ob-connect-area">
        <button id="obConnectYouTubeBtn" class="ob-cta" type="button">
          <svg class="ob-cta-icon" viewBox="0 0 24 24" fill="currentColor"><path d="M23.498 6.186a3.016 3.016 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z"/></svg>
          Connect YouTube
        </button>
        <div id="obConnectStatus" class="ob-connect-status" hidden>
          <div class="ob-channel-card" id="obChannelCard">
            <img id="obChannelAvatar" class="ob-channel-avatar" src="" alt="" />
            <div>
              <strong id="obChannelName"></strong>
              <span id="obChannelSubs" class="ob-channel-subs"></span>
            </div>
          </div>
        </div>
      </div>
      <button id="obConnectSkip" class="ob-skip" type="button">Skip for now — I'll connect later</button>
      <button id="obConnectNext" class="ob-cta ob-cta-next" type="button" hidden>Continue →</button>
    </div>

    <!-- Screen 2b: Pick niche (shared by new + existing-skip paths) -->
    <div class="ob-screen" data-ob-screen="niche">
      <button class="ob-back" type="button" data-ob-goto="intent" aria-label="Go back">← Back</button>
      <div class="ob-eyebrow">New channel</div>
      <h2 class="ob-heading">What kind of videos do you want to make?</h2>
      <p class="ob-subtext">Pick a niche — you can always change this later.</p>
      <div class="ob-niche-grid">
        <button class="ob-niche-card" data-ob-niche="tech" type="button"><span class="ob-niche-emoji">⚡</span>Tech / AI</button>
        <button class="ob-niche-card" data-ob-niche="finance" type="button"><span class="ob-niche-emoji">💰</span>Finance</button>
        <button class="ob-niche-card" data-ob-niche="science" type="button"><span class="ob-niche-emoji">🔬</span>Science</button>
        <button class="ob-niche-card" data-ob-niche="history" type="button"><span class="ob-niche-emoji">📜</span>History</button>
        <button class="ob-niche-card" data-ob-niche="true-crime" type="button"><span class="ob-niche-emoji">🔍</span>True Crime</button>
        <button class="ob-niche-card" data-ob-niche="motivational" type="button"><span class="ob-niche-emoji">🔥</span>Motivation</button>
        <button class="ob-niche-card" data-ob-niche="scary-story" type="button"><span class="ob-niche-emoji">👻</span>Horror / Mystery</button>
        <button class="ob-niche-card" data-ob-niche="travel" type="button"><span class="ob-niche-emoji">🌎</span>Travel</button>
        <button class="ob-niche-card" data-ob-niche="kids-edu" type="button"><span class="ob-niche-emoji">🎓</span>Kids / Education</button>
        <button class="ob-niche-card" data-ob-niche="philosophy" type="button"><span class="ob-niche-emoji">🧠</span>Philosophy</button>
        <button class="ob-niche-card" data-ob-niche="reddit-story" type="button"><span class="ob-niche-emoji">📖</span>Reddit Stories</button>
        <button class="ob-niche-card" data-ob-niche="custom" data-ob-custom type="button"><span class="ob-niche-emoji">✏️</span>Something else</button>
      </div>
      <div id="obCustomNicheWrap" class="ob-custom-niche" hidden>
        <input id="obCustomNicheInput" class="ob-niche-input" type="text" placeholder="Describe your niche..." />
        <button id="obCustomNicheConfirm" class="ob-cta" type="button">Continue →</button>
      </div>
    </div>

    <!-- Screen 3: Example video showcase -->
    <div class="ob-screen" data-ob-screen="generating">
      <div class="ob-eyebrow" id="obShowcaseEyebrow">Here's what you can create</div>
      <h2 class="ob-heading" id="obShowcaseHeading">This is a Phantomline video</h2>
      <p class="ob-subtext" id="obShowcaseSubtext">Every part — script, voice, music, captions, visuals — was generated by AI on a local machine.</p>
      <div class="ob-showcase-layout">
        <div class="ob-phone-wrap">
          <div class="ob-phone-frame">
            <video class="ob-phone-video" id="obShowcaseVideo"
                   autoplay muted loop playsinline preload="metadata"
                   aria-label="Example Phantomline video"></video>
            <div class="ob-phone-top">
              <span class="ob-phone-time">9:41</span>
              <span class="ob-phone-system">
                <svg viewBox="0 0 16 10" width="14" height="9" fill="currentColor" aria-hidden="true"><rect x="0" y="6" width="3" height="4"/><rect x="4" y="4" width="3" height="6"/><rect x="8" y="2" width="3" height="8"/><rect x="12" y="0" width="3" height="10"/></svg>
                <svg viewBox="0 0 16 12" width="13" height="10" fill="none" stroke="currentColor" stroke-width="1.5" aria-hidden="true"><path d="M2 5a9 9 0 0 1 12 0M4.5 7.5a5 5 0 0 1 7 0M7 10a1.5 1.5 0 0 1 2 0"/></svg>
                <svg viewBox="0 0 22 10" width="18" height="8" fill="none" stroke="currentColor" stroke-width="1" aria-hidden="true"><rect x="0.5" y="0.5" width="18" height="9" rx="2"/><rect x="2" y="2" width="14" height="6" rx="1" fill="currentColor"/><rect x="19.5" y="3" width="2" height="4" rx="0.5" fill="currentColor"/></svg>
              </span>
            </div>
            <div class="ob-phone-topactions" aria-hidden="true">
              <svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><circle cx="11" cy="11" r="7"/><line x1="21" y1="21" x2="16.5" y2="16.5"/></svg>
              <svg viewBox="0 0 24 24" width="16" height="16" fill="currentColor"><circle cx="12" cy="5" r="1.6"/><circle cx="12" cy="12" r="1.6"/><circle cx="12" cy="19" r="1.6"/></svg>
            </div>
            <div class="ob-phone-rail">
              <div class="ob-phone-action"><svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor" aria-hidden="true"><path d="M14 9V5a3 3 0 0 0-3-3l-4 9v11h11.28a2 2 0 0 0 2-1.7l1.38-9A2 2 0 0 0 19.7 9zM7 22H4a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2h3"/></svg><span>12K</span></div>
              <div class="ob-phone-action"><svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor" aria-hidden="true"><path d="M21 11.5a8.38 8.38 0 0 1-9 8.38 8.5 8.5 0 0 1-3.8-.9L3 21l1.9-5.2A8.5 8.5 0 0 1 4 12 8.38 8.38 0 0 1 12.5 3.5 8.38 8.38 0 0 1 21 11.5z"/></svg><span>847</span></div>
              <div class="ob-phone-action"><svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor" aria-hidden="true"><path d="M2 21l21-9L2 3v7l15 2-15 2z"/></svg><span>Share</span></div>
              <div class="ob-phone-yt"><svg viewBox="0 0 28 20" width="22" height="16"><rect width="28" height="20" rx="5" fill="#ff0033"/><polygon points="11,5 11,15 19,10" fill="#fff"/></svg></div>
            </div>
            <div class="ob-phone-bottom">
              <span class="ob-phone-creator">
                <span class="ob-phone-avatar"></span>
                <strong id="obShowcaseCreator">@your_channel</strong>
              </span>
              <span class="ob-phone-desc" id="obShowcaseDesc">Made with Phantomline</span>
            </div>
          </div>
        </div>
        <div class="ob-showcase-info">
          <div class="ob-gen-steps">
            <div class="ob-gen-step done" data-ob-gen="script"><span class="ob-gen-dot"></span>Script written by AI</div>
            <div class="ob-gen-step done" data-ob-gen="narration"><span class="ob-gen-dot"></span>Voice generated locally</div>
            <div class="ob-gen-step done" data-ob-gen="visuals"><span class="ob-gen-dot"></span>Visuals created by AI</div>
            <div class="ob-gen-step done" data-ob-gen="music"><span class="ob-gen-dot"></span>Music composed by AI</div>
            <div class="ob-gen-step done" data-ob-gen="render"><span class="ob-gen-dot"></span>Rendered as MP4</div>
          </div>
          <div class="ob-gen-status" id="obGenStatus">100% generated on a local machine — no cloud, no APIs.</div>
          <button id="obShowcaseGo" class="ob-cta" type="button">Start creating →</button>
        </div>
      </div>
    </div>
  </div>
</div>

<a class="skip-to-main" href="#main" style="position:absolute;top:-40px;left:0;background:#22E7F5;color:#0B0F11;font-family:'Geist Mono',ui-monospace,monospace;font-size:13px;font-weight:600;padding:10px 16px;text-decoration:none;letter-spacing:0.02em;text-transform:lowercase;z-index:9999;transition:top 120ms ease;" onfocus="this.style.top='0'" onblur="this.style.top='-40px'">Skip to main content</a>
<div class="wrap">
  <!-- Visually-hidden h1 for screen readers + SEO. The studio's visible
       hierarchy is brand mark → tabs → tab content (h2s), so we don't
       want a big "Phantomline Studio" headline painting on desktop.
       The mobile interstitial above this in DOM also has its own h1,
       but its parent has display:none on desktop. modern screen
       readers + crawlers correctly skip display:none subtrees. -->
  <h1 class="visually-hidden">Phantomline Studio</h1>
  <!-- Install reminder banner. Sticky at the top of the studio when the
       readiness check finds required blockers (Ollama not running,
       Python missing, etc). Hidden by default; install-banner.js polls
       /api/launch/readiness and reveals the banner when blockers exist.
       Click "Open Launch Setup" jumps to the Launch tab where the full
       checklist + actionable install links live. -->
  <div id="installBanner" class="install-banner" role="status" hidden>
    <div class="install-banner-inner">
      <span class="install-banner-icon" aria-hidden="true">⚠</span>
      <div class="install-banner-msg">
        <strong id="installBannerTitle">Setup needed</strong>
        <span id="installBannerDetail">Some required tools aren't installed yet.</span>
      </div>
      <button id="installBannerOpen" type="button" class="btn">Open Launch Setup →</button>
      <button id="installBannerDismiss" type="button" class="install-banner-x" aria-label="Dismiss">×</button>
    </div>
  </div>
  <header>
    <a class="brand" href="/" title="Phantomline home" aria-label="Phantomline home">
      <img class="brand-img" src="{{ '/static/phantomline-logo-primary.png' | versioned }}" alt="Phantomline" />
    </a>
    <div class="header-actions">
      <!-- Cloud-sync status dot. Updated by static/sync-status.js. States:
           grey = sync not configured (no Supabase env vars)
           green = last write succeeded
           amber = transient error, retry pending
           red = persistent failure -->
      <span id="syncStatusDot" class="sync-status sync-status--unknown"
            title="Cloud sync status" aria-label="Cloud sync status: checking">
        <span class="sync-status-dot" aria-hidden="true"></span>
        <span class="sync-status-text">checking</span>
      </span>
      <div class="notification-wrap">
        <button id="notificationBell" class="notification-button" type="button"
                title="Notifications" aria-label="Notifications"
                aria-controls="notificationPanel" aria-expanded="false" aria-haspopup="true">
          <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M18 8a6 6 0 0 0-12 0c0 7-3 7-3 9h18c0-2-3-2-3-9"/><path d="M10 21h4"/></svg>
          <span id="notificationCount" class="notification-count" aria-label="0 unread notifications">0</span>
        </button>
        <div id="notificationPanel" class="notification-panel" role="dialog" aria-label="Notifications" aria-modal="false">
          <div class="notification-head">
            <strong>Notifications</strong>
            <div class="notification-actions">
              <button id="enableDesktopNotificationsBtn" type="button">Desktop</button>
              <button id="markNotificationsReadBtn" type="button">Mark read</button>
            </div>
          </div>
          <div id="notificationList" class="notification-list"></div>
        </div>
      </div>
      <div class="mode-toggle-wrap">
        <button id="ollamaBadge" class="ollama-badge mode-toggle-btn" type="button" aria-haspopup="menu" aria-expanded="false">
          <span class="pulse"></span>
          <span id="ollamaText">checking…</span>
          <span class="mode-toggle-caret" aria-hidden="true">▾</span>
        </button>
        <div id="modeToggleMenu" class="mode-toggle-menu" hidden>
          <div class="mode-toggle-row" data-mode-row="browser">
            <strong>Browser mode</strong>
            <span class="mode-toggle-meta">In-tab AI · WebGPU · ffmpeg.wasm</span>
          </div>
          <a class="mode-toggle-row" id="modeToggleLocal" href="http://localhost:5000/app">
            <strong>Local Phantomline <span id="modeToggleLocalStatus" class="mode-toggle-status">checking…</span></strong>
            <span class="mode-toggle-meta">Your Ollama, Kokoro, Forge · faster, premium quality</span>
            <span class="mode-toggle-meta mode-toggle-hint" id="modeToggleHint" hidden>Make sure Phantomline desktop is running on your computer.</span>
          </a>
          <a class="mode-toggle-row mode-toggle-download" href="/download">
            <strong>Don't have it yet?</strong>
            <span class="mode-toggle-meta">Download Phantomline desktop →</span>
          </a>
        </div>
      </div>
      <a id="quotaWidget" class="quota-widget" href="/pricing" style="display:none;" title="Renders used this month">
        <span id="quotaUsed">--</span><span class="quota-sep">/</span><span id="quotaLimit">--</span>
        <span class="quota-label">renders</span>
        <span id="quotaUpgrade" class="quota-upgrade" hidden>Upgrade →</span>
      </a>
      <div class="profile-wrap" id="profileWrap" style="display:none;">
        <button id="profileBtn" class="profile-btn" type="button" title="Account">
          <img id="profileAvatar" class="profile-avatar" alt="" />
          <span id="profileInitials" class="profile-initials">·</span>
          <span id="profileName" class="profile-name">Account</span>
        </button>
        <div id="profileMenu" class="profile-menu" hidden>
          <div class="profile-menu-head">
            <img id="profileMenuAvatar" class="profile-menu-avatar" alt="" />
            <span id="profileMenuInitials" class="profile-menu-initials">·</span>
            <div class="profile-menu-info">
              <strong id="profileMenuName">Creator</strong>
              <span id="profileMenuEmail"></span>
            </div>
          </div>
          <label class="profile-menu-row" for="profileAvatarInput">
            <span>Change avatar</span>
            <input id="profileAvatarInput" type="file" accept="image/png,image/jpeg,image/webp,image/gif" hidden />
          </label>
          <button class="profile-menu-row" id="profileEditNameBtn" type="button">Change display name</button>
          <a class="profile-menu-row" href="/account">Manage account &amp; billing</a>
          <button class="profile-menu-row danger" id="profileSignOutBtn" type="button">Sign out</button>
        </div>
      </div>
    </div>
  </header>

  <div class="tabs" role="tablist">
    <div class="nav-section">Workflow</div>
    <button class="tab-btn" data-tab="launch">
      <span class="ico"><svg viewBox="0 0 24 24"><path d="M12 2l3 7 7 3-7 3-3 7-3-7-7-3 7-3 3-7z"/><path d="M5 19l2-2M17 7l2-2"/></svg></span>
      Launch Setup
    </button>
    <button class="tab-btn active" data-tab="make">
      <span class="ico"><svg viewBox="0 0 24 24"><rect x="3" y="5" width="18" height="14" rx="2"/><path d="M9 9l5 3-5 3V9z"/><path d="M4 21h16"/></svg></span>
      Create Video
    </button>
    <button class="tab-btn" data-tab="publish" data-pub-jump="compose">
      <span class="ico"><svg viewBox="0 0 24 24"><path d="M4 6h16M4 12h10M4 18h7"/><path d="M18 14l3 3-3 3M12 17h9"/></svg></span>
      Publish
    </button>
    <button class="tab-btn" data-tab="library">
      <span class="ico"><svg viewBox="0 0 24 24"><path d="M3 7l3-3h5l2 2h8v13a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V7z"/></svg></span>
      Library
    </button>
    <button class="tab-btn" data-tab="settings">
      <span class="ico"><svg viewBox="0 0 24 24"><path d="M12 15.5A3.5 3.5 0 1 0 12 8a3.5 3.5 0 0 0 0 7.5z"/><path d="M19.4 15a1.7 1.7 0 0 0 .3 1.9l.1.1-2 3.4-.2-.1a1.7 1.7 0 0 0-1.9.3l-.2.2-3.8-2.2v-.3a1.7 1.7 0 0 0-1.4-1.6l-.3-.1V12l.3-.1a1.7 1.7 0 0 0 1.4-1.6V10l3.8-2.2.2.2a1.7 1.7 0 0 0 1.9.3l.2-.1 2 3.4-.1.1a1.7 1.7 0 0 0-.3 1.9l.1.3z"/></svg></span>
      Settings
    </button>
    <button id="advancedNavToggle" class="tab-btn nav-toggle" type="button">
      <span class="ico"><svg viewBox="0 0 24 24"><path d="M4 7h16M7 12h10M10 17h4"/></svg></span>
      Advanced Tools <span class="chev">v</span>
    </button>
    <div class="nav-section secondary-nav">Advanced</div>
    <button class="tab-btn secondary-nav engine-server-only" data-tab="generate">
      <span class="ico"><svg viewBox="0 0 24 24"><path d="M4 19h16M5 17l8-8 4 4-8 8H5v-4zM13 9l3-3 4 4-3 3"/></svg></span>
      Long Story
    </button>
    <button class="tab-btn secondary-nav" data-tab="short">
      <span class="ico"><svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 2"/></svg></span>
      Short Script
    </button>
    <button class="tab-btn secondary-nav" data-tab="paste">
      <span class="ico"><svg viewBox="0 0 24 24"><rect x="9" y="3" width="6" height="12" rx="3"/><path d="M5 11a7 7 0 0 0 14 0M12 18v3M8 21h8"/></svg></span>
      Narrate Text
    </button>
    <button class="tab-btn secondary-nav engine-server-only" data-tab="music">
      <span class="ico"><svg viewBox="0 0 24 24"><path d="M9 18V5l12-2v13"/><circle cx="6" cy="18" r="3"/><circle cx="18" cy="16" r="3"/></svg></span>
      Music &amp; Mix
    </button>
    <button class="tab-btn secondary-nav engine-server-only" data-tab="video">
      <span class="ico"><svg viewBox="0 0 24 24"><rect x="3" y="5" width="18" height="14" rx="2"/><path d="M9 9l5 3-5 3V9z"/></svg></span>
      Video Studio
    </button>
  </div>

  <a id="main" tabindex="-1"></a>
  <div id="tab-launch" class="tab-content">
    <div class="launch-shell">
      <div class="wizard-progress" id="wizardProgress">
        <div class="wizard-bar"><div class="wizard-bar-fill" id="wizardBarFill"></div></div>
        <div class="wizard-steps-row" id="wizardStepsRow"></div>
      </div>

      <div class="wizard-step" id="wizStep-phantomline" data-wiz="0">
        <div class="wizard-step-inner">
          <span class="launch-step-num">1</span>
          <div>
            <h3>Install Phantomline</h3>
            <p class="hint">You're running the local studio right now — this step is already done.</p>
          </div>
        </div>
        <div class="wizard-check-status ready"><span class="launch-dot"></span> Running on this machine</div>
        <div class="wizard-actions">
          <button class="btn wizard-next" type="button">Next →</button>
        </div>
      </div>

      <div class="wizard-step" id="wizStep-ollama" data-wiz="1" style="display:none;">
        <div class="wizard-step-inner">
          <span class="launch-step-num">2</span>
          <div>
            <h3>Install the script writer (Ollama)</h3>
            <p class="hint">Ollama runs an AI model on your machine to write video scripts, hooks, and titles. No cloud API needed.</p>
          </div>
        </div>
        <div class="wizard-check-status" id="wizOllamaStatus"><span class="launch-dot"></span> Checking...</div>
        <div id="wizOllamaActions" class="wizard-check-actions-area"></div>
        <div class="wizard-actions">
          <button class="btn wizard-next" type="button" id="wizOllamaNext" disabled>Next →</button>
          <button class="btn secondary wizard-recheck" type="button" data-wiz-check="ollama">Re-check</button>
        </div>
      </div>

      <div class="wizard-step" id="wizStep-kokoro" data-wiz="2" style="display:none;">
        <div class="wizard-step-inner">
          <span class="launch-step-num">3</span>
          <div>
            <h3>Install voice narrator (Kokoro)</h3>
            <p class="hint">Kokoro generates natural-sounding voiceovers for your videos — 16 voices, fully offline.</p>
          </div>
        </div>
        <div class="wizard-check-status" id="wizKokoroStatus"><span class="launch-dot"></span> Checking...</div>
        <div id="wizKokoroActions" class="wizard-check-actions-area"></div>
        <div class="wizard-actions">
          <button class="btn wizard-next" type="button" id="wizKokoroNext" disabled>Next →</button>
          <button class="btn secondary wizard-recheck" type="button" data-wiz-check="kokoro">Re-check</button>
        </div>
      </div>

      <div class="wizard-step" id="wizStep-optional" data-wiz="3" style="display:none;">
        <div class="wizard-step-inner">
          <span class="launch-step-num">4</span>
          <div>
            <h3>Optional upgrades</h3>
            <p class="hint">These make Phantomline more powerful but aren't required to create your first video.</p>
          </div>
        </div>
        <div id="wizOptionalChecks"></div>
        <div class="wizard-actions">
          <button class="btn wizard-next" type="button">Skip &amp; continue →</button>
        </div>
      </div>

      <div class="wizard-step" id="wizStep-test" data-wiz="4" style="display:none;">
        <div class="wizard-step-inner">
          <span class="launch-step-num">5</span>
          <div>
            <h3>See it work</h3>
            <p class="hint">Render a quick 8-second demo video to confirm everything is connected. Takes about 30 seconds.</p>
          </div>
        </div>
        <button id="launchTestRenderBtn" class="btn" type="button" style="margin-top:12px;">Render 8-second demo</button>
        <div class="hint" id="launchTestStatus" style="margin-top:12px;"></div>
        <div class="launch-test-result" id="launchTestResult" style="display:none;">
          <div class="phone-preview phone-preview-wizard">
            <div class="phone-preview-inner" id="wizPhonePreviewInner">
              <video id="wizPhonePreviewVideo" class="phone-preview-video" controls playsinline></video>
              <div class="shorts-chrome" aria-hidden="true">
                <div class="shorts-chrome-top">
                  <span class="shorts-chrome-time">9:41</span>
                  <span class="shorts-chrome-system">
                    <svg viewBox="0 0 16 10" width="14" height="9" fill="currentColor"><rect x="0" y="6" width="3" height="4"/><rect x="4" y="4" width="3" height="6"/><rect x="8" y="2" width="3" height="8"/><rect x="12" y="0" width="3" height="10"/></svg>
                    <svg viewBox="0 0 16 12" width="13" height="10" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M2 5a9 9 0 0 1 12 0M4.5 7.5a5 5 0 0 1 7 0M7 10a1.5 1.5 0 0 1 2 0"/></svg>
                    <svg viewBox="0 0 22 10" width="18" height="8" fill="none" stroke="currentColor" stroke-width="1"><rect x="0.5" y="0.5" width="18" height="9" rx="2"/><rect x="2" y="2" width="14" height="6" rx="1" fill="currentColor"/><rect x="19.5" y="3" width="2" height="4" rx="0.5" fill="currentColor"/></svg>
                  </span>
                </div>
                <div class="shorts-chrome-topactions">
                  <svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><circle cx="11" cy="11" r="7"/><line x1="21" y1="21" x2="16.5" y2="16.5"/></svg>
                  <svg viewBox="0 0 24 24" width="16" height="16" fill="currentColor"><circle cx="12" cy="5" r="1.6"/><circle cx="12" cy="12" r="1.6"/><circle cx="12" cy="19" r="1.6"/></svg>
                </div>
                <div class="shorts-chrome-rail">
                  <div class="shorts-chrome-action">
                    <svg viewBox="0 0 24 24" width="22" height="22" fill="currentColor"><path d="M14 9V5a3 3 0 0 0-3-3l-4 9v11h11.28a2 2 0 0 0 2-1.7l1.38-9A2 2 0 0 0 19.7 9zM7 22H4a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2h3"/></svg>
                    <span>2.8M</span>
                  </div>
                  <div class="shorts-chrome-action">
                    <svg viewBox="0 0 24 24" width="22" height="22" fill="currentColor"><path d="M10 15v4a3 3 0 0 0 3 3l4-9V2H5.72a2 2 0 0 0-2 1.7l-1.38 9A2 2 0 0 0 4.32 15zM17 2h3a2 2 0 0 1 2 2v7a2 2 0 0 1-2 2h-3"/></svg>
                    <span>Dislike</span>
                  </div>
                  <div class="shorts-chrome-action">
                    <svg viewBox="0 0 24 24" width="22" height="22" fill="currentColor"><path d="M21 11.5a8.38 8.38 0 0 1-9 8.38 8.5 8.5 0 0 1-3.8-.9L3 21l1.9-5.2A8.5 8.5 0 0 1 4 12 8.38 8.38 0 0 1 12.5 3.5 8.38 8.38 0 0 1 21 11.5z"/></svg>
                    <span>2.8M</span>
                  </div>
                  <div class="shorts-chrome-action">
                    <svg viewBox="0 0 24 24" width="22" height="22" fill="currentColor"><path d="M2 21l21-9L2 3v7l15 2-15 2z"/></svg>
                    <span>2.8M</span>
                  </div>
                  <div class="shorts-chrome-action">
                    <svg viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="23 4 23 10 17 10"/><polyline points="1 20 1 14 7 14"/><path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"/></svg>
                    <span>2.8M</span>
                  </div>
                  <div class="shorts-chrome-yt">
                    <svg viewBox="0 0 28 20" width="22" height="16"><rect width="28" height="20" rx="5" fill="#ff0033"/><polygon points="11,5 11,15 19,10" fill="#fff"/></svg>
                  </div>
                </div>
                <div class="shorts-chrome-bottom">
                  <span class="shorts-chrome-creator">
                    <strong>@your_channel</strong>
                  </span>
                  <span class="shorts-chrome-desc">Your Phantomline video — ready for YouTube Shorts</span>
                </div>
              </div>
            </div>
          </div>
        </div>
        <div class="wizard-actions">
          <button class="btn wizard-next" type="button" id="wizTestNext" style="display:none;">Next →</button>
          <button class="btn secondary wizard-next" type="button">Skip →</button>
        </div>
      </div>

      <div class="wizard-step" id="wizStep-done" data-wiz="5" style="display:none;">
        <div class="wizard-done-inner">
          <div class="wizard-done-check">✓</div>
          <h3>You're all set</h3>
          <p class="hint">Phantomline is ready to create faceless videos. Pick a format, let AI write the script, and render — all on your machine.</p>
          <button id="launchStartBtn" class="btn" type="button" style="margin-top:18px; font-size:16px; padding:14px 36px;">Start creating →</button>
          <div class="hint" style="margin-top:8px;">First full video takes ~3-5 minutes to render.</div>
        </div>
      </div>

      <!-- Hidden elements referenced by existing JS -->
      <div style="display:none;">
        <span id="launchReadinessLabel"></span>
        <span id="launchReadinessText"></span>
        <span id="launchScore"></span>
        <span id="launchChecks"></span>
        <span id="launchDemoStatus"></span>
        <button id="launchCheckBtn" type="button"></button>
        <button id="launchDemoBtn" type="button"></button>
        <button id="launchOpenSettingsBtn" type="button"></button>
        <button id="launchOpenConnectionsBtn" type="button"></button>
        <button id="launchUseSourceModeBtn" type="button"></button>
      </div>
    </div>
  </div>

  <div id="tab-make" class="tab-content active">
    <div class="workflow-grid">
      <section class="panel make-main-panel">
        <!-- Wizard step bar -->
        <div class="wiz-bar" id="wizBar">
          <button class="wiz-step active" data-wiz="1" type="button"><span class="wiz-num">1</span><span class="wiz-label">Script</span></button>
          <div class="wiz-line"></div>
          <button class="wiz-step" data-wiz="2" type="button"><span class="wiz-num">2</span><span class="wiz-label">Style</span></button>
          <div class="wiz-line"></div>
          <button class="wiz-step" data-wiz="3" type="button"><span class="wiz-num">3</span><span class="wiz-label">Review</span></button>
        </div>

        <!-- Hidden selects that existing JS depends on -->
        <select id="makeRecipe" style="display:none;">
          <option value="custom" selected>Custom faceless channel</option>
          <option value="viral-story">Viral Story Short</option>
          <option value="survival">Survival tips</option>
          <option value="mystery">Mystery documentary</option>
          <option value="history">History explainer</option>
          <option value="kids-edu">Kids educational</option>
          <option value="tech">Tech explainer</option>
          <option value="finance">Finance explainer</option>
          <option value="scary-story">Scary story narration</option>
          <option value="motivational">Motivational shorts</option>
          <option value="reddit-story">Reddit-style story</option>
          <option value="true-crime">True crime</option>
          <option value="asmr-sleep">ASMR / Sleep</option>
          <option value="science">Science explainer</option>
          <option value="conspiracy">Conspiracy / What if</option>
          <option value="top-list">Top 10 / Ranked</option>
          <option value="travel">Travel / Geography</option>
          <option value="philosophy">Philosophy</option>
          <option value="urban-legend">Urban legends</option>
          <option value="news-recap">News recap</option>
          <option value="product-review">Product review</option>
        </select>
        <div id="makeRecipeCards" style="display:none;"></div>

        <!-- ═══ STEP 1: Script ═══ -->
        <div class="wiz-panel active" data-wiz-panel="1">
          <div class="make-stage" id="makeStageIdea">
            <div class="make-stage-title">
              <strong>What's the video about?</strong>
              <span>Generate ideas or type your own.</span>
            </div>
            <div class="script-actions" style="margin-top:0;">
              <button id="makeShuffleIdeaBtn" class="btn secondary" type="button" style="width:auto; margin-top:0;">Generate Ideas</button>
              <button id="makeTitleIdeasBtn" class="btn secondary" type="button" style="width:auto; margin-top:0;" disabled data-blocked-reason="Generate and pick an idea first">Generate Titles</button>
              <span class="hint" id="makeIdeaStatus" style="align-self:center;"></span>
            </div>
            <div id="makeIdeaDeck" class="idea-deck"></div>
            <div id="makeTitleDeck" class="idea-deck"></div>
            <div class="title-package" id="makeTitlePackage">
              <label for="makePreferredTitle">Chosen YouTube title</label>
              <input type="text" id="makePreferredTitle" placeholder="Click a generated title or write your own." />
            </div>
            <label for="makeTopic">Video idea</label>
            <textarea id="makeTopic" rows="3" placeholder="e.g. {{ defaults.topic }}"></textarea>
          </div>

          <div class="make-top-grid">
            <div>
              <label for="makeDuration">Length</label>
              <select id="makeDuration">
                <option value="70">30 seconds</option>
                <option value="95">40 seconds</option>
                <option value="140">1 minute</option>
                <option value="280" selected>2 minutes</option>
                <option value="700">5 minutes</option>
                <option value="1400">10 minutes</option>
                <option value="2800">20 minutes</option>
                <option value="4200">30 minutes</option>
                <option value="8400">60 minutes</option>
              </select>
              <div id="makeDurationHint" class="hint" style="margin-top:6px;">Short-form mode.</div>
            </div>
            <div>
              <label for="makeAspect">Video ratio</label>
              <select id="makeAspect">
                <option value="16:9" selected>16:9 YouTube</option>
                <option value="9:16">9:16 Shorts / TikTok</option>
                <option value="1:1">1:1 square</option>
              </select>
            </div>
          </div>

          <div class="make-stage" id="makeViralStage" style="display:none;">
            <div class="make-stage-title">
              <strong>Story engine</strong>
              <span>Phantomline uses this to shape the hook, twist, captions, and loop.</span>
            </div>
            <div class="choice-grid" id="makeTensionCards">
              <button class="choice-card active" type="button" data-tension="betrayal_drama"><strong>Betrayal / Drama</strong><span>Wrong screenshots, hidden phones, secrets.</span></button>
              <button class="choice-card" type="button" data-tension="confession_secret"><strong>Confession / Secret</strong><span>A private lie or secret finally leaks.</span></button>
              <button class="choice-card" type="button" data-tension="rule_horror"><strong>Rule Horror</strong><span>A clear rule gets broken.</span></button>
              <button class="choice-card" type="button" data-tension="impossible_situation"><strong>Impossible Situation</strong><span>A text, person, or event that should not exist.</span></button>
            </div>
            <div class="row">
              <div>
                <label for="makeTensionFormat">Tension format</label>
                <select id="makeTensionFormat">
                  <option value="betrayal_drama" selected>Betrayal / Drama</option>
                  <option value="confession_secret">Confession / Secret</option>
                  <option value="rule_horror">Rule-Based Horror</option>
                  <option value="impossible_situation">Impossible Situation</option>
                </select>
              </div>
              <div>
                <label for="makeLoopType">Loop ending</label>
                <select id="makeLoopType">
                  <option value="circular" selected>Circular callback</option>
                  <option value="missing-info">Missing information</option>
                  <option value="hard-cut">Fast hard cut</option>
                  <option value="question">Question ending</option>
                </select>
              </div>
            </div>
          </div>

          <details class="advanced-box">
            <summary>Advanced script controls</summary>
            <div class="advanced-inner">
              <div class="row">
                <div>
                  <label for="makeNiche">Niche</label>
                  <input type="text" id="makeNiche" value="{{ defaults.genre }}" />
                </div>
                <div>
                  <label for="makeAudience">Audience</label>
                  <input type="text" id="makeAudience" value="curious general viewers" />
                </div>
              </div>
              <div class="row">
                <div>
                  <label for="makeFormat">Video format</label>
                  <select id="makeFormat">
                    <option value="story">Narrated story</option>
                    <option value="listicle">Listicle</option>
                    <option value="tutorial">Step-by-step tutorial</option>
                    <option value="documentary">Mini documentary</option>
                    <option value="explainer">Explainer</option>
                    <option value="short-hook">Short-form hook</option>
                    <option value="viral-short">Viral story short</option>
                  </select>
                </div>
                <div>
                  <label for="makeModel">Ollama model</label>
                  <select id="makeModel"><option value="{{ defaults.model }}">{{ defaults.model }}</option></select>
                </div>
              </div>
              <label for="makeDesc">Script direction</label>
              <textarea id="makeDesc" rows="3">Open with a strong hook, keep each section visual, avoid filler, and write for a single narrator.</textarea>
              <div class="row">
                <div>
                  <label for="makeHookStyle">Hook style</label>
                  <select id="makeHookStyle">
                    <option value="mistake" selected>Mistake warning</option>
                    <option value="question">Shocking question</option>
                    <option value="secret">Most people don't know</option>
                    <option value="cold-open">Visual cold open</option>
                    <option value="countdown">Countdown / list</option>
                    <option value="conflict">Story conflict</option>
                  </select>
                </div>
                <div>
                  <label for="makeKeywordMode">Keyword emphasis</label>
                  <select id="makeKeywordMode">
                    <option value="auto" selected>Auto highlight</option>
                    <option value="minimal">Minimal</option>
                    <option value="off">Off</option>
                  </select>
                </div>
              </div>
              <div class="row">
                <div>
                  <label for="makeTone">Narration tone</label>
                  <input type="text" id="makeTone" value="{{ defaults.tone }}" />
                </div>
              </div>
            </div>
          </details>

          <div class="wiz-nav">
            <div></div>
            <button class="btn wiz-next" type="button" data-wiz-to="2">Next: Style →</button>
          </div>
        </div>

        <!-- ═══ STEP 2: Style ═══ -->
        <div class="wiz-panel" data-wiz-panel="2">
          <div class="make-stage" id="makeStageVisuals">
            <div class="make-stage-title">
              <strong>Visual layer</strong>
              <span>AI-generated art or retention footage.</span>
            </div>
            <div class="choice-grid" id="makeVisualSourceCards">
              <button class="choice-card active" type="button" data-mode="generated"><strong>AI scene images</strong><span>Generate matching local scene stills with Forge.</span></button>
              <button class="choice-card" type="button" data-mode="source"><strong>Retention footage</strong><span>Use gameplay, parkour, screen recordings, or any background clip.</span></button>
            </div>
          </div>

          <div class="make-top-grid">
            <div>
              <label for="makeVideoMode">Visual source</label>
              <select id="makeVideoMode">
                <option value="generated">Generate scene images</option>
                <option value="library" selected>Use Phantomline footage</option>
                <option value="source">Use retention/source footage</option>
              </select>
            </div>
            <div>
              <label for="makeVoice">Voice</label>
              <select id="makeVoice"></select>
            </div>
          </div>

          <div id="makeSourceVideoPanel" class="make-section" style="display:none;">
            <div class="make-section-title">
              <strong>Source video</strong>
              <span>Gameplay, parkour, clips, screen recordings.</span>
            </div>
            <div id="makeSourceUploadRow">
              <label for="makeSourceVideoFile">Source video</label>
              <input type="file" id="makeSourceVideoFile" accept="video/mp4,video/quicktime,video/webm,video/x-m4v,video/x-msvideo,video/x-matroska" />
            </div>
            <div id="makeSourceLibraryRow" class="source-library-row" hidden>
              <label>Phantomline footage</label>
              <button type="button" id="browseFootageLibraryBtn" class="btn secondary">Browse videos</button>
              <div id="makeSourceLibraryPick" class="source-library-pick" hidden>
                <span class="source-library-pick-label">Library:</span>
                <span class="source-library-pick-id" data-clip-id=""></span>
                <button type="button" class="source-library-pick-clear" aria-label="Clear library pick">×</button>
              </div>
            </div>
            <div class="row">
              <div>
                <label for="makeSourceFit">Video fit</label>
                <select id="makeSourceFit">
                  <option value="cover" selected>Fill frame / crop</option>
                  <option value="contain">Fit full video / black bars</option>
                </select>
              </div>
              <div>
                <label for="makeCaptions">Captions</label>
                <select id="makeCaptions">
                  <option value="1" selected>Burn in captions</option>
                  <option value="0">No captions</option>
                </select>
              </div>
            </div>
            <div class="row">
              <div>
                <label for="makeCaptionStyle">Caption style</label>
                <select id="makeCaptionStyle">
                  <option value="tiktok" selected>TikTok bold</option>
                  <option value="clean">Clean lower-third</option>
                  <option value="horror">Mystery / horror</option>
                  <option value="kids">Kids cartoon</option>
                  <option value="documentary">Documentary</option>
                </select>
              </div>
              <div>
                <label for="makeTitleStyle">Title style</label>
                <select id="makeTitleStyle">
                  <option value="top" selected>Top badge</option>
                  <option value="none">No title overlay</option>
                </select>
              </div>
            </div>
            <select id="makePatternInterrupts" style="display:none;"><option value="0" selected>Off</option></select>
            <select id="makeSourceEnhance" style="display:none;"><option value="none" selected>None</option></select>
            <div class="hint" id="makeSourceVideoStatus" style="margin-top:6px;">Upload gameplay, parkour, drone footage, screen recordings, or any clip you want narrated.</div>
          </div>

          <details class="advanced-box">
            <summary>Advanced style controls</summary>
            <div class="advanced-inner">
              <div class="row">
                <div>
                  <label for="makeVisualPreset">Visual style</label>
                  <select id="makeVisualPreset">
                    <option value="cinematic-photoreal" selected>Cinematic photoreal</option>
                    <option value="animated-film">Animated film</option>
                    <option value="storybook">Storybook illustration</option>
                    <option value="cartoon">Clean cartoon</option>
                    <option value="survival-channel">Survival tips channel</option>
                    <option value="custom">Custom</option>
                  </select>
                </div>
                <div>
                  <label for="makeAmbience">Ambience</label>
                  <input type="text" id="makeAmbience" value="night mist, soft blue moonlight, calm suspense" />
                </div>
              </div>
              <div class="row">
                <div>
                  <label for="makeSceneSeconds">Seconds per scene</label>
                  <input type="number" id="makeSceneSeconds" min="4" max="12" step="1" value="8" />
                </div>
              </div>
              <label for="makeVisualStyle">Style details</label>
              <input type="text" id="makeVisualStyle" value="dark cinematic sci-fi, atmospheric fog, soft contrast, film grain" />
              <label for="makeVisualCharacter">Recurring character / host</label>
              <input type="text" id="makeVisualCharacter" placeholder="Example: a small friendly backpacker host demonstrating each survival tip, orange jacket, simple readable silhouette" />
              <label for="makeMusicPrompt">Music bed</label>
              <textarea id="makeMusicPrompt" rows="2">calm cinematic ambient pad, distant piano, low strings, soft reverb, no drums, sleep-friendly, slow tempo</textarea>
            </div>
          </details>

          <div class="wiz-nav">
            <button class="btn secondary wiz-back" type="button" data-wiz-to="1">← Script</button>
            <button class="btn wiz-next" type="button" data-wiz-to="3">Next: Review →</button>
          </div>
        </div>

        <!-- ═══ STEP 3: Review & Render ═══ -->
        <div class="wiz-panel" data-wiz-panel="3">
          <div class="make-stage" id="makeStageRender">
            <div class="make-stage-title">
              <strong>Review &amp; render</strong>
              <span>Confirm your choices and hit render.</span>
            </div>
            <div class="wiz-review" id="wizReview"></div>
          </div>

          <!-- Channel Insights (optional, collapsed) -->
          <details id="insightsPanel" class="insights-panel compact">
            <summary class="ip-head" style="cursor:pointer; list-style:none;">
              <span class="ip-title">Optional: channel intelligence</span>
              <span class="ip-status" id="insightsStatus">not configured</span>
            </summary>
            <div id="insightsBody" class="ip-empty">
              Upload analytics once to make ideas, titles, scripts, SEO, and publish packages follow what already works for your channel.
            </div>
            <div class="ip-actions" id="insightsActions">
              <button type="button" id="insightsImportAnalyticsBtn">Import Overview CSV</button>
              <button type="button" id="insightsImportTrafficBtn">Import Traffic Sources CSV</button>
              <button type="button" id="insightsImportSearchBtn">Import Search Terms CSV</button>
              <button type="button" id="insightsClearBtn" style="margin-left:auto; opacity:0.65;">Clear</button>
            </div>
            <input type="file" id="insightsAnalyticsFile" accept=".csv,.tsv" style="display:none;" />
            <input type="file" id="insightsTrafficFile" accept=".csv,.tsv" style="display:none;" />
            <input type="file" id="insightsSearchFile" accept=".csv,.tsv" style="display:none;" />
          </details>

          <div class="wiz-nav">
            <button class="btn secondary wiz-back" type="button" data-wiz-to="2">← Style</button>
            <div class="wiz-render-group">
              <button id="makeVideoBtn" class="btn">Render Video</button>
              <button id="makeCancelBtn" class="btn secondary" type="button" style="display:none;" data-blocked-reason="No render in progress">Stop</button>
            </div>
          </div>

          <!-- Hidden utility buttons preserved for existing JS -->
          <button id="makeLoadDemoBtn" type="button" style="display:none;">Load Demo</button>
          <button id="makeSaveBrandBtn" type="button" style="display:none;">Save Brand</button>
          <button id="makeLoadBrandBtn" type="button" style="display:none;">Load Brand</button>
        </div>

        <!-- Production line (shown during/after render, outside wizard steps) -->
        <h2 id="makeProductionLineHeader" style="margin-top:18px; display:none;">Production line</h2>
        <div class="workflow-steps" id="makeProductionLine" style="display:none;">
          <div class="workflow-step" id="makeStepScript">
            <div class="num">1</div><div><strong>Script</strong><span>Write the voiceover.</span></div><div class="state">waiting</div>
          </div>
          <div class="workflow-step" id="makeStepNarration">
            <div class="num">2</div><div><strong>Narration</strong><span>Generate voice audio locally.</span></div><div class="state">waiting</div>
          </div>
          <div class="workflow-step" id="makeStepScenes">
            <div class="num">3</div><div><strong>Visuals</strong><span>Generate scene art or prepare source video.</span></div><div class="state">waiting</div>
          </div>
          <div class="workflow-step" id="makeStepMusic">
            <div class="num">4</div><div><strong>Music</strong><span>Create a background bed.</span></div><div class="state">waiting</div>
          </div>
          <div class="workflow-step" id="makeStepMix">
            <div class="num">5</div><div><strong>Mix</strong><span>Ducks music under narration.</span></div><div class="state">waiting</div>
          </div>
          <div class="workflow-step" id="makeStepTimeline">
            <div class="num">6</div><div><strong>Timeline</strong><span>Match scenes to audio length.</span></div><div class="state">waiting</div>
          </div>
          <div class="workflow-step" id="makeStepVideo">
            <div class="num">7</div><div><strong>Video</strong><span>Render MP4.</span></div><div class="state">waiting</div>
          </div>
        </div>

        <div id="makeResult" class="make-result">
          <h2 style="margin-top:0;">Video ready</h2>
          <div class="handoff-card" id="makeHandoffCard">
            <strong>Finish the launch package</strong>
            <div class="hint">Preview the MP4, check the publish draft, then schedule or upload from Publish Studio.</div>
            <div class="row" style="margin-top:12px;">
              <div>
                <label for="makePinnedComment">Pinned comment</label>
                <input type="text" id="makePinnedComment" placeholder="Would you have checked?" />
              </div>
              <div>
                <label for="makeHashtags">Hashtags</label>
                <input type="text" id="makeHashtags" placeholder="#shorts #storytime #plotwist" />
              </div>
            </div>
            <div class="readiness-checklist" id="makeHandoffChecklist"></div>
            <div class="handoff-actions">
              <button class="btn secondary" id="makePreviewFinalBtn" type="button">Preview final video</button>
              <button class="btn secondary" id="makePublishDraftBtn" type="button">Review publish draft</button>
            </div>
          </div>
          <div class="script-actions">
            <button class="btn secondary" id="makeDownloadVideoBtn">Download MP4</button>
            <button class="btn secondary" id="makeOpenPublishBtn">Review publish draft</button>
            <button class="btn secondary" id="makeOpenLibraryBtn">Open Library</button>
            <button class="btn secondary" id="makeOpenAdvancedBtn">Edit scenes</button>
          </div>
          <video id="makePreviewVideo" controls style="display:none;"></video>
          <p class="hint" id="makeResultHint"></p>
        </div>
      </section>

      <section class="panel make-studio-panel">
        <!-- Right rail = preview + readiness + next-best-action.
             Non-sticky; the page scrolls as one block. Bottoms align
             with the left column via grid stretch. Phone preview is
             a <details> so users who don't need the platform mock
             can collapse it and shrink the rail to readiness +
             next-action only. -->
        <details class="studio-preview-collapse" open>
          <summary class="studio-head" style="cursor:pointer; list-style:none; user-select:none;">
            <div>
              <h2>Studio Preview</h2>
              <div class="hint">Frame + format reference. Opens to the platform mock.</div>
            </div>
            <div class="studio-live">Local</div>
          </summary>

        <div class="studio-preview-card">
          <div class="phone-preview">
            <div class="phone-preview-inner" id="makePhonePreviewInner">
              <img class="phone-preview-bg" src="{{ '/static/footage-thumbs/demo_03.jpg' | versioned }}" alt="Pink marble stairs to sea" loading="lazy" decoding="async" />
              <video id="makePhonePreviewVideo" class="phone-preview-video" controls playsinline></video>
              <div class="shorts-chrome" aria-hidden="true">
                <div class="shorts-chrome-top">
                  <span class="shorts-chrome-time">9:41</span>
                  <span class="shorts-chrome-system">
                    <svg viewBox="0 0 16 10" width="14" height="9" fill="currentColor"><rect x="0" y="6" width="3" height="4"/><rect x="4" y="4" width="3" height="6"/><rect x="8" y="2" width="3" height="8"/><rect x="12" y="0" width="3" height="10"/></svg>
                    <svg viewBox="0 0 16 12" width="13" height="10" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M2 5a9 9 0 0 1 12 0M4.5 7.5a5 5 0 0 1 7 0M7 10a1.5 1.5 0 0 1 2 0"/></svg>
                    <svg viewBox="0 0 22 10" width="18" height="8" fill="none" stroke="currentColor" stroke-width="1"><rect x="0.5" y="0.5" width="18" height="9" rx="2"/><rect x="2" y="2" width="14" height="6" rx="1" fill="currentColor"/><rect x="19.5" y="3" width="2" height="4" rx="0.5" fill="currentColor"/></svg>
                  </span>
                </div>
                <div class="shorts-chrome-topactions">
                  <svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><circle cx="11" cy="11" r="7"/><line x1="21" y1="21" x2="16.5" y2="16.5"/></svg>
                  <svg viewBox="0 0 24 24" width="16" height="16" fill="currentColor"><circle cx="12" cy="5" r="1.6"/><circle cx="12" cy="12" r="1.6"/><circle cx="12" cy="19" r="1.6"/></svg>
                </div>
                <div class="shorts-chrome-rail">
                  <div class="shorts-chrome-action">
                    <svg viewBox="0 0 24 24" width="22" height="22" fill="currentColor"><path d="M14 9V5a3 3 0 0 0-3-3l-4 9v11h11.28a2 2 0 0 0 2-1.7l1.38-9A2 2 0 0 0 19.7 9zM7 22H4a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2h3"/></svg>
                    <span>2.8M</span>
                  </div>
                  <div class="shorts-chrome-action">
                    <svg viewBox="0 0 24 24" width="22" height="22" fill="currentColor"><path d="M10 15v4a3 3 0 0 0 3 3l4-9V2H5.72a2 2 0 0 0-2 1.7l-1.38 9A2 2 0 0 0 4.32 15zM17 2h3a2 2 0 0 1 2 2v7a2 2 0 0 1-2 2h-3"/></svg>
                    <span>Dislike</span>
                  </div>
                  <div class="shorts-chrome-action">
                    <svg viewBox="0 0 24 24" width="22" height="22" fill="currentColor"><path d="M21 11.5a8.38 8.38 0 0 1-9 8.38 8.5 8.5 0 0 1-3.8-.9L3 21l1.9-5.2A8.5 8.5 0 0 1 4 12 8.38 8.38 0 0 1 12.5 3.5 8.38 8.38 0 0 1 21 11.5z"/></svg>
                    <span>2.8M</span>
                  </div>
                  <div class="shorts-chrome-action">
                    <svg viewBox="0 0 24 24" width="22" height="22" fill="currentColor"><path d="M2 21l21-9L2 3v7l15 2-15 2z"/></svg>
                    <span>2.8M</span>
                  </div>
                  <div class="shorts-chrome-action">
                    <svg viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="23 4 23 10 17 10"/><polyline points="1 20 1 14 7 14"/><path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"/></svg>
                    <span>2.8M</span>
                  </div>
                  <div class="shorts-chrome-yt">
                    <svg viewBox="0 0 28 20" width="22" height="16"><rect width="28" height="20" rx="5" fill="#ff0033"/><polygon points="11,5 11,15 19,10" fill="#fff"/></svg>
                  </div>
                </div>
                <div class="shorts-chrome-bottom">
                  <span class="shorts-chrome-creator">
                    <strong>@your_channel</strong>
                  </span>
                  <span class="shorts-chrome-desc">Caption safe preview · keep subtitles above the handle</span>
                </div>
              </div>
              <div class="preview-title" id="makePreviewTitle">Pink Marble Stairs to Sea</div>
              <div class="preview-meta">
                <span id="makePreviewRatio">9:16</span>
                <span id="makePreviewMeta">Local render</span>
              </div>
            </div>
          </div>
          <!-- studio-specs (Format/Visuals/Length chips) removed: it
               duplicated the dropdowns in step 4 (Final render choices)
               and added vertical noise without giving the user any
               new information. -->
        </div>
        </details>

        <!-- Readiness card. Score-first; the per-check breakdown lives
             in a <details> expander because it duplicated the workflow
             step titles in the main column. The score is the signal
             users actually act on; the breakdown is a debugging tool. -->
        <details class="readiness-card">
          <summary class="readiness-top" style="cursor:pointer; list-style:none;">
            <div>
              <strong>Video readiness</strong>
              <div class="hint" style="margin-top:2px;">Score updates as you fill each step.</div>
            </div>
            <div class="readiness-score" id="makeReadinessScore">0</div>
          </summary>
          <div class="readiness-items" id="makeReadinessItems" style="margin-top:12px;"></div>
        </details>
        <div class="next-action-card">
          <div class="make-section-title">
            <div>
              <strong>Next best action</strong>
              <div class="hint" id="makeNextActionText">Load a demo or generate ideas to begin.</div>
            </div>
          </div>
          <button id="makeNextActionBtn" class="btn secondary" type="button">Start</button>
        </div>

        <!-- Production line. Hidden by default; revealed by phantomline.js
             when render kicks off (any .workflow-step earns .running or
             .done class). Until then it's empty noise. 7 stages all
             "waiting" before the user has even pressed render. -->
        <h2 id="makeProductionLineHeader" style="margin-top:18px; display:none;">Production line</h2>
        <div class="workflow-steps" id="makeProductionLine" style="display:none;">
          <div class="workflow-step" id="makeStepScript">
            <div class="num">1</div><div><strong>Script</strong><span>Write the voiceover.</span></div><div class="state">waiting</div>
          </div>
          <div class="workflow-step" id="makeStepNarration">
            <div class="num">2</div><div><strong>Narration</strong><span>Generate voice audio locally.</span></div><div class="state">waiting</div>
          </div>
          <div class="workflow-step" id="makeStepScenes">
            <div class="num">3</div><div><strong>Visuals</strong><span>Generate scene art or prepare source video.</span></div><div class="state">waiting</div>
          </div>
          <div class="workflow-step" id="makeStepMusic">
            <div class="num">4</div><div><strong>Music</strong><span>Create a background bed.</span></div><div class="state">waiting</div>
          </div>
          <div class="workflow-step" id="makeStepMix">
            <div class="num">5</div><div><strong>Mix</strong><span>Ducks music under narration.</span></div><div class="state">waiting</div>
          </div>
          <div class="workflow-step" id="makeStepTimeline">
            <div class="num">6</div><div><strong>Timeline</strong><span>Match scenes to audio length.</span></div><div class="state">waiting</div>
          </div>
          <div class="workflow-step" id="makeStepVideo">
            <div class="num">7</div><div><strong>Video</strong><span>Render MP4.</span></div><div class="state">waiting</div>
          </div>
        </div>

        <div id="makeResult" class="make-result">
          <h2 style="margin-top:0;">Video ready</h2>
          <div class="handoff-card" id="makeHandoffCard">
            <strong>Finish the launch package</strong>
            <div class="hint">Preview the MP4, check the publish draft, then schedule or upload from Publish Studio.</div>
            <!-- Publish metadata. Populated automatically when ideas /
                 titles are generated. Lives here (post-render) instead
                 of mid-step-2 because they're metadata for publishing,
                 not inputs that change the rendered video. -->
            <div class="row" style="margin-top:12px;">
              <div>
                <label for="makePinnedComment">Pinned comment</label>
                <input type="text" id="makePinnedComment" placeholder="Would you have checked?" />
              </div>
              <div>
                <label for="makeHashtags">Hashtags</label>
                <input type="text" id="makeHashtags" placeholder="#shorts #storytime #plotwist" />
              </div>
            </div>
            <div class="readiness-checklist" id="makeHandoffChecklist"></div>
            <div class="handoff-actions">
              <button class="btn secondary" id="makePreviewFinalBtn" type="button">Preview final video</button>
              <button class="btn secondary" id="makePublishDraftBtn" type="button">Review publish draft</button>
            </div>
          </div>
          <div class="script-actions">
            <button class="btn secondary" id="makeDownloadVideoBtn">Download MP4</button>
            <button class="btn secondary" id="makeOpenPublishBtn">Review publish draft</button>
            <button class="btn secondary" id="makeOpenLibraryBtn">Open Library</button>
            <button class="btn secondary" id="makeOpenAdvancedBtn">Edit scenes</button>
          </div>
          <video id="makePreviewVideo" controls style="display:none;"></video>
          <p class="hint" id="makeResultHint"></p>
        </div>
      </section>
    </div>
  </div>

  <div id="tab-generate" class="tab-content">
  <div class="grid">
    <!-- LEFT: Form -->
    <section class="panel">
      <h2>Story input</h2>
      <div class="advanced-banner"><strong>Advanced manual tool</strong>Most users should start with Create Video. Use this when you only want a long script or narration source.</div>

      <label for="topic">Story idea / topic</label>
      <textarea id="topic" rows="4">{{ defaults.topic }}</textarea>

      <label for="genre">Genre</label>
      <input type="text" id="genre" value="{{ defaults.genre }}" />
      <div class="hint">Click to use:</div>
      <div id="genreChips">
        {% for g in defaults.genres %}<span class="tag" data-g="{{ g }}">{{ g }}</span>{% endfor %}
      </div>

      <label for="tone">Tone</label>
      <input type="text" id="tone" value="{{ defaults.tone }}" />

      <div class="row">
        <div>
          <label for="words">Target word count</label>
          <input type="number" id="words" min="500" step="500" value="{{ defaults.words }}" />
        </div>
        <div>
          <label for="model">Ollama model</label>
          <select id="model">
            <option value="{{ defaults.model }}">{{ defaults.model }}</option>
          </select>
          <div class="hint">Falls back to text if Ollama is unreachable.</div>
        </div>
      </div>

      <button id="startBtn" class="btn">Generate Story</button>
      <div class="hint" style="margin-top:10px;">
        Section-by-section generation with rolling context. A 10,000-word story typically takes 5–15 minutes depending on the model and your hardware.
      </div>
    </section>

    <!-- RIGHT: Dashboard (idle) / Status (running) -->
    <section class="panel" id="genRight">
      <!-- IDLE DASHBOARD -->
      <div id="genIdle" class="dashboard">
        <div class="hero-card">
          <h2>What would you like to make?</h2>
          <p>Pick a starting point. You can edit any field afterwards.</p>
          <div class="hero-choices">
            <button class="hero-choice" data-go="generate">
              <span class="hc-ico"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"><path d="M4 19h16M5 17l8-8 4 4-8 8H5v-4zM13 9l3-3 4 4-3 3"/></svg></span>
              <div><strong>Long story</strong><span class="hint">10,000 word narration with rolling memory.</span></div>
            </button>
            <button class="hero-choice" data-go="short">
              <span class="hc-ico"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 2"/></svg></span>
              <div><strong>Short script</strong><span class="hint">30s to 10min, ready to film.</span></div>
            </button>
            <button class="hero-choice" data-go="paste">
              <span class="hc-ico"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="3" width="6" height="12" rx="3"/><path d="M5 11a7 7 0 0 0 14 0M12 18v3M8 21h8"/></svg></span>
              <div><strong>Narrate text</strong><span class="hint">Turn any book or script into audio.</span></div>
            </button>
            <button class="hero-choice" data-go="music">
              <span class="hc-ico"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"><path d="M9 18V5l12-2v13"/><circle cx="6" cy="18" r="3"/><circle cx="18" cy="16" r="3"/></svg></span>
              <div><strong>Music &amp; mix</strong><span class="hint">Score and mix into one MP3.</span></div>
            </button>
            <button class="hero-choice" data-go="video">
              <span class="hc-ico"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="5" width="18" height="14" rx="2"/><path d="M9 9l5 3-5 3V9z"/></svg></span>
              <div><strong>Video plan</strong><span class="hint">Break a script into scenes and local visual prompts.</span></div>
            </button>
          </div>
        </div>

        <div class="recent-strip">
          <h3>Recent work</h3>
          <div class="recent-row" id="recentRow">
            <div class="empty-state" style="padding:30px 14px;">
              <div class="big">📖</div>
              <h3 style="margin:0;">Nothing here yet</h3>
              <p style="margin:6px 0 0;">Your finished projects will appear here.</p>
            </div>
          </div>
        </div>
      </div>

      <!-- WORKING (visible while a job is running) -->
      <div id="genWorking" style="display:none;">
        <h2 class="eyebrow">Status</h2>

        <div id="titleDisplay" class="title-display empty">Starting...</div>

        <div class="status-grid">
          <div class="stat">
            <div class="label">Section</div>
            <div class="value" id="sectionStat">-</div>
          </div>
          <div class="stat">
            <div class="label">Words written</div>
            <div class="value" id="wordsStat">0</div>
          </div>
          <div class="stat">
            <div class="label">Status</div>
            <div class="value" id="statusStat" style="font-size:14px;">idle</div>
          </div>
        </div>

        <div class="progress"><div id="progressBar"></div></div>

        <div class="log" id="log"><span class="line"><span class="t">·</span>Awaiting input.</span></div>
      </div>

      <div id="scriptOutput" class="script-output">
        <h2 style="margin-top:22px;">Final script</h2>
        <textarea id="scriptText"></textarea>
        <div class="script-actions">
          <button class="btn secondary" id="copyBtn">Copy to clipboard</button>
          <button class="btn secondary" id="downloadBtn">Download .txt</button>
          <button class="btn secondary" id="scriptVideoBtn">Send to Video Studio</button>
          <span class="hint" id="savedPathHint" style="margin-left:auto;align-self:center;"></span>
        </div>

        <h2 style="margin-top:28px;">Narration audio (Kokoro TTS)</h2>
        <div class="row">
          <div>
            <label for="voice">Voice</label>
            <select id="voice"></select>
          </div>
          <div>
            <label for="speed">Speed: <span id="speedVal">1.00x</span></label>
            <input type="range" id="speed" min="0.7" max="1.3" step="0.05" value="1.0" />
          </div>
        </div>
        <div class="row">
          <div>
            <label for="audioFmt">Format</label>
            <select id="audioFmt">
              <option value="mp3" selected>MP3 (smaller, recommended)</option>
              <option value="wav">WAV (lossless, larger)</option>
            </select>
          </div>
          <div>
            <label>&nbsp;</label>
            <button id="ttsBtn" class="btn">Generate audio</button>
          </div>
        </div>
        <div class="hint">
          Edits to the script above are sent to Kokoro. First run downloads the model (~330 MB).
          Long stories take several minutes - synthesis runs locally on your machine.
        </div>

        <div id="ttsPanel" class="tts-panel" style="display:none;">
          <div class="status-grid" style="margin-top:14px;">
            <div class="stat">
              <div class="label">TTS status</div>
              <div class="value" id="ttsStatus" style="font-size:14px;">-</div>
            </div>
            <div class="stat">
              <div class="label">Segment</div>
              <div class="value" id="ttsSegment">-</div>
            </div>
            <div class="stat">
              <div class="label">Characters</div>
              <div class="value" id="ttsChars" style="font-size:14px;">0</div>
            </div>
          </div>
          <div class="progress"><div id="ttsProgress"></div></div>
          <audio id="ttsAudio" controls style="width:100%; margin-top:8px; display:none;"></audio>
          <div class="script-actions">
            <button class="btn secondary" id="ttsDownloadBtn" disabled data-blocked-reason="Generate audio first">Download audio</button>
            <span class="hint" id="ttsPathHint" style="margin-left:auto;align-self:center;"></span>
          </div>
        </div>
      </div>
    </section>
  </div>
  </div>

  <div id="tab-short" class="tab-content">
    <section class="panel">
      <h2>Short script</h2>
      <div class="advanced-banner"><strong>Advanced manual tool</strong>Create Video already builds scripts, narration, captions, music, and MP4. Use this for standalone short scripts or experiments.</div>
      <p class="hint" style="margin-top:0;">
        One-shot generator for short-form videos. Pick a duration; the script comes back as a single tight scene with a hook and a haunting close.
      </p>

      <label>Duration</label>
      <div id="durButtons" style="display:flex; flex-wrap:wrap; gap:8px; margin-bottom:6px;">
        <button class="dur-btn" data-words="70"   data-label="30s">30s · 70 words</button>
        <button class="dur-btn" data-words="105"  data-label="45s">45s · 105 words</button>
        <button class="dur-btn" data-words="140"  data-label="1m">1 min · 140 words</button>
        <button class="dur-btn active" data-words="280" data-label="2m">2 min · 280 words</button>
        <button class="dur-btn" data-words="420"  data-label="3m">3 min · 420 words</button>
        <button class="dur-btn" data-words="700"  data-label="5m">5 min · 700 words</button>
        <button class="dur-btn" data-words="1400" data-label="10m">10 min · 1400 words</button>
      </div>
      <div class="hint" style="margin-top:0;">Word counts assume ~140 wpm narration.</div>

      <label>Niche presets (Viral Shorts Playbook)</label>
      <div id="nicheChips" style="display:flex; flex-wrap:wrap; gap:6px; margin-bottom:6px;"></div>
      <div class="hint">Click a niche to pre-fill the topic + style description below.</div>

      <label for="shortTopic">Story idea / topic</label>
      <textarea id="shortTopic" rows="3">{{ defaults.topic }}</textarea>

      <label for="shortDesc">Describe the script (style, voice, pacing, hook angle)</label>
      <textarea id="shortDesc" rows="3" placeholder="e.g. cold-open hook, rising dread, single-narrator voiceover, end on an unanswered question"></textarea>

      <div class="row">
        <div>
          <label for="shortGenre">Genre</label>
          <input type="text" id="shortGenre" value="{{ defaults.genre }}" />
        </div>
        <div>
          <label for="shortTone">Tone</label>
          <input type="text" id="shortTone" value="{{ defaults.tone }}" />
        </div>
      </div>
      <div id="shortGenreChips" style="margin-top:4px;">
        {% for g in defaults.genres %}<span class="tag" data-target="shortGenre" data-g="{{ g }}">{{ g }}</span>{% endfor %}
      </div>

      <label for="shortModel">Ollama model</label>
      <select id="shortModel">
        <option value="{{ defaults.model }}">{{ defaults.model }}</option>
      </select>

      <button id="shortBtn" class="btn">Generate Short Script</button>

      <div id="shortStatusPanel" style="display:none; margin-top:18px;">
        <div id="shortTitleDisplay" class="title-display empty">Working…</div>
        <div class="status-grid">
          <div class="stat">
            <div class="label">Status</div>
            <div class="value" id="shortStatus" style="font-size:14px;">starting</div>
          </div>
          <div class="stat">
            <div class="label">Words</div>
            <div class="value" id="shortWords">0</div>
          </div>
          <div class="stat">
            <div class="label">Target</div>
            <div class="value" id="shortTarget">-</div>
          </div>
        </div>
        <div class="progress"><div id="shortProgress"></div></div>
        <div id="shortNextActions" class="script-actions" style="display:none; margin-top:16px;">
          <button class="btn secondary" id="shortShowScriptBtn">Show script</button>
          <button class="btn secondary" id="shortNarrateTopBtn">Narrate</button>
          <button class="btn secondary" id="shortVideoTopBtn">Video Studio</button>
          <button class="btn" id="shortProductionBtn" style="width:auto; margin-top:0;">Build Production Kit</button>
        </div>
        <div class="hint" id="shortProductionHint" style="display:none; margin-top:10px;"></div>
      </div>

      <div id="shortOutput" class="script-output">
        <h2 style="margin-top:22px;">Final script</h2>
        <textarea id="shortScriptText"></textarea>
        <div class="script-actions">
          <button class="btn secondary" id="shortCopyBtn">Copy</button>
          <button class="btn secondary" id="shortDownloadBtn">Download .txt</button>
          <button class="btn secondary" id="shortNarrateBtn">Send to Narrate tab →</button>
          <button class="btn secondary" id="shortVideoBtn">Send to Video Studio</button>
          <span class="hint" id="shortPathHint" style="margin-left:auto;align-self:center;"></span>
        </div>
      </div>
    </section>
  </div>

  <div id="tab-paste" class="tab-content">
    <section class="panel">
      <h2>Narrate your own text</h2>
      <div class="advanced-banner"><strong>Advanced manual tool</strong>Use this when you already have a script and only need local voiceover audio.</div>
      <p class="hint" style="margin-top:0;">
        Paste any script, story, or article. Kokoro will narrate it locally and produce a downloadable MP3.
      </p>

      <label for="pasteTitle">Output filename</label>
      <input type="text" id="pasteTitle" placeholder="my_narration" value="narration" />

      <label for="pasteText">Text to narrate</label>
      <textarea id="pasteText" class="paste-text" placeholder="Paste your story here..."></textarea>
      <div class="hint" id="pasteCount" style="margin-top:6px;">0 characters · 0 words</div>

      <div class="row">
        <div>
          <label for="pasteVoice">Voice</label>
          <select id="pasteVoice"></select>
        </div>
        <div>
          <label for="pasteSpeed">Speed: <span id="pasteSpeedVal">1.00x</span></label>
          <input type="range" id="pasteSpeed" min="0.7" max="1.3" step="0.05" value="1.0" />
        </div>
      </div>

      <div class="row">
        <div>
          <label for="pasteFmt">Format</label>
          <select id="pasteFmt">
            <option value="mp3" selected>MP3 (smaller, recommended)</option>
            <option value="wav">WAV (lossless, larger)</option>
          </select>
        </div>
        <div>
          <label>&nbsp;</label>
          <button id="pasteBtn" class="btn">Generate audio</button>
        </div>
      </div>

      <div id="pastePanel" style="display:none; margin-top:16px;">
        <div class="status-grid">
          <div class="stat">
            <div class="label">Status</div>
            <div class="value" id="pasteStatus" style="font-size:14px;">-</div>
          </div>
          <div class="stat">
            <div class="label">Segment</div>
            <div class="value" id="pasteSegment">-</div>
          </div>
          <div class="stat">
            <div class="label">Characters</div>
            <div class="value" id="pasteChars" style="font-size:14px;">0</div>
          </div>
        </div>
        <div class="progress"><div id="pasteProgress"></div></div>
        <audio id="pasteAudio" controls style="width:100%; margin-top:8px; display:none;"></audio>
        <div class="script-actions">
          <button class="btn secondary" id="pasteDownloadBtn" disabled data-blocked-reason="Generate audio first">Download audio</button>
          <span class="hint" id="pastePathHint" style="margin-left:auto;align-self:center;"></span>
        </div>
      </div>
    </section>
  </div>

  <div id="tab-video" class="tab-content">
    <section class="panel">
      <h2>Video Studio</h2>
      <div class="advanced-banner"><strong>Advanced manual tool</strong>Create Video handles the normal end-to-end path. Use Video Studio when you want to edit plans, timelines, or render settings by hand.</div>
      <p class="hint" style="margin-top:0;">
        Turn a finished narration script into a timed visual plan. Phantomline will use matching scene motion clips or still images when you render the draft MP4, and only use fallback cards for scenes with no media yet.
      </p>

      <label for="videoTitle">Video title</label>
      <input type="text" id="videoTitle" placeholder="The Silent Watchers in the Darkening Skies" />

      <label for="videoScript">Narration script</label>
      <textarea id="videoScript" class="paste-text" placeholder="Paste a Phantomline script here, or send one from Long Story / Short Script."></textarea>
      <div class="hint" id="videoCount" style="margin-top:6px;">0 words</div>

      <div class="row">
        <div>
          <label for="videoWorkflow">Workflow</label>
          <select id="videoWorkflow">
            <option value="image-to-video" selected>Image to video (recommended)</option>
            <option value="text-to-video">Text to video</option>
            <option value="image-only">Image only / Ken Burns edit</option>
          </select>
        </div>
        <div>
          <label for="videoAspect">Aspect</label>
          <select id="videoAspect">
            <option value="16:9" selected>16:9 YouTube</option>
            <option value="9:16">9:16 Shorts</option>
            <option value="1:1">1:1 square test</option>
          </select>
        </div>
      </div>

      <div class="row">
        <div>
          <label for="videoSceneSeconds">Seconds per scene</label>
          <input type="number" id="videoSceneSeconds" min="4" max="12" step="1" value="8" />
        </div>
        <div>
          <label for="videoVisualPreset">Visual style</label>
          <select id="videoVisualPreset">
            <option value="cinematic-photoreal" selected>Cinematic photoreal</option>
            <option value="animated-film">Animated film</option>
            <option value="storybook">Storybook illustration</option>
            <option value="cartoon">Clean cartoon</option>
            <option value="survival-channel">Survival tips channel</option>
            <option value="custom">Custom</option>
          </select>
        </div>
      </div>

      <div class="row">
        <div>
          <label for="videoStyle">Style details</label>
          <input type="text" id="videoStyle" value="dark cinematic sci-fi, atmospheric fog, soft contrast, film grain" />
        </div>
        <div>
          <label for="videoAmbience">Ambience</label>
          <input type="text" id="videoAmbience" value="night mist, soft blue moonlight, calm suspense" />
        </div>
      </div>

      <label for="videoVisualCharacter">Recurring character / host</label>
      <input type="text" id="videoVisualCharacter" placeholder="Example: a small friendly backpacker host demonstrating each survival tip, orange jacket, simple readable silhouette" />

      <div class="row">
        <div>
          <label for="videoImageProvider">Scene image source</label>
          <select id="videoImageProvider">
            <option value="forge" selected>Local Forge + FLUX</option>
            <option value="none">Manual assets only</option>
          </select>
        </div>
        <div>
          <label for="videoImageQuality">Image quality</label>
          <select id="videoImageQuality">
            <option value="high" selected>high</option>
            <option value="medium">medium</option>
            <option value="low">low</option>
          </select>
        </div>
      </div>

      <div class="row">
        <div>
          <label for="videoForgeUrl">Forge URL</label>
          <input type="text" id="videoForgeUrl" value="http://127.0.0.1:7861" />
        </div>
        <div>
          <label for="videoForgeCheckpoint">Forge checkpoint</label>
          <input type="text" id="videoForgeCheckpoint" value="flux1-dev-bnb-nf4-v2.safetensors" />
        </div>
      </div>
      <div class="hint" style="margin-top:6px;">
        Local Forge generation creates real per-scene stills and saves them into the timeline project's <code>visuals</code> folder before rendering the MP4.
      </div>

      <button id="videoPlanBtn" class="btn">Create Video Plan</button>

      <div id="videoPlanPanel" style="display:none; margin-top:18px;">
        <div class="status-grid">
          <div class="stat">
            <div class="label">Scenes</div>
            <div class="value" id="videoSceneCount">0</div>
          </div>
          <div class="stat">
            <div class="label">Runtime</div>
            <div class="value" id="videoRuntime" style="font-size:14px;">-</div>
          </div>
          <div class="stat">
            <div class="label">Visuals</div>
            <div class="value" id="videoBackendLabel" style="font-size:14px;">Local Forge</div>
          </div>
        </div>
        <div class="script-actions">
          <button class="btn secondary" id="videoCopyBtn">Copy prompts</button>
          <button class="btn secondary" id="videoDownloadPlanBtn" disabled data-blocked-reason="Generate the scene plan first">Download plan JSON</button>
          <button class="btn secondary" id="videoDownloadPromptsBtn" disabled data-blocked-reason="Generate the scene plan first">Download prompts</button>
          <span class="hint" id="videoPathHint" style="margin-left:auto;align-self:center;"></span>
        </div>
        <div class="hint" style="margin-top:10px;">
          Local Forge creates matching scene stills automatically when enabled. For motion workflows, render short clips per scene and Phantomline will assemble them with narration.
        </div>
        <div id="videoScenes" class="library-grid" style="margin-top:16px;"></div>
      </div>

      <h2 style="margin-top:32px;">Auto timeline</h2>
      <p class="hint" style="margin-top:0;">
        Match a saved video plan to narration audio. Phantomline reads the narration length, assigns exact start/end times to each scene, and saves an edit list for the final video assembly step.
      </p>

      <div class="row">
        <div>
          <label for="timelinePlan">Video plan</label>
          <select id="timelinePlan"><option value="">- create a video plan first -</option></select>
        </div>
        <div>
          <label for="timelineNarration">Narration audio</label>
          <select id="timelineNarration"><option value="">- generate narration first -</option></select>
        </div>
      </div>

      <div class="script-actions">
        <button class="btn secondary" id="refreshTimelineAssetsBtn">Refresh choices</button>
        <button class="btn" id="timelineBtn" style="width:auto; margin-top:0;">Build Aligned Timeline</button>
      </div>

      <div id="timelinePanel" style="display:none; margin-top:18px;">
        <div class="status-grid">
          <div class="stat">
            <div class="label">Scenes</div>
            <div class="value" id="timelineScenes">0</div>
          </div>
          <div class="stat">
            <div class="label">Audio length</div>
            <div class="value" id="timelineDuration" style="font-size:14px;">-</div>
          </div>
          <div class="stat">
            <div class="label">Output</div>
            <div class="value" style="font-size:14px;">Edit list</div>
          </div>
        </div>
        <div class="script-actions">
          <button class="btn secondary" id="timelineCopyBtn">Copy edit list</button>
          <button class="btn secondary" id="timelineDownloadJsonBtn" disabled data-blocked-reason="Build the timeline first">Download timeline JSON</button>
          <button class="btn secondary" id="timelineDownloadTxtBtn" disabled data-blocked-reason="Build the timeline first">Download edit list</button>
          <button class="btn secondary" id="draftVideoBtn">Render Draft MP4</button>
          <button class="btn secondary" id="draftVideoDownloadBtn" disabled data-blocked-reason="Render the draft video first">Download MP4</button>
          <span class="hint" id="timelinePathHint" style="margin-left:auto;align-self:center;"></span>
        </div>
        <div class="hint" id="draftVideoStatus" style="margin-top:10px;"></div>
        <div id="timelineScenesGrid" class="library-grid" style="margin-top:16px;"></div>
      </div>
    </section>
  </div>

  <div id="tab-music" class="tab-content">
    <section class="panel">
      <h2>Background music (MusicGen)</h2>
      <div class="advanced-banner"><strong>Advanced manual tool</strong>Create Video makes a music bed automatically. Use this for standalone tracks or custom audio mixing.</div>
      <p class="hint" style="margin-top:0;">
        Generates a 30-second ambient track locally, then crossfade-loops it to the length you want. Designed to sit calmly under your narration without pulling focus, ideal for full audiobooks, narration videos, and long-form content.
      </p>

      <label>Music presets</label>
      <div id="musicChips" style="display:flex; flex-wrap:wrap; gap:6px; margin-bottom:6px;"></div>
      <div class="hint">Click a preset to fill the prompt below. Edit freely.</div>

      <label for="musicPrompt">Music prompt</label>
      <textarea id="musicPrompt" rows="3" placeholder="calm cinematic ambient pad, distant piano, low strings, no drums, sleep-friendly"></textarea>

      <div class="row">
        <div>
          <label for="musicMinutes">Length (minutes)</label>
          <input type="number" id="musicMinutes" min="0.5" step="0.5" value="60" />
        </div>
        <div>
          <label for="musicModelSize">Model size</label>
          <select id="musicModelSize">
            <option value="small" selected>small (fast, ~4 GB VRAM)</option>
            <option value="medium">medium (better quality, ~8 GB)</option>
            <option value="large">large (best quality, ~16 GB)</option>
          </select>
        </div>
      </div>
      <div class="row">
        <div>
          <label for="musicFade">Crossfade (seconds)</label>
          <input type="number" id="musicFade" min="0.5" max="8" step="0.5" value="2" />
        </div>
        <div>
          <label for="musicFmt">Format</label>
          <select id="musicFmt">
            <option value="mp3" selected>MP3</option>
            <option value="wav">WAV</option>
          </select>
        </div>
      </div>
      <div class="row">
        <div>
          <label for="musicName">Output filename</label>
          <input type="text" id="musicName" value="music_bed" />
        </div>
        <div>
          <label>&nbsp;</label>
          <button id="musicBtn" class="btn">Generate music</button>
        </div>
      </div>
      <div class="hint">First run downloads the MusicGen-small model (~1.5 GB). Subsequent generations are seconds-fast on GPU, a few minutes on CPU.</div>

      <div id="musicPanel" style="display:none; margin-top:18px;">
        <div class="status-grid">
          <div class="stat">
            <div class="label">Status</div>
            <div class="value" id="musicStatus" style="font-size:14px;">-</div>
          </div>
          <div class="stat">
            <div class="label">Length</div>
            <div class="value" id="musicLen" style="font-size:14px;">-</div>
          </div>
          <div class="stat">
            <div class="label">Model</div>
            <div class="value" id="musicModelStat" style="font-size:14px;">-</div>
          </div>
        </div>
        <div class="progress"><div id="musicProgress"></div></div>
        <div class="log" id="musicLog" style="height:140px;"></div>
        <audio id="musicAudio" controls style="width:100%; margin-top:8px; display:none;"></audio>
        <div class="script-actions">
          <button class="btn secondary" id="musicDownloadBtn" disabled data-blocked-reason="Generate music first">Download music</button>
          <span class="hint" id="musicPathHint" style="margin-left:auto;align-self:center;"></span>
        </div>
      </div>

      <h2 style="margin-top:32px;">Mix narration + music</h2>
      <p class="hint" style="margin-top:0;">Combines a narration with the background music into a single upload-ready file. Music is automatically lowered (ducked) under the narration so the voice stays clear.</p>

      <label for="mixTts">Narration</label>
      <select id="mixTts"><option value="">- none yet -</option></select>

      <label for="mixUploadFile" style="margin-top:14px;">…or upload an existing narration file</label>
      <div style="display:flex; gap:10px; align-items:center; flex-wrap:wrap;">
        <input type="file" id="mixUploadFile" accept=".mp3,.wav,.flac,.ogg,.m4a,.aac" style="flex:1; min-width:240px;" />
        <button class="btn secondary" id="mixClearUploadBtn" style="display:none;">Clear upload</button>
      </div>
      <div class="hint" id="mixUploadStatus">Supports MP3, WAV, FLAC, OGG, M4A, AAC. Up to 1 GB. If a file is uploaded, it overrides the dropdown above.</div>

      <label for="mixMusic" style="margin-top:14px;">Background music</label>
      <select id="mixMusic"><option value="">- none yet -</option></select>
      <div class="row">
        <div>
          <label for="mixDuck">Music level under narration: <span id="mixDuckVal">18 dB</span></label>
          <input type="range" id="mixDuck" min="0" max="30" step="1" value="18" />
        </div>
        <div>
          <label>&nbsp;</label>
          <button id="mixBtn" class="btn">Mix into final file</button>
        </div>
      </div>

      <div id="mixPanel" style="display:none; margin-top:18px;">
        <div class="status-grid">
          <div class="stat">
            <div class="label">Status</div>
            <div class="value" id="mixStatus" style="font-size:14px;">-</div>
          </div>
        </div>
        <audio id="mixAudio" controls style="width:100%; margin-top:8px; display:none;"></audio>
        <div class="script-actions">
          <button class="btn secondary" id="mixDownloadBtn" disabled data-blocked-reason="Generate the final mix first">Download final mix</button>
          <span class="hint" id="mixPathHint" style="margin-left:auto;align-self:center;"></span>
        </div>
      </div>
    </section>
  </div>

  <div id="tab-publish" class="tab-content">
    <div class="publish-shell">
      <div class="publish-nav">
        <div class="publish-nav-label">Prepare</div>
        <button class="active" data-pub-view="compose">Compose</button>
        <div class="publish-nav-label">Schedule</div>
        <button data-pub-view="calendar">Calendar</button>
        <div class="publish-nav-label">Intelligence</div>
        <button data-pub-view="seo">SEO Finder</button>
        <button data-pub-view="analytics">Analytics</button>
        <button data-pub-view="optimize">Optimize</button>
        <div class="publish-nav-label">Settings</div>
        <button data-pub-view="connections">Connections</button>
      </div>
      <section class="panel">
        <div style="display:flex; justify-content:space-between; gap:12px; align-items:center; margin-bottom:16px;">
          <div>
            <h2 style="margin-bottom:4px;">Publish Studio</h2>
            <p class="hint" style="margin:0;">Package, schedule, and publish finished videos to YouTube.</p>
          </div>
          <button id="publishNewPostBtn" class="btn secondary" type="button">Clear &amp; start fresh</button>
        </div>

        <div id="pubView-compose" class="publish-view">
          <div class="publish-grid">
            <div>
              <div class="publish-card">
                <div class="make-section-title"><strong>Media</strong><span id="publishDraftSaved">Draft saved locally</span></div>
                <label for="publishVideoProject">Finished Phantomline video</label>
                <select id="publishVideoProject"><option value="">- choose a rendered MP4 -</option></select>
                <div class="hint">When Make Video finishes, Phantomline auto-selects the new MP4 here.</div>
              </div>
              <div class="publish-card">
                <div class="make-section-title"><strong>Caption / Description</strong><span><span id="publishCaptionCount">0</span> / 5000</span></div>
                <textarea id="publishCaption" rows="7" placeholder="Video description, CTA, disclosure, and hashtags..."></textarea>
                <div class="publish-pill-row" id="publishHashtagPills">
                  <button class="publish-pill" type="button" title="Click to add to Tags">#shorts</button>
                  <button class="publish-pill" type="button" title="Click to add to Tags">#storytime</button>
                  <button class="publish-pill" type="button" title="Click to add to Tags">#viralshorts</button>
                  <button class="publish-pill" type="button" title="Click to add to Tags">#plotwist</button>
                  <button class="publish-pill" type="button" title="Click to add to Tags">#facelessyoutube</button>
                </div>
              </div>
              <div class="publish-card">
                <div class="make-section-title"><strong>YouTube details</strong><span>Auto-filled from Make Video</span></div>
                <label for="publishTitle">Title</label>
                <input id="publishTitle" type="text" maxlength="100" placeholder="Clickable YouTube title" />
                <div class="row">
                  <div>
                    <label for="publishTags">Tags</label>
                    <input id="publishTags" type="text" placeholder="shorts storytime plot twist" />
                  </div>
                  <div>
                    <label for="publishPinnedComment">Pinned comment</label>
                    <input id="publishPinnedComment" type="text" placeholder="What would you do?" />
                  </div>
                </div>
                <div class="row">
                  <div>
                    <label for="publishPrivacy">Privacy</label>
                    <select id="publishPrivacy">
                      <option value="private" selected>Private</option>
                      <option value="unlisted">Unlisted</option>
                      <option value="public">Public</option>
                    </select>
                  </div>
                  <div>
                    <label for="publishCategory">Category</label>
                    <select id="publishCategory">
                      <option value="24" selected>Entertainment</option>
                      <option value="22">People & Blogs</option>
                      <option value="20">Gaming</option>
                      <option value="27">Education</option>
                      <option value="26">Howto & Style</option>
                      <option value="28">Science & Technology</option>
                    </select>
                  </div>
                </div>
              </div>
              <div class="publish-card">
                <div class="make-section-title"><strong>Thumbnail</strong><span id="thumbStatus">Auto from your video</span></div>
                <div class="hint" style="margin-bottom:8px;">
                  Reads the script + scenes of the video selected above. Generates one full-quality thumbnail at a time. Don't like it? Click Regenerate for a fresh take.
                </div>
                <div style="display:flex; gap:8px; flex-wrap:wrap; align-items:center; margin-bottom:8px;">
                  <select id="thumbAspect" style="flex:0 0 auto; max-width:140px;">
                    <option value="16:9" selected>16:9 (YouTube)</option>
                    <option value="9:16">9:16 (Shorts)</option>
                    <option value="1:1">1:1 (Square)</option>
                  </select>
                  <button id="thumbGenerateBtn" class="btn secondary" type="button" style="flex:0 0 auto; margin-left:auto;">Generate thumbnail</button>
                  <button id="thumbRegenerateBtn" class="btn secondary" type="button" style="flex:0 0 auto; display:none;">Regenerate</button>
                </div>
                <div id="thumbStatusLine" class="hint" style="margin:8px 0; min-height:1em;"></div>
                <div id="thumbVariantsGrid"></div>
                <select id="thumbStyle" style="display:none;"><option value="auto" selected>Auto</option></select>
                <div id="thumbSelectedRow" style="display:none; margin-top:10px; padding:10px; border:1px solid #1f3447; border-radius:8px; background:#0a1722;">
                  <div style="display:flex; gap:10px; align-items:center;">
                    <img id="thumbSelectedPreview" alt="Selected thumbnail" style="width:120px; height:auto; border-radius:6px; flex:0 0 auto;" />
                    <div style="flex:1;">
                      <strong style="display:block; margin-bottom:4px;">Attached to this post</strong>
                      <span class="hint" id="thumbSelectedMeta">--</span>
                    </div>
                    <button id="thumbDownloadBtn" class="btn secondary" type="button" style="flex:0 0 auto;">Download PNG</button>
                  </div>
                </div>
              </div>
            </div>
            <div>
              <div class="publish-card">
                <div class="make-section-title"><strong>YouTube</strong><span id="publishConnectionStatus">checking...</span></div>
                <button id="publishConnectYouTubeBtn" class="btn secondary" type="button" style="width:100%;">Connect YouTube</button>
              </div>
              <div class="publish-card">
                <div class="make-section-title"><strong>Schedule</strong><span>local time</span></div>
                <input id="publishScheduledAt" type="datetime-local" />
                <div class="publish-pill-row">
                  <button class="publish-pill" type="button" data-schedule-quick="now">Now</button>
                  <button class="publish-pill" type="button" data-schedule-quick="1h">+1h</button>
                  <button class="publish-pill" type="button" data-schedule-quick="tonight">Tonight 7pm</button>
                  <button class="publish-pill" type="button" data-schedule-quick="tomorrow">Tomorrow 9am</button>
                </div>
                <div class="hint" id="publishReadinessLine" style="margin-top:8px;"></div>
                <button id="publishScheduleBtn" class="btn" type="button" style="margin-top:10px;">Schedule &amp; upload</button>
                <div class="hint" id="publishStatusLine"></div>
              </div>
              <div class="publish-card">
                <div class="make-section-title"><strong>Preview</strong><span>YouTube</span></div>
                <div class="publish-phone">
                  <div class="publish-preview" id="publishPreviewBox"><div class="empty">No media selected</div></div>
                  <div class="shorts-chrome" aria-hidden="true">
                    <div class="shorts-chrome-top">
                      <span class="shorts-chrome-time">9:41</span>
                      <span class="shorts-chrome-system">
                        <svg viewBox="0 0 16 10" width="14" height="9" fill="currentColor"><rect x="0" y="6" width="3" height="4"/><rect x="4" y="4" width="3" height="6"/><rect x="8" y="2" width="3" height="8"/><rect x="12" y="0" width="3" height="10"/></svg>
                        <svg viewBox="0 0 16 12" width="13" height="10" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M2 5a9 9 0 0 1 12 0M4.5 7.5a5 5 0 0 1 7 0M7 10a1.5 1.5 0 0 1 2 0"/></svg>
                        <svg viewBox="0 0 22 10" width="18" height="8" fill="none" stroke="currentColor" stroke-width="1"><rect x="0.5" y="0.5" width="18" height="9" rx="2"/><rect x="2" y="2" width="14" height="6" rx="1" fill="currentColor"/><rect x="19.5" y="3" width="2" height="4" rx="0.5" fill="currentColor"/></svg>
                      </span>
                    </div>
                    <div class="shorts-chrome-topactions">
                      <svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><circle cx="11" cy="11" r="7"/><line x1="21" y1="21" x2="16.5" y2="16.5"/></svg>
                      <svg viewBox="0 0 24 24" width="16" height="16" fill="currentColor"><circle cx="12" cy="5" r="1.6"/><circle cx="12" cy="12" r="1.6"/><circle cx="12" cy="19" r="1.6"/></svg>
                    </div>
                    <div class="shorts-chrome-rail">
                      <div class="shorts-chrome-action">
                        <svg viewBox="0 0 24 24" width="18" height="18" fill="currentColor"><path d="M14 9V5a3 3 0 0 0-3-3l-4 9v11h11.28a2 2 0 0 0 2-1.7l1.38-9A2 2 0 0 0 19.7 9zM7 22H4a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2h3"/></svg>
                      </div>
                      <div class="shorts-chrome-action">
                        <svg viewBox="0 0 24 24" width="18" height="18" fill="currentColor"><path d="M10 15v4a3 3 0 0 0 3 3l4-9V2H5.72a2 2 0 0 0-2 1.7l-1.38 9A2 2 0 0 0 4.32 15zM17 2h3a2 2 0 0 1 2 2v7a2 2 0 0 1-2 2h-3"/></svg>
                      </div>
                      <div class="shorts-chrome-action">
                        <svg viewBox="0 0 24 24" width="18" height="18" fill="currentColor"><path d="M21 11.5a8.38 8.38 0 0 1-9 8.38 8.5 8.5 0 0 1-3.8-.9L3 21l1.9-5.2A8.5 8.5 0 0 1 4 12 8.38 8.38 0 0 1 12.5 3.5 8.38 8.38 0 0 1 21 11.5z"/></svg>
                      </div>
                      <div class="shorts-chrome-action">
                        <svg viewBox="0 0 24 24" width="18" height="18" fill="currentColor"><path d="M2 21l21-9L2 3v7l15 2-15 2z"/></svg>
                      </div>
                      <div class="shorts-chrome-action">
                        <svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="23 4 23 10 17 10"/><polyline points="1 20 1 14 7 14"/><path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"/></svg>
                      </div>
                      <div class="shorts-chrome-yt">
                        <svg viewBox="0 0 28 20" width="22" height="16"><rect width="28" height="20" rx="5" fill="#ff0033"/><polygon points="11,5 11,15 19,10" fill="#fff"/></svg>
                      </div>
                    </div>
                    <div class="shorts-chrome-bottom">
                      <span class="shorts-chrome-creator" id="publishPreviewCreator">
                        <strong>@your_channel</strong>
                      </span>
                      <span class="shorts-chrome-desc" id="publishPreviewDesc">Your video description here</span>
                    </div>
                  </div>
                </div>
                <h3 id="publishPreviewTitle" style="font-size:15px; margin:12px 0 4px;">Untitled video</h3>
                <div class="hint" id="publishPreviewCaption">Description preview</div>
              </div>
              <div id="publishReadinessChecklist" style="display:none;"></div>
            </div>
          </div>
        </div>

        <div id="pubView-calendar" class="publish-view" style="display:none;">
          <div class="publish-card"><div class="make-section-title"><strong>Calendar</strong><span>Scheduled Phantomline posts</span></div><div class="calendar-grid" id="publishCalendar"></div></div>
        </div>
        <div id="pubView-analytics" class="publish-view" style="display:none;">
          <div class="publish-card">
            <div class="make-section-title"><strong>Analytics</strong><span>Posted links and channel learning</span></div>
            <div class="status-grid">
              <div class="stat"><div class="label">Scheduled</div><div class="value" id="anaScheduled">0</div></div>
              <div class="stat"><div class="label">Posted</div><div class="value" id="anaPosted">0</div></div>
              <div class="stat"><div class="label">Failed</div><div class="value" id="anaFailed">0</div></div>
            </div>
          </div>
          <div class="publish-card">
            <div class="make-section-title"><strong>YouTube Analytics Import</strong><span>CSV / TSV export</span></div>
            <p class="hint" style="margin-top:0;">Export a table from YouTube Studio Analytics, upload it here, and Ollama will turn it into rules for better hooks, titles, topics, captions, and posting tests.</p>
            <input type="file" id="analyticsUploadFile" accept=".csv,.tsv,.txt" />
            <button id="analyticsAnalyzeBtn" class="btn secondary" type="button" style="margin-top:12px;">Analyze with Ollama</button>
            <div class="hint" id="analyticsUploadStatus"></div>
            <div id="analyticsResult" class="publish-list" style="margin-top:12px;">
              <div class="empty-state" style="padding:22px 14px;">
                <h3>No analytics imported yet</h3>
                <p>Upload a YouTube Studio CSV to turn real channel performance into hook, title, SEO, and posting rules.</p>
              </div>
            </div>
          </div>
        </div>
        <div id="pubView-seo" class="publish-view" style="display:none;">
          <div class="publish-card">
            <div class="make-section-title"><strong>SEO Opportunity Finder</strong><span>Any niche or product</span></div>
            <p class="hint" style="margin-top:0;">Type any niche, product, tool, or topic. Phantomline expands search phrases, checks YouTube demand and competition when your API key is available, then keeps only the strongest opportunities.</p>
            <label for="seoNicheInput">Niche / product / topic</label>
            <div class="row">
              <input id="seoNicheInput" type="text" placeholder="makko.ai, AI video editor, survival tips, faceless Reddit stories..." />
              <button id="seoResearchBtn" class="btn" type="button" style="margin-top:0;">Find best keywords</button>
            </div>
            <label class="checkline" style="margin-top:12px;">
              <input type="checkbox" id="seoUseAnalyticsContext" disabled data-blocked-reason="Import channel analytics in Publish first" />
              <span>Use latest analytics import as channel-fit context</span>
            </label>
            <div class="hint" id="seoAnalyticsContextStatus">Upload analytics first if you want SEO ranked against your channel's proven winners.</div>
            <div class="hint" id="seoResearchStatus"></div>
          </div>
          <div id="seoResult" class="publish-list">
            <div class="empty-state" style="padding:22px 14px;">
              <h3>Find a ranking angle</h3>
              <p>Type any niche, product, or topic. Use analytics context when you want keywords ranked against your actual channel winners.</p>
            </div>
          </div>
        </div>
        <div id="pubView-optimize" class="publish-view" style="display:none;">
          <h2>Optimize Library</h2>
          <p class="hint" style="margin-top:0;">
            Pull every video on your connected channel, then deep-analyze them one at a time. Phantomline grades each video against your channel insights and proposes a single tightly-scoped repackaging. Phase 1 is read-only: suggestions only, no writes back to YouTube yet.
          </p>

          <div id="optimizeConnect" class="ip-empty" style="margin-bottom:14px;">
            Connecting to YouTube is required. Open <button type="button" class="ip-link" onclick="switchPublishView('connections')">Connections</button> to connect a channel.
          </div>

          <div id="optimizeWorkspace" style="display:none;">
            <div class="optimize-toolbar">
              <button id="optimizeRefreshBtn" type="button" class="btn secondary" style="width:auto; margin-top:0;">Refresh videos</button>
              <span id="optimizeChannelLabel" class="hint" style="align-self:center;"></span>
              <span id="optimizeCount" class="hint" style="margin-left:auto; align-self:center;"></span>
            </div>

            <div class="optimize-filter-row">
              <button class="filter-btn active" data-vfilter="all" type="button">All</button>
              <button class="filter-btn" data-vfilter="winner" type="button">Winners</button>
              <button class="filter-btn" data-vfilter="mid" type="button">Mid-tier</button>
              <button class="filter-btn" data-vfilter="underperformer" type="button">Underperformers</button>
              <input id="optimizeSearch" placeholder="Search title..." class="optimize-search" />
            </div>

            <div class="optimize-layout">
              <div id="optimizeList" class="optimize-list">
                <div class="ip-empty" style="padding:30px 12px;">No videos loaded yet. Click <strong>Refresh videos</strong>.</div>
              </div>
              <div id="optimizeDetail" class="optimize-detail">
                <div class="ip-empty" style="padding:60px 18px; text-align:center;">
                  <div style="font-size:36px; opacity:0.45; margin-bottom:12px;">🎯</div>
                  <h3 style="margin:0 0 4px;">Pick a video</h3>
                  <p style="margin:0; font-size:13px;">Select a video from the list to deep-analyze with Ollama.</p>
                </div>
              </div>
            </div>
          </div>
        </div>
        <div id="pubView-connections" class="publish-view" style="display:none;">
          <div class="publish-card"><div class="make-section-title"><strong>Connections</strong><span>OAuth accounts</span></div><p id="connectionsText" class="hint"></p><button id="connectionsYoutubeBtn" class="btn" type="button">Connect YouTube</button></div>
        </div>
      </section>
    </div>
  </div>

  <div id="tab-settings" class="tab-content">
    <section class="panel">
      <h2>Settings</h2>
      <p class="hint" style="margin-top:0;">Central defaults for the main Create Video workflow. These apply to the visible creator flow without making users hunt through Advanced tools.</p>
      <div class="settings-grid">
        <div class="settings-card">
          <div class="make-section-title"><strong>Local AI</strong><span>Ollama</span></div>
          <label for="settingsModel">Default Ollama model</label>
          <select id="settingsModel"></select>
          <div class="hint">Used for ideas, titles, scripts, descriptions, analytics, and SEO guidance.</div>
        </div>
        <div class="settings-card">
          <div class="make-section-title"><strong>Channel format</strong><span>sets creative defaults</span></div>
          <div class="hint" style="margin:0 0 10px;">Changing your format auto-fills script direction, visual style, music, hook style, and more across every new video.</div>
          <label for="settingsRecipe">Active format</label>
          <select id="settingsRecipe">
            <option value="custom" selected>Custom faceless channel</option>
            <option value="viral-story">Viral Story Short</option>
            <option value="reddit-story">Reddit-style story</option>
            <option value="survival">Survival tips</option>
            <option value="scary-story">Scary story narration</option>
            <option value="mystery">Mystery documentary</option>
            <option value="finance">Finance explainer</option>
            <option value="history">History explainer</option>
            <option value="kids-edu">Kids educational</option>
            <option value="motivational">Motivational shorts</option>
            <option value="true-crime">True crime</option>
            <option value="asmr-sleep">ASMR / Sleep</option>
            <option value="science">Science explainer</option>
            <option value="conspiracy">Conspiracy / What if</option>
            <option value="top-list">Top 10 / Ranked</option>
            <option value="travel">Travel / Geography</option>
            <option value="philosophy">Philosophy</option>
            <option value="urban-legend">Urban legends</option>
            <option value="news-recap">News recap</option>
            <option value="product-review">Product review</option>
            <option value="tech">Tech explainer</option>
          </select>
          <div class="hint" id="settingsRecipeStatus" style="margin-top:6px;"></div>
        </div>
        <div class="settings-card">
          <div class="make-section-title"><strong>Video defaults</strong><span>short-form safe</span></div>
          <div class="row">
            <div>
              <label for="settingsAspect">Default ratio</label>
              <select id="settingsAspect">
                <option value="9:16">9:16 Shorts / TikTok</option>
                <option value="16:9">16:9 YouTube</option>
                <option value="1:1">1:1 square</option>
              </select>
            </div>
            <div>
              <label for="settingsCaptionStyle">Caption style</label>
              <select id="settingsCaptionStyle">
                <option value="tiktok">TikTok bold</option>
                <option value="clean">Clean lower-third</option>
                <option value="horror">Mystery / horror</option>
                <option value="kids">Kids cartoon</option>
                <option value="documentary">Documentary</option>
              </select>
            </div>
          </div>
        </div>
        <div class="settings-card">
          <div class="make-section-title"><strong>Voice and audio</strong><span>local</span></div>
          <label for="settingsVoice">Default voice</label>
          <select id="settingsVoice"></select>
          <label for="settingsMusicLevel">Music under narration</label>
          <select id="settingsMusicLevel">
            <option value="18">18 dB quieter - default</option>
            <option value="20">20 dB quieter - softer</option>
            <option value="15">15 dB quieter - louder</option>
          </select>
        </div>
        <div class="settings-card">
          <div class="make-section-title"><strong>Local image generation</strong><span>Forge / FLUX</span></div>
          <label for="settingsForgeUrl">Forge URL</label>
          <input id="settingsForgeUrl" type="text" value="http://127.0.0.1:7861" />
          <label for="settingsForgeCheckpoint">Checkpoint</label>
          <input id="settingsForgeCheckpoint" type="text" value="flux1-dev-bnb-nf4-v2.safetensors" />
        </div>
        <div class="settings-card">
          <div class="make-section-title"><strong>Interface</strong><span>show only what you need</span></div>
          <label class="toggle-row">
            <input id="settingsSimpleMode" type="checkbox" checked />
            <span><strong>Simple mode:</strong> hide Advanced tools (Long Story, Short Script, Narrate Text, Music &amp; Mix, Video Studio) from the sidebar. Toggle off when you want every tool visible.</span>
          </label>
          <label class="toggle-row">
            <input id="settingsTelemetry" type="checkbox" checked />
            <span><strong>Anonymous diagnostics:</strong> send error reports and render outcomes so issues are caught fast. No content, no identifiers.</span>
          </label>
        </div>
        <div class="settings-card" id="settingsEngineCard">
          <div class="make-section-title">
            <strong>AI engine</strong>
            <span id="settingsEngineStatus">Loading…</span>
          </div>
          <div class="hint" style="margin:0 0 10px;">Pick where script generation runs. Server uses your Ollama install (fast, full-quality, requires PC). This device runs Llama 3.2 1B in your browser via WebGPU. Works on mobile, downloads once (~1 GB), no PC needed afterward.</div>
          <label class="toggle-row">
            <input type="radio" name="ghEngine" value="server" id="engineServer" />
            <span><strong>Local server (Ollama):</strong> desktop default. 8B model quality. Requires the Phantomline Python server running on this machine.</span>
          </label>
          <label class="toggle-row">
            <input type="radio" name="ghEngine" value="device" id="engineDevice" />
            <span><strong>This device (WebGPU + Llama 3.2 1B):</strong> mobile/PWA path. First use downloads ~1 GB, then runs offline forever. Lower quality than 8B but no backend needed.</span>
          </label>
          <label class="toggle-row">
            <input type="radio" name="ghEngine" value="cloud" id="engineCloud" />
            <span><strong>Cloud (your API key):</strong> <span id="cloudEngineTrialBadge"></span> easiest path. Paste your own API key from Anthropic, OpenAI, Google Gemini, or OpenRouter. Gemini and OpenRouter have free tiers — no credit card. Claude/GPT cost ~$0.005 per script. 2 free trials, then Pro. Key stays in your browser.</span>
          </label>
          <div id="engineLoadProgress" class="hint" style="margin-top:8px; display:none;"></div>
          <div id="cloudKeyPanel" style="margin-top:10px; padding-top:10px; border-top:1px solid var(--border); display:none;">
            <strong style="font-size:13px;">Cloud API key</strong>
            <div class="hint" style="margin:4px 0 8px;">
              Get a key at <a href="https://console.anthropic.com/" target="_blank" rel="noopener" style="color:var(--accent);">console.anthropic.com</a> (Claude), <a href="https://platform.openai.com/api-keys" target="_blank" rel="noopener" style="color:var(--accent);">platform.openai.com</a> (GPT), <a href="https://aistudio.google.com/apikey" target="_blank" rel="noopener" style="color:var(--accent);">aistudio.google.com</a> (Gemini, free), or <a href="https://openrouter.ai/keys" target="_blank" rel="noopener" style="color:var(--accent);">openrouter.ai</a> (free models). Stored only in your browser. Never sent to our server.
            </div>
            <div style="display:flex; gap:8px; margin-bottom:8px; flex-wrap:wrap;">
              <select id="settingsCloudProvider" style="flex:0 0 160px;">
                <option value="anthropic">Anthropic (Claude)</option>
                <option value="openai">OpenAI (GPT)</option>
                <option value="gemini">Google Gemini</option>
                <option value="openrouter">OpenRouter</option>
              </select>
              <input id="settingsCloudModel" type="text" placeholder="model id" style="flex:1; min-width:140px;" />
            </div>
            <input id="settingsCloudKey" type="password" placeholder="API key (sk-… or claude-…)" autocomplete="off" spellcheck="false" />
            <div class="script-actions" style="margin-top:8px;">
              <button id="settingsCloudSaveBtn" class="btn secondary" type="button" style="width:auto;">Save key</button>
              <button id="settingsCloudTestBtn" class="btn secondary" type="button" style="width:auto;">Test key</button>
              <button id="settingsCloudClearBtn" class="btn secondary" type="button" style="width:auto;">Clear</button>
            </div>
            <div class="hint" id="settingsCloudStatus" style="margin-top:6px;"></div>
          </div>
          <div style="margin-top:14px; padding-top:12px; border-top:1px solid var(--border);">
            <strong style="font-size:13px;">Stock video API (optional)</strong>
            <div class="hint" style="margin:4px 0 8px;">For mobile rendering when the server's image gen isn't reachable. Free API, sign up at <a href="https://www.pexels.com/api/" target="_blank" rel="noopener" style="color:var(--accent);">pexels.com/api</a>. Stays in your browser, never sent to our server.</div>
            <input id="settingsPexelsKey" type="password" placeholder="Pexels API key" autocomplete="off" />
            <div class="script-actions" style="margin-top:8px;">
              <button id="settingsPexelsSaveBtn" class="btn secondary" type="button" style="width:auto;">Save key</button>
              <button id="settingsPexelsTestBtn" class="btn secondary" type="button" style="width:auto;">Test search</button>
            </div>
            <div class="hint" id="settingsPexelsStatus" style="margin-top:6px;"></div>
          </div>
        </div>
        <div class="settings-card" id="settingsLicenseCard">
          <div class="make-section-title">
            <strong>License</strong>
            <span id="settingsLicenseTier">Loading...</span>
          </div>
          <div class="hint" id="settingsLicenseStatus">Free tier active.</div>
          <div class="hint" id="settingsUsageStatus" style="margin-top:6px;"></div>
          <div id="settingsPricingGrid" class="settings-pricing-grid" style="margin-top:14px;"></div>
          <label for="settingsLicenseKey" style="margin-top:14px; display:block;">Got a license key?</label>
          <input id="settingsLicenseKey" type="text" placeholder="GHL1.…" autocomplete="off" spellcheck="false" />
          <div class="script-actions" style="margin-top:10px;">
            <button id="settingsLicenseApplyBtn" class="btn secondary" type="button">Apply key</button>
            <button id="settingsLicenseClearBtn" class="btn secondary" type="button">Clear license</button>
          </div>
        </div>
      </div>
      <div class="script-actions" style="margin-top:16px;">
        <button id="settingsApplyBtn" class="btn" type="button">Apply defaults</button>
        <button id="settingsOpenConnectionsBtn" class="btn secondary" type="button">YouTube connections</button>
        <button id="settingsReplayWalkBtn" class="btn secondary" type="button">Replay walkthrough</button>
        <button id="settingsReplayOnboardingBtn" class="btn secondary" type="button">Replay onboarding</button>
        <button id="settingsInstallAppBtn" class="btn secondary" type="button" style="display:none;" data-blocked-reason="Install prompt only appears in supported browsers (Chrome, Edge, Safari on iOS 16+).">Install as app</button>
        <button id="settingsOpenCreateBtn" class="btn secondary" type="button">Back to Create Video</button>
      </div>
      <div class="hint" id="settingsStatus"></div>
    </section>
  </div>

  <div id="tab-library" class="tab-content">
    <section class="panel">
      <h2>Project library</h2>
      <p class="hint" style="margin-top:0;">
        Every story, narration, music bed, and mix you create is saved here. Survives restarts.
      </p>

      <div class="library-toolbar">
        <button class="filter-btn active" data-filter="bundle">Videos</button>
        <button class="filter-btn" data-filter="all">All artifacts</button>
        <button class="filter-btn" data-filter="story">Long stories</button>
        <button class="filter-btn" data-filter="short_script">Shorts</button>
        <button class="filter-btn" data-filter="narration">Narrations</button>
        <button class="filter-btn" data-filter="music">Music beds</button>
        <button class="filter-btn" data-filter="mix">Mixes</button>
        <button class="filter-btn" data-filter="video_plan">Video plans</button>
        <button class="filter-btn" data-filter="timeline">Timelines</button>
        <button class="filter-btn" data-filter="video">Raw MP4s</button>
        <button class="filter-btn" data-filter="upload">Uploads</button>
        <span style="margin-left:auto; color:var(--muted); font-size:12px;" id="libCount"></span>
      </div>

      <div id="libGrid" class="library-grid"></div>
    </section>
  </div>


  <div class="footer">
    Phantomline runs on your machine via Ollama, Kokoro, MusicGen, and optional local Forge. No paid APIs required.
  </div>
</div>

<div id="toast" class="toast"></div>

<aside id="genTray" class="gen-tray" aria-live="polite">
  <div class="gt-head">
    <span><span class="gt-dot"></span>Generating</span>
    <button id="gtClose" class="gt-close" title="Hide">×</button>
  </div>
  <div id="gtList" class="gt-list"></div>
</aside>

<script>
window.GHOSTLINE_DEFAULTS = {
  topic: {{ defaults.topic | tojson }},
  genre: {{ defaults.genre | tojson }},
  tone: {{ defaults.tone | tojson }},
  words: {{ defaults.words | tojson }},
  model: {{ defaults.model | tojson }},
};
</script>
<div id="footageLibraryModal" class="gh-footage-modal" role="dialog" aria-modal="true" aria-labelledby="footageLibraryTitle" aria-hidden="true">
  <div class="gh-footage-card">
    <div class="gh-footage-head">
      <h3 id="footageLibraryTitle">Browse footage library</h3>
      <button type="button" class="gh-footage-close" aria-label="Close">×</button>
    </div>
    <p class="gh-footage-sub">Pick a clip to use as the rolling visual layer. Cached locally on first use.</p>
    <div id="footageLibraryFilters" class="gh-footage-filters" role="tablist" aria-label="Filter footage by category"></div>
    <div id="footageLibraryGrid" class="gh-footage-grid" aria-busy="false"></div>
    <div id="footageLibraryStatus" class="gh-footage-status" aria-live="polite"></div>
  </div>
</div>
<script src="{{ '/static/phantomline.js' | versioned }}" defer></script>
<script src="{{ '/static/profile-widget.js' | versioned }}" defer></script>
<script src="{{ '/static/sync-status.js' | versioned }}" defer></script>
<script src="{{ '/static/install-banner.js' | versioned }}" defer></script>
<script src="{{ '/static/update-banner.js' | versioned }}" defer></script>
</body>
</html>
