#!/usr/bin/env python3
"""
br-video-create — BlackRoad Animated Video Generator
Usage:
  br-video-create <config.json> [output.html]
  br-video-create --interactive
  br-video-create --example

Reads a JSON config defining scenes and generates a complete
animated HTML video following BRAND-LOCK design rules.
"""

import json
import sys
import os
import textwrap
from pathlib import Path
from datetime import datetime

# ── BlackRoad Design Tokens ─────────────────────────────────────────────
GRADIENT = "linear-gradient(90deg,#FF6B2B,#FF2255,#CC00AA,#8844FF,#4488FF,#00D4FF)"
FONTS_URL = "https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500&family=Inter:wght@300;400&display=swap"

EXAMPLE_CONFIG = {
    "title": "My Awesome Video",
    "aspect": "16:9",
    "background": "particles",
    "scenes": [
        {
            "type": "title",
            "pretitle": "INTRODUCING",
            "title": "Something Amazing",
            "subtitle": "Built from scratch.",
            "duration": 4000
        },
        {
            "type": "quote",
            "text": "The best way to predict the future is to build it.",
            "attribution": "— ALAN KAY",
            "duration": 5000
        },
        {
            "type": "stats",
            "heading": "BY THE NUMBERS",
            "items": [
                {"value": "10K", "label": "Users"},
                {"value": "99.9%", "label": "Uptime"},
                {"value": "50ms", "label": "Latency"},
                {"value": "24/7", "label": "Support"}
            ],
            "duration": 5000
        },
        {
            "type": "bullets",
            "heading": "Why Us",
            "items": [
                "Zero cloud dependency",
                "Open source everything",
                "Built on sovereign hardware"
            ],
            "duration": 5000
        },
        {
            "type": "terminal",
            "title": "demo@localhost — zsh",
            "lines": [
                {"type": "input", "text": "curl api.example.com/status"},
                {"type": "success", "text": '  {"status": "operational", "uptime": "99.99%"}'},
                {"type": "input", "text": "deploy --production"},
                {"type": "info", "text": "  Deployed to 3 regions in 2.4s"}
            ],
            "duration": 7000
        },
        {
            "type": "cta",
            "title": "Get Started",
            "subtitle": "example.com",
            "duration": 4000
        }
    ]
}

# ── Scene Types ──────────────────────────────────────────────────────────

def scene_title(s, idx):
    pretitle = s.get("pretitle", "")
    title = s.get("title", "Title")
    subtitle = s.get("subtitle", "")
    gradient_title = s.get("gradient", True)

    title_class = ' class="grad"' if gradient_title else ''
    pretitle_html = f'<div class="mono" style="font-size:13px;letter-spacing:.3em;color:rgba(255,255,255,.2);margin-bottom:16px">{esc(pretitle)}</div>' if pretitle else ''
    subtitle_html = f'<div style="font-size:20px;color:rgba(255,255,255,.3);margin-top:16px;font-weight:300">{esc(subtitle)}</div>' if subtitle else ''

    return f'''<div class="scene" id="s{idx}">
  {pretitle_html}
  <div style="font-size:64px;font-weight:700;text-align:center;line-height:1.15"{title_class}>{esc(title)}</div>
  <div class="grad-line" style="width:150px;margin-top:24px"></div>
  {subtitle_html}
</div>'''


def scene_subtitle(s, idx):
    lines = s.get("lines", [s.get("text", "")])
    size = s.get("size", 28)
    html_lines = "<br>".join(esc(l) for l in lines)
    return f'''<div class="scene" id="s{idx}">
  <div style="text-align:center;max-width:1000px">
    <div style="font-size:{size}px;color:rgba(255,255,255,.3);line-height:2">{html_lines}</div>
    <div class="grad-line" style="width:200px;margin:32px auto"></div>
  </div>
</div>'''


def scene_bigtext(s, idx):
    text = s.get("text", "")
    gradient = s.get("gradient", False)
    size = s.get("size", 64)
    cls = ' class="grad"' if gradient else ''
    return f'''<div class="scene" id="s{idx}">
  <div style="font-size:{size}px;font-weight:700;text-align:center;line-height:1.15"{cls}>{esc(text)}</div>
</div>'''


def scene_quote(s, idx):
    text = s.get("text", "")
    attr = s.get("attribution", "")
    attr_html = f'<div class="quote-attr">{esc(attr)}</div>' if attr else ''
    return f'''<div class="scene" id="s{idx}">
  <div class="quote">"{esc(text)}"</div>
  {attr_html}
</div>'''


def scene_stats(s, idx):
    heading = s.get("heading", "")
    items = s.get("items", [])
    heading_html = f'<div class="mono" style="font-size:14px;letter-spacing:.2em;color:rgba(255,255,255,.2);margin-bottom:40px">{esc(heading)}</div>' if heading else ''

    cards = []
    for i, item in enumerate(items):
        delay = i * 0.2
        cards.append(f'''<div class="stat-card" style="transition-delay:{delay}s">
      <div class="stat-value grad">{esc(str(item["value"]))}</div>
      <div class="stat-label">{esc(item["label"])}</div>
    </div>''')

    return f'''<div class="scene" id="s{idx}" data-anim="stats">
  {heading_html}
  <div class="stats-grid">
    {"".join(cards)}
  </div>
</div>'''


def scene_bullets(s, idx):
    heading = s.get("heading", "")
    items = s.get("items", [])
    icon = s.get("icon", "▸")
    heading_html = f'<div style="font-size:36px;font-weight:600;margin-bottom:40px;text-align:center">{esc(heading)}</div>' if heading else ''

    bullets = []
    for i, item in enumerate(items):
        delay = i * 0.3
        bullets.append(f'<div class="bullet-item" style="transition-delay:{delay}s"><span class="bullet-icon grad">{esc(icon)}</span> {esc(item)}</div>')

    return f'''<div class="scene" id="s{idx}" data-anim="bullets">
  {heading_html}
  <div class="bullet-list">
    {"".join(bullets)}
  </div>
</div>'''


def scene_cards(s, idx):
    heading = s.get("heading", "")
    items = s.get("items", [])
    heading_html = f'<div class="mono" style="font-size:14px;letter-spacing:.2em;color:rgba(255,255,255,.2);margin-bottom:40px">{esc(heading)}</div>' if heading else ''

    cards = []
    for i, item in enumerate(items):
        delay = i * 0.2
        icon = item.get("icon", "")
        name = item.get("name", "")
        desc = item.get("description", "")
        status = item.get("status", "")
        status_cls = "on" if status.lower() in ("online", "active", "ok", "yes") else "off" if status else ""
        status_html = f'<div class="card-status {status_cls}">{esc(status)}</div>' if status else ''
        icon_html = f'<div class="card-icon">{icon}</div>' if icon else ''
        cards.append(f'''<div class="info-card" style="transition-delay:{delay}s">
      {icon_html}
      <div class="card-body"><div class="card-name">{esc(name)}</div><div class="card-desc">{esc(desc)}</div></div>
      {status_html}
    </div>''')

    return f'''<div class="scene" id="s{idx}" data-anim="cards">
  {heading_html}
  <div class="cards-list">
    {"".join(cards)}
  </div>
</div>'''


def scene_timeline(s, idx):
    heading = s.get("heading", "THE TIMELINE")
    items = s.get("items", [])
    heading_html = f'<div class="mono" style="font-size:14px;letter-spacing:.2em;color:rgba(255,255,255,.2);margin-bottom:40px">{esc(heading)}</div>'

    entries = []
    for i, item in enumerate(items):
        delay = i * 0.2
        date = item.get("date", "")
        event = item.get("event", "")
        detail = item.get("detail", "")
        detail_html = f'<div class="tl-detail">{esc(detail)}</div>' if detail else ''
        entries.append(f'''<div class="tl-item" style="transition-delay:{delay}s">
      <div class="tl-date">{esc(date)}</div>
      <div class="tl-event">{esc(event)}</div>
      {detail_html}
    </div>''')

    return f'''<div class="scene" id="s{idx}" data-anim="timeline">
  {heading_html}
  <div class="timeline">
    {"".join(entries)}
  </div>
</div>'''


def scene_terminal(s, idx):
    title = s.get("title", "terminal")
    lines = s.get("lines", [])
    term_id = f"term{idx}"

    lines_json = json.dumps(lines)

    return f'''<div class="scene" id="s{idx}" data-anim="terminal" data-term="{term_id}" data-lines='{lines_json}'>
  <div class="term-window">
    <div class="term-bar">
      <div class="term-dot r"></div><div class="term-dot y"></div><div class="term-dot g"></div>
      <div class="term-title">{esc(title)}</div>
    </div>
    <div class="term-body" id="{term_id}"></div>
  </div>
</div>'''


def scene_comparison(s, idx):
    heading = s.get("heading", "")
    left_title = s.get("left_title", "Before")
    right_title = s.get("right_title", "After")
    left = s.get("left", [])
    right = s.get("right", [])
    heading_html = f'<div style="font-size:36px;font-weight:600;margin-bottom:40px;text-align:center">{esc(heading)}</div>' if heading else ''

    left_items = "".join(f'<div class="cmp-item bad" style="transition-delay:{i*0.15}s"><span class="cmp-x">✗</span> {esc(x)}</div>' for i, x in enumerate(left))
    right_items = "".join(f'<div class="cmp-item good" style="transition-delay:{i*0.15+0.3}s"><span class="cmp-check">✓</span> {esc(x)}</div>' for i, x in enumerate(right))

    return f'''<div class="scene" id="s{idx}" data-anim="comparison">
  {heading_html}
  <div class="compare">
    <div class="compare-col">
      <div class="compare-head" style="color:rgba(255,255,255,.3)">{esc(left_title)}</div>
      {left_items}
    </div>
    <div class="compare-divider"></div>
    <div class="compare-col">
      <div class="compare-head grad">{esc(right_title)}</div>
      {right_items}
    </div>
  </div>
</div>'''


def scene_code(s, idx):
    code = s.get("code", "")
    lang = s.get("language", "")
    caption = s.get("caption", "")
    caption_html = f'<div class="mono" style="font-size:13px;color:rgba(255,255,255,.2);margin-top:16px;letter-spacing:.1em">{esc(caption)}</div>' if caption else ''

    return f'''<div class="scene" id="s{idx}">
  <div class="code-block">
    <div class="code-lang">{esc(lang)}</div>
    <pre class="code-pre">{esc(code)}</pre>
  </div>
  {caption_html}
</div>'''


def scene_split(s, idx):
    """Left text, right visual (big number, icon, or gradient text)"""
    left_lines = s.get("left", [])
    right_text = s.get("right", "")
    right_gradient = s.get("right_gradient", True)

    left_html = "".join(f'<div style="font-size:24px;color:rgba(255,255,255,.4);line-height:2">{esc(l)}</div>' for l in left_lines)
    right_cls = ' class="grad"' if right_gradient else ''

    return f'''<div class="scene" id="s{idx}">
  <div class="split-layout">
    <div class="split-left">{left_html}</div>
    <div class="split-right"><div style="font-size:72px;font-weight:700"{right_cls}>{esc(right_text)}</div></div>
  </div>
</div>'''


def scene_flow(s, idx):
    """Horizontal pipeline / data flow"""
    stages = s.get("stages", [])
    heading = s.get("heading", "")
    heading_html = f'<div style="font-size:36px;font-weight:600;margin-bottom:48px;text-align:center">{esc(heading)}</div>' if heading else ''

    stage_html = []
    for i, st in enumerate(stages):
        delay = i * 0.3
        icon = st.get("icon", "")
        label = st.get("label", "")
        stage_html.append(f'<div class="flow-stage" style="transition-delay:{delay}s"><div class="flow-icon">{icon}</div><div class="flow-label">{esc(label)}</div></div>')
        if i < len(stages) - 1:
            stage_html.append('<div class="flow-arrow">→</div>')

    return f'''<div class="scene" id="s{idx}" data-anim="flow">
  {heading_html}
  <div class="flow-row">
    {"".join(stage_html)}
  </div>
</div>'''


def scene_metrics(s, idx):
    """Grid of metric tiles"""
    items = s.get("items", [])
    heading = s.get("heading", "")
    heading_html = f'<div class="mono" style="font-size:14px;letter-spacing:.2em;color:rgba(255,255,255,.2);margin-bottom:40px">{esc(heading)}</div>' if heading else ''

    tiles = []
    for i, item in enumerate(items):
        delay = i * 0.12
        tiles.append(f'''<div class="metric-tile" style="transition-delay:{delay}s">
      <div class="mv">{esc(str(item.get("value","")))}</div>
      <div class="ml">{esc(item.get("label",""))}</div>
    </div>''')

    return f'''<div class="scene" id="s{idx}" data-anim="metrics">
  {heading_html}
  <div class="metrics-grid">
    {"".join(tiles)}
  </div>
</div>'''


def scene_image(s, idx):
    """Full-screen image or centered image with caption"""
    src = s.get("src", "")
    caption = s.get("caption", "")
    full = s.get("fullscreen", False)
    caption_html = f'<div style="font-size:18px;color:rgba(255,255,255,.3);margin-top:24px">{esc(caption)}</div>' if caption else ''

    if full:
        return f'''<div class="scene" id="s{idx}">
  <img src="{esc(src)}" style="width:100%;height:100%;object-fit:cover;position:absolute;inset:0">
  <div style="position:absolute;bottom:80px;left:80px;z-index:2">{caption_html}</div>
</div>'''
    else:
        return f'''<div class="scene" id="s{idx}">
  <img src="{esc(src)}" style="max-width:80%;max-height:70%;border-radius:12px;border:1px solid rgba(255,255,255,.08)">
  {caption_html}
</div>'''


def scene_logos(s, idx):
    """Row of logos or brand names"""
    items = s.get("items", [])
    heading = s.get("heading", "")
    heading_html = f'<div style="font-size:24px;color:rgba(255,255,255,.3);margin-bottom:40px">{esc(heading)}</div>' if heading else ''

    logos = []
    for i, item in enumerate(items):
        delay = i * 0.15
        if isinstance(item, dict):
            text = item.get("name", "")
            icon = item.get("icon", "")
            logos.append(f'<div class="logo-item" style="transition-delay:{delay}s">{icon}<div>{esc(text)}</div></div>')
        else:
            logos.append(f'<div class="logo-item" style="transition-delay:{delay}s"><div>{esc(str(item))}</div></div>')

    return f'''<div class="scene" id="s{idx}" data-anim="logos">
  {heading_html}
  <div class="logos-row">
    {"".join(logos)}
  </div>
</div>'''


def scene_cta(s, idx):
    title = s.get("title", "")
    subtitle = s.get("subtitle", "")
    gradient_title = s.get("gradient", True)
    links = s.get("links", [])

    title_cls = ' class="grad"' if gradient_title else ''
    subtitle_html = f'<div class="mono" style="font-size:16px;color:rgba(255,255,255,.2);margin-top:20px;letter-spacing:.2em">{esc(subtitle)}</div>' if subtitle else ''

    links_html = ""
    if links:
        link_items = "".join(f'<div class="mono" style="font-size:13px;color:rgba(255,255,255,.2)">{esc(l)}</div>' for l in links)
        links_html = f'<div style="display:flex;gap:48px;margin-top:48px">{link_items}</div>'

    return f'''<div class="scene" id="s{idx}">
  <div class="grad-line" style="width:200px;margin-bottom:32px"></div>
  <div style="font-size:48px;font-weight:700"{title_cls}>{esc(title)}</div>
  {subtitle_html}
  <div class="grad-line" style="width:200px;margin-top:32px"></div>
  {links_html}
</div>'''


def scene_countdown(s, idx):
    """Animated number with label"""
    target = s.get("target", 100)
    label = s.get("label", "")
    prefix = s.get("prefix", "")
    suffix = s.get("suffix", "")

    return f'''<div class="scene" id="s{idx}" data-anim="counter" data-target="{target}" data-prefix="{esc(prefix)}" data-suffix="{esc(suffix)}">
  <div style="text-align:center">
    <div class="counter-value grad" id="counter{idx}">{esc(prefix)}0{esc(suffix)}</div>
    <div style="font-size:28px;color:rgba(255,255,255,.4);margin-top:16px">{esc(label)}</div>
  </div>
</div>'''


def scene_reveal(s, idx):
    """Word-by-word reveal of a statement"""
    words = s.get("text", "").split()
    gradient_words = s.get("gradient_words", [])

    word_spans = []
    for i, w in enumerate(words):
        delay = i * 0.15
        cls = "reveal-word grad" if w.strip(".,!?") in gradient_words else "reveal-word"
        word_spans.append(f'<span class="{cls}" style="transition-delay:{delay}s">{esc(w)}</span>')

    return f'''<div class="scene" id="s{idx}" data-anim="reveal">
  <div class="reveal-text">
    {" ".join(word_spans)}
  </div>
</div>'''


SCENE_RENDERERS = {
    "title": scene_title,
    "subtitle": scene_subtitle,
    "bigtext": scene_bigtext,
    "quote": scene_quote,
    "stats": scene_stats,
    "bullets": scene_bullets,
    "cards": scene_cards,
    "timeline": scene_timeline,
    "terminal": scene_terminal,
    "comparison": scene_comparison,
    "code": scene_code,
    "split": scene_split,
    "flow": scene_flow,
    "metrics": scene_metrics,
    "image": scene_image,
    "logos": scene_logos,
    "cta": scene_cta,
    "countdown": scene_countdown,
    "reveal": scene_reveal,
}


def esc(text):
    """Escape HTML entities (minimal — preserve intentional HTML in content)"""
    if not text:
        return ""
    return str(text).replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")


def bg_particles():
    return '''<canvas id="bgCanvas"></canvas>
<script>
(function(){
const c=document.getElementById('bgCanvas'),x=c.getContext('2d');
c.width=parseInt(document.body.style.width||1920);c.height=parseInt(document.body.style.height||1080);
const N=50,ps=Array.from({length:N},()=>({x:Math.random()*c.width,y:Math.random()*c.height,vx:(Math.random()-.5)*.6,vy:(Math.random()-.5)*.6,r:Math.random()*2+.5}));
const g=x.createLinearGradient(0,0,c.width,0);
g.addColorStop(0,'#FF6B2B');g.addColorStop(.2,'#FF2255');g.addColorStop(.4,'#CC00AA');g.addColorStop(.6,'#8844FF');g.addColorStop(.8,'#4488FF');g.addColorStop(1,'#00D4FF');
function d(){x.clearRect(0,0,c.width,c.height);
ps.forEach(p=>{p.x+=p.vx;p.y+=p.vy;if(p.x<0||p.x>c.width)p.vx*=-1;if(p.y<0||p.y>c.height)p.vy*=-1;x.fillStyle='rgba(255,255,255,.5)';x.beginPath();x.arc(p.x,p.y,p.r,0,Math.PI*2);x.fill()});
x.strokeStyle=g;x.lineWidth=.5;x.globalAlpha=.12;
for(let i=0;i<N;i++)for(let j=i+1;j<N;j++){const dx=ps[i].x-ps[j].x,dy=ps[i].y-ps[j].y;if(dx*dx+dy*dy<40000){x.beginPath();x.moveTo(ps[i].x,ps[i].y);x.lineTo(ps[j].x,ps[j].y);x.stroke()}}
x.globalAlpha=1;requestAnimationFrame(d)}d()})();
</script>'''


def bg_mesh():
    return '''<canvas id="bgCanvas"></canvas>
<script>
(function(){
const c=document.getElementById('bgCanvas'),x=c.getContext('2d');
c.width=parseInt(document.body.style.width||1920);c.height=parseInt(document.body.style.height||1080);
const N=60,ps=Array.from({length:N},()=>({x:Math.random()*c.width,y:Math.random()*c.height,vx:(Math.random()-.5)*.8,vy:(Math.random()-.5)*.8,r:Math.random()*2+1}));
const g=x.createLinearGradient(0,0,c.width,0);
g.addColorStop(0,'#FF6B2B');g.addColorStop(.2,'#FF2255');g.addColorStop(.4,'#CC00AA');g.addColorStop(.6,'#8844FF');g.addColorStop(.8,'#4488FF');g.addColorStop(1,'#00D4FF');
function d(){x.clearRect(0,0,c.width,c.height);
ps.forEach(p=>{p.x+=p.vx;p.y+=p.vy;if(p.x<0||p.x>c.width)p.vx*=-1;if(p.y<0||p.y>c.height)p.vy*=-1;x.fillStyle='#fff';x.beginPath();x.arc(p.x,p.y,p.r,0,Math.PI*2);x.fill()});
x.strokeStyle=g;x.lineWidth=.5;x.globalAlpha=.15;
for(let i=0;i<N;i++)for(let j=i+1;j<N;j++){const dx=ps[i].x-ps[j].x,dy=ps[i].y-ps[j].y;if(dx*dx+dy*dy<40000){x.beginPath();x.moveTo(ps[i].x,ps[i].y);x.lineTo(ps[j].x,ps[j].y);x.stroke()}}
x.globalAlpha=1;requestAnimationFrame(d)}d()})();
</script>'''


def bg_grid():
    return '''<canvas id="bgCanvas"></canvas>
<script>
(function(){
const c=document.getElementById('bgCanvas'),x=c.getContext('2d');
c.width=parseInt(document.body.style.width||1920);c.height=parseInt(document.body.style.height||1080);
x.strokeStyle='rgba(255,255,255,.03)';x.lineWidth=1;
const s=60;
for(let i=0;i<=c.width;i+=s){x.beginPath();x.moveTo(i,0);x.lineTo(i,c.height);x.stroke()}
for(let j=0;j<=c.height;j+=s){x.beginPath();x.moveTo(0,j);x.lineTo(c.width,j);x.stroke()}
// Pulse a random intersection
let px=0,py=0;
function pulse(){
x.clearRect(px-20,py-20,40,40);
x.strokeStyle='rgba(255,255,255,.03)';
[px,py]=[Math.floor(Math.random()*(c.width/s))*s, Math.floor(Math.random()*(c.height/s))*s];
x.fillStyle='rgba(255,255,255,.08)';x.beginPath();x.arc(px,py,3,0,Math.PI*2);x.fill();
setTimeout(pulse,800+Math.random()*2000)}
pulse()})();
</script>'''


def bg_none():
    return ''


BG_RENDERERS = {
    "particles": bg_particles,
    "mesh": bg_mesh,
    "grid": bg_grid,
    "none": bg_none,
    "": bg_none,
}


def generate(config):
    title = config.get("title", "BlackRoad Video")
    aspect = config.get("aspect", "16:9")
    bg_type = config.get("background", "none")
    cinematic = config.get("cinematic_bars", False)
    scenes = config.get("scenes", [])

    if aspect == "9:16":
        w, h = 1080, 1920
    else:
        w, h = 1920, 1080

    # Render scenes
    scene_htmls = []
    durations = []
    for i, s in enumerate(scenes):
        stype = s.get("type", "title")
        renderer = SCENE_RENDERERS.get(stype)
        if not renderer:
            print(f"Warning: Unknown scene type '{stype}', skipping", file=sys.stderr)
            continue
        scene_htmls.append(renderer(s, i + 1))
        durations.append(s.get("duration", 4000))

    # Background
    bg_html = BG_RENDERERS.get(bg_type, bg_none)()

    # Cinematic bars
    cinema_html = ""
    if cinematic:
        cinema_html = '<div class="cinema-top" id="ctop"></div><div class="cinema-bot" id="cbot"></div>'

    durations_js = json.dumps(durations)

    return f'''<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width={w},height={h}">
<title>{esc(title)}</title>
<link href="{FONTS_URL}" rel="stylesheet">
<style>
*{{margin:0;padding:0;box-sizing:border-box}}
html,body{{width:{w}px;height:{h}px;overflow:hidden;background:#000;color:#fff;font-family:'Space Grotesk',sans-serif}}

/* Scene system */
.scene{{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;flex-direction:column;padding:{80 if aspect=="9:16" else 60}px;opacity:0;transition:opacity .8s;z-index:1}}
.scene.active{{opacity:1}}
.mono{{font-family:'JetBrains Mono',monospace}}
.grad{{background:{GRADIENT};-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}}
.grad-line{{height:3px;border-radius:2px;background:{GRADIENT}}}

/* Canvas background */
#bgCanvas{{position:fixed;inset:0;z-index:0;opacity:.4}}

/* Cinematic bars */
.cinema-top,.cinema-bot{{position:fixed;left:0;right:0;background:#000;z-index:100;transition:height 1.5s ease}}
.cinema-top{{top:0;height:80px}}.cinema-bot{{bottom:0;height:80px}}
.cinema-top.open,.cinema-bot.open{{height:0}}

/* Quote */
.quote{{font-size:{32 if aspect=="9:16" else 36}px;font-weight:300;line-height:1.6;max-width:{800 if aspect=="9:16" else 900}px;text-align:center;font-style:italic;color:rgba(255,255,255,.7)}}
.quote-attr{{font-family:'JetBrains Mono',monospace;font-size:14px;color:rgba(255,255,255,.3);margin-top:24px;letter-spacing:.1em}}

/* Stats */
.stats-grid{{display:flex;gap:{32 if aspect=="9:16" else 48}px;flex-wrap:wrap;justify-content:center}}
.stat-card{{text-align:center;opacity:0;transform:translateY(20px);transition:all .5s}}
.stat-card.visible{{opacity:1;transform:translateY(0)}}
.stat-value{{font-size:{56 if aspect=="9:16" else 64}px;font-weight:700}}
.stat-label{{font-family:'JetBrains Mono',monospace;font-size:13px;color:rgba(255,255,255,.3);margin-top:8px;letter-spacing:.15em;text-transform:uppercase}}

/* Bullets */
.bullet-list{{max-width:800px;width:100%}}
.bullet-item{{font-size:{22 if aspect=="9:16" else 26}px;padding:16px 0;opacity:0;transform:translateX(-20px);transition:all .5s;color:rgba(255,255,255,.6)}}
.bullet-item.visible{{opacity:1;transform:translateX(0)}}
.bullet-icon{{margin-right:16px;font-weight:700}}

/* Cards */
.cards-list{{width:100%;max-width:{900 if aspect=="9:16" else 1200}px;display:flex;flex-direction:column;gap:16px}}
.info-card{{display:flex;align-items:center;gap:20px;padding:24px 28px;border:1px solid rgba(255,255,255,.08);border-radius:12px;opacity:0;transform:translateY(20px);transition:all .5s}}
.info-card.visible{{opacity:1;transform:translateY(0)}}
.card-icon{{font-size:32px;width:52px;height:52px;display:flex;align-items:center;justify-content:center;border-radius:10px;border:1px solid rgba(255,255,255,.08)}}
.card-body{{flex:1}}.card-name{{font-size:20px;font-weight:600}}.card-desc{{font-size:14px;color:rgba(255,255,255,.3);margin-top:4px;font-family:'JetBrains Mono',monospace}}
.card-status{{font-size:13px;font-family:'JetBrains Mono',monospace}}.card-status.on{{color:#22c55e}}.card-status.off{{color:#ef4444}}

/* Timeline */
.timeline{{width:{800 if aspect=="9:16" else 1100}px;position:relative;padding-left:40px;border-left:1px solid rgba(255,255,255,.08)}}
.tl-item{{padding:16px 0 16px 36px;position:relative;opacity:0;transform:translateX(-20px);transition:all .5s}}
.tl-item.visible{{opacity:1;transform:translateX(0)}}
.tl-item::before{{content:'';position:absolute;left:-5px;top:24px;width:9px;height:9px;border-radius:50%;background:{GRADIENT}}}
.tl-date{{font-family:'JetBrains Mono',monospace;font-size:12px;color:rgba(255,255,255,.3);letter-spacing:.1em}}
.tl-event{{font-size:20px;font-weight:500;margin-top:4px}}
.tl-detail{{font-size:14px;color:rgba(255,255,255,.3);margin-top:4px}}

/* Terminal */
.term-window{{width:{900 if aspect=="9:16" else 1400}px;background:#0a0a0a;border:1px solid rgba(255,255,255,.1);border-radius:12px;overflow:hidden}}
.term-bar{{height:40px;background:rgba(255,255,255,.03);border-bottom:1px solid rgba(255,255,255,.06);display:flex;align-items:center;padding:0 16px;gap:8px}}
.term-dot{{width:12px;height:12px;border-radius:50%}}.term-dot.r{{background:#ff5f57}}.term-dot.y{{background:#febc2e}}.term-dot.g{{background:#28c840}}
.term-title{{flex:1;text-align:center;font-family:'JetBrains Mono',monospace;font-size:12px;color:rgba(255,255,255,.3)}}
.term-body{{padding:24px 28px;font-family:'JetBrains Mono',monospace;font-size:{14 if aspect=="9:16" else 16}px;line-height:1.9;min-height:300px}}
.term-body .prompt{{color:#8844FF}}.term-body .cmd{{color:#fff}}.term-body .output{{color:rgba(255,255,255,.5)}}
.term-body .success{{color:#22c55e}}.term-body .error{{color:#ef4444}}.term-body .info{{color:#4488FF}}
.term-body .pink{{color:#FF2255}}.term-body .dim{{color:rgba(255,255,255,.2)}}
.tline{{opacity:0;transform:translateY(5px);transition:all .3s}}.tline.show{{opacity:1;transform:translateY(0)}}

/* Comparison */
.compare{{display:flex;gap:48px;align-items:flex-start;max-width:1100px}}
.compare-col{{flex:1}}.compare-head{{font-size:20px;font-weight:600;margin-bottom:20px;text-align:center}}
.compare-divider{{width:1px;background:rgba(255,255,255,.08);align-self:stretch}}
.cmp-item{{padding:12px 0;font-size:18px;opacity:0;transform:translateX(-10px);transition:all .4s}}
.cmp-item.visible{{opacity:1;transform:translateX(0)}}
.cmp-x{{color:#ef4444;margin-right:12px}}.cmp-check{{color:#22c55e;margin-right:12px}}
.cmp-item.bad{{color:rgba(255,255,255,.3);text-decoration:line-through}}

/* Code block */
.code-block{{background:rgba(255,255,255,.02);border:1px solid rgba(255,255,255,.08);border-radius:12px;padding:32px 36px;max-width:1000px;width:100%}}
.code-lang{{font-family:'JetBrains Mono',monospace;font-size:11px;color:rgba(255,255,255,.2);margin-bottom:16px;letter-spacing:.1em;text-transform:uppercase}}
.code-pre{{font-family:'JetBrains Mono',monospace;font-size:16px;line-height:1.8;color:rgba(255,255,255,.7);white-space:pre-wrap}}

/* Split */
.split-layout{{display:flex;align-items:center;gap:80px;width:100%;max-width:1400px}}
.split-left,.split-right{{flex:1}}
.split-right{{text-align:center}}

/* Flow */
.flow-row{{display:flex;align-items:center;gap:24px;justify-content:center;flex-wrap:wrap}}
.flow-stage{{text-align:center;opacity:0;transform:scale(.8);transition:all .5s}}.flow-stage.visible{{opacity:1;transform:scale(1)}}
.flow-icon{{font-size:40px;width:80px;height:80px;display:flex;align-items:center;justify-content:center;border:1px solid rgba(255,255,255,.08);border-radius:16px;margin-bottom:12px}}
.flow-label{{font-size:14px;color:rgba(255,255,255,.5);font-family:'JetBrains Mono',monospace}}
.flow-arrow{{font-size:28px;color:rgba(255,255,255,.15);margin:0 8px}}

/* Metrics grid */
.metrics-grid{{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:16px;max-width:1000px;width:100%}}
.metric-tile{{border:1px solid rgba(255,255,255,.06);border-radius:10px;padding:24px;text-align:center;opacity:0;transform:translateY(15px);transition:all .4s}}
.metric-tile.visible{{opacity:1;transform:translateY(0)}}
.mv{{font-size:28px;font-weight:700}}.ml{{font-family:'JetBrains Mono',monospace;font-size:11px;color:rgba(255,255,255,.3);margin-top:8px;letter-spacing:.1em;text-transform:uppercase}}

/* Logos */
.logos-row{{display:flex;gap:48px;align-items:center;justify-content:center;flex-wrap:wrap}}
.logo-item{{text-align:center;font-size:18px;font-weight:500;opacity:0;transform:translateY(10px);transition:all .4s;color:rgba(255,255,255,.4)}}
.logo-item.visible{{opacity:1;transform:translateY(0)}}

/* Counter */
.counter-value{{font-size:120px;font-weight:700}}

/* Reveal */
.reveal-text{{font-size:{40 if aspect=="9:16" else 52}px;font-weight:700;text-align:center;line-height:1.4;max-width:1100px}}
.reveal-word{{display:inline-block;opacity:0;transform:translateY(15px);transition:all .4s}}.reveal-word.visible{{opacity:1;transform:translateY(0)}}
</style>
</head>
<body>
{cinema_html}
{bg_html}

{chr(10).join(scene_htmls)}

<script>
// Terminal typing engine
function typeTerminal(id, lines, charDelay) {{
  charDelay = charDelay || 30;
  const el = document.getElementById(id);
  if (!el) return;
  el.innerHTML = '';
  let totalDelay = 0;
  lines.forEach(function(line) {{
    const div = document.createElement('div');
    div.className = 'tline';
    if (line.type === 'input') {{
      totalDelay += 200;
      const ps = '<span class="prompt">$</span> ';
      div.innerHTML = ps;
      el.appendChild(div);
      setTimeout(function(){{ div.classList.add('show') }}, totalDelay);
      const chars = line.text.split('');
      chars.forEach(function(ch, ci) {{
        totalDelay += charDelay;
        setTimeout(function() {{
          div.innerHTML = ps + '<span class="cmd">' + line.text.slice(0, ci + 1) + '</span><span class="dim">_</span>';
        }}, totalDelay);
      }});
      totalDelay += 400;
      setTimeout(function() {{
        div.innerHTML = ps + '<span class="cmd">' + line.text + '</span>';
      }}, totalDelay);
    }} else {{
      totalDelay += line.delay || 80;
      div.innerHTML = '<span class="' + (line.type || 'output') + '">' + line.text + '</span>';
      el.appendChild(div);
      setTimeout(function(){{ div.classList.add('show') }}, totalDelay);
    }}
  }});
}}

// Scene controller
const scenes = document.querySelectorAll('.scene');
const durations = {durations_js};
let cur = 0;

function animateScene(el) {{
  const anim = el.dataset.anim;
  if (!anim) return;

  if (anim === 'stats') el.querySelectorAll('.stat-card').forEach(function(c,j){{ setTimeout(function(){{ c.classList.add('visible') }}, j*200) }});
  if (anim === 'bullets') el.querySelectorAll('.bullet-item').forEach(function(c,j){{ setTimeout(function(){{ c.classList.add('visible') }}, j*300) }});
  if (anim === 'cards') el.querySelectorAll('.info-card').forEach(function(c,j){{ setTimeout(function(){{ c.classList.add('visible') }}, j*200) }});
  if (anim === 'timeline') el.querySelectorAll('.tl-item').forEach(function(c,j){{ setTimeout(function(){{ c.classList.add('visible') }}, j*300) }});
  if (anim === 'comparison') el.querySelectorAll('.cmp-item').forEach(function(c,j){{ setTimeout(function(){{ c.classList.add('visible') }}, j*150) }});
  if (anim === 'flow') el.querySelectorAll('.flow-stage').forEach(function(c,j){{ setTimeout(function(){{ c.classList.add('visible') }}, j*300) }});
  if (anim === 'metrics') el.querySelectorAll('.metric-tile').forEach(function(c,j){{ setTimeout(function(){{ c.classList.add('visible') }}, j*120) }});
  if (anim === 'logos') el.querySelectorAll('.logo-item').forEach(function(c,j){{ setTimeout(function(){{ c.classList.add('visible') }}, j*150) }});
  if (anim === 'reveal') el.querySelectorAll('.reveal-word').forEach(function(c,j){{ setTimeout(function(){{ c.classList.add('visible') }}, j*150) }});

  if (anim === 'terminal') {{
    const termId = el.dataset.term;
    try {{ var lines = JSON.parse(el.dataset.lines); typeTerminal(termId, lines); }} catch(e){{}}
  }}

  if (anim === 'counter') {{
    const target = parseInt(el.dataset.target) || 100;
    const prefix = el.dataset.prefix || '';
    const suffix = el.dataset.suffix || '';
    const counterEl = el.querySelector('.counter-value');
    if (!counterEl) return;
    let current = 0;
    const step = Math.max(1, Math.floor(target / 60));
    const iv = setInterval(function() {{
      current = Math.min(current + step, target);
      counterEl.textContent = prefix + current.toLocaleString() + suffix;
      if (current >= target) clearInterval(iv);
    }}, 25);
  }}
}}

function show(i) {{
  if (i >= scenes.length) return;
  scenes.forEach(function(s) {{ s.classList.remove('active') }});
  scenes[i].classList.add('active');
  animateScene(scenes[i]);
  {"// Cinematic bars" if cinematic else ""}
  {"if(i===1){document.getElementById('ctop').classList.add('open');document.getElementById('cbot').classList.add('open')}" if cinematic else ""}
  {"if(i===scenes.length-1){document.getElementById('ctop').classList.remove('open');document.getElementById('cbot').classList.remove('open')}" if cinematic else ""}
  setTimeout(function(){{ show(i + 1) }}, durations[i]);
}}
show(0);
</script>
</body>
</html>'''


def print_usage():
    print("""
\033[38;5;205m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\033[0m
\033[38;5;205m  br-video-create — Animated Video Generator\033[0m
\033[38;5;205m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\033[0m

  \033[38;5;82mUsage:\033[0m
    br-video-create <config.json> [output.html]
    br-video-create --example
    br-video-create --types

  \033[38;5;135mScene Types:\033[0m
    title        Hero title with optional pretitle/subtitle
    subtitle     Multi-line subtitle text
    bigtext      Single big statement
    quote        Quoted text with attribution
    stats        Animated stat counters in a row
    bullets      Bullet point list with stagger
    cards        Info cards with icon/name/desc/status
    timeline     Vertical timeline with dates
    terminal     Typing terminal with colored output
    comparison   Side-by-side before/after
    code         Syntax-highlighted code block
    split        Left text, right big number/word
    flow         Horizontal pipeline stages
    metrics      Grid of metric tiles
    image        Image with optional caption
    logos        Row of brand names/icons
    cta          Call-to-action end card
    countdown    Animated counting number
    reveal       Word-by-word text reveal

  \033[38;5;135mBackgrounds:\033[0m  particles, mesh, grid, none

  \033[38;5;135mAspect Ratios:\033[0m  16:9 (1920x1080), 9:16 (1080x1920)

  \033[38;5;69mExample:\033[0m
    br-video-create --example > my-config.json
    br-video-create my-config.json my-video.html
    open my-video.html
""")


def print_types():
    print(json.dumps(list(SCENE_RENDERERS.keys()), indent=2))


def main():
    args = sys.argv[1:]

    if not args or args[0] in ("-h", "--help"):
        print_usage()
        sys.exit(0)

    if args[0] == "--example":
        print(json.dumps(EXAMPLE_CONFIG, indent=2))
        sys.exit(0)

    if args[0] == "--types":
        print_types()
        sys.exit(0)

    # Read config
    config_path = args[0]
    if config_path == "-":
        config = json.load(sys.stdin)
    else:
        with open(config_path) as f:
            config = json.load(f)

    # Generate
    html = generate(config)

    # Output
    if len(args) > 1:
        output_path = args[1]
        os.makedirs(os.path.dirname(output_path) or ".", exist_ok=True)
        with open(output_path, "w") as f:
            f.write(html)
        print(f"\033[38;5;82m✓ Generated: {output_path}\033[0m")
        print(f"\033[38;5;69m  Scenes: {len(config.get('scenes',[]))}\033[0m")
        print(f"\033[38;5;69m  Aspect: {config.get('aspect','16:9')}\033[0m")
        print(f"\033[38;5;69m  Background: {config.get('background','none')}\033[0m")
    else:
        print(html)


if __name__ == "__main__":
    main()
