← back to Pitchvideo
public/index.html
156 lines
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>PitchVideo · 30-second VC pitch from any idea</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<style>
:root{--bg:#0b0b0d;--ink:#f5f3ee;--accent:#DDAB1E;--green:#22c55e;--dim:#7a7a82;--card:#13131a;--border:#2a2a31}
*{box-sizing:border-box;margin:0;padding:0}
html,body{background:var(--bg);color:var(--ink);font-family:-apple-system,BlinkMacSystemFont,Helvetica,Arial,sans-serif;font-size:15px;line-height:1.55}
.wrap{max-width:840px;margin:0 auto;padding:48px 24px}
header{margin-bottom:48px}
.kicker{color:var(--accent);text-transform:uppercase;letter-spacing:.28em;font-size:.72rem;margin-bottom:1rem;font-weight:600}
h1{font-family:Georgia,serif;font-size:clamp(2.4rem,6vw,3.8rem);letter-spacing:-0.02em;line-height:1.05;margin-bottom:1rem}
.sub{color:#cfcbc1;font-size:1.05rem;max-width:60ch;margin-bottom:1.5rem}
.pricing{display:inline-flex;gap:8px;align-items:center;background:rgba(34,197,94,.12);color:#86efac;padding:6px 12px;border-radius:6px;font-size:13px;font-weight:600}
.example{margin:32px 0;background:var(--card);border:1px solid var(--border);border-radius:12px;padding:24px}
.example h3{font-size:11px;letter-spacing:.15em;color:var(--dim);text-transform:uppercase;margin-bottom:14px}
.example video{width:100%;border-radius:8px;background:#000;aspect-ratio:16/9}
form{background:var(--card);border:1px solid var(--border);border-radius:12px;padding:28px;margin-top:32px}
form h2{font-size:18px;font-weight:600;margin-bottom:6px}
form .help{color:var(--dim);font-size:13px;margin-bottom:20px}
label{display:block;margin-bottom:14px}
label .l{display:block;font-size:12px;color:var(--dim);font-weight:600;letter-spacing:.05em;margin-bottom:6px;text-transform:uppercase}
input,textarea{width:100%;background:#0d0d12;color:var(--ink);border:1px solid var(--border);border-radius:8px;padding:11px 12px;font-size:14px;font-family:inherit;outline:none;line-height:1.5}
input:focus,textarea:focus{border-color:#3a3a47}
textarea{min-height:80px;resize:vertical}
.count{font-size:11px;color:#5a5a62;margin-top:2px;text-align:right}
button{background:var(--accent);color:#0b0b0d;border:none;padding:12px 24px;border-radius:8px;font-size:15px;font-weight:700;cursor:pointer;font-family:inherit;letter-spacing:-0.005em;transition:opacity .15s ease}
button:hover{opacity:.9}
button:disabled{opacity:.4;cursor:not-allowed}
.status{margin-top:24px;padding:16px;background:#0d0d12;border-radius:8px;border:1px solid var(--border);font-size:13.5px;display:none}
.status.show{display:block}
.status.ok{border-color:rgba(34,197,94,.4)}
.status.err{border-color:rgba(239,68,68,.4)}
.status code{background:#1a1a22;padding:1px 5px;border-radius:3px;font-size:11.5px}
.status video{width:100%;margin-top:14px;border-radius:8px;background:#000;aspect-ratio:16/9}
.footer{margin-top:48px;color:#5a5a62;font-size:12px;text-align:center}
.footer a{color:#9aa3b2;text-decoration:none;border-bottom:1px solid #2a2a31}
</style>
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
</head>
<body>
<div class="wrap">
<header>
<div class="kicker">PitchVideo · v0.1 beta</div>
<h1>A 30-second VC pitch from any idea.<br>Email-delivered.</h1>
<p class="sub">Type your idea, your headline, and the scalability story. We hand back a 1080p MP4 with ElevenLabs voiceover, branded slide, and 30-second cadence — investor-ready in under five minutes.</p>
<div class="pricing">🎁 Free during beta · early access to paid tier when launched</div>
</header>
<section class="example">
<h3>Sample output</h3>
<video controls preload="none">
<source src="https://videos.agentabrams.com/Pitch-DesignerLine-2026-05-11.mp4" type="video/mp4">
</video>
<p style="color:var(--dim);font-size:12.5px;margin-top:10px">Example: DesignerLine — vertical platform combining commerce + advocacy for the design trade.</p>
</section>
<form id="orderForm">
<h2>Generate your pitch</h2>
<p class="help">Three fields. Slide + voice + render happens automatically. Video lands by email within ~30 seconds.</p>
<label>
<span class="l">Idea name</span>
<input type="text" name="name" required maxlength="40" placeholder="e.g. AcmeCorp" pattern="[a-zA-Z0-9 ]{2,40}">
<div class="count">≤ 40 chars, letters + numbers only</div>
</label>
<label>
<span class="l">Headline (4-8 words)</span>
<input type="text" name="headline" required maxlength="120" placeholder="e.g. The trade designer's everything-app." id="h">
<div class="count" id="hc">0 / 120</div>
</label>
<label>
<span class="l">Scalability story (1-3 sentences)</span>
<textarea name="scalability" required maxlength="800" placeholder="Why does this scale? Network effects? Compounding moat? Real numbers?" id="s"></textarea>
<div class="count" id="sc">0 / 800</div>
</label>
<label>
<span class="l">Your email</span>
<input type="email" name="email" required placeholder="you@example.com">
</label>
<button type="submit" id="submit">Generate my pitch video →</button>
</form>
<div id="status" class="status"></div>
<div class="footer">
Built on top of the agentabrams pitch pipeline. Same engine that rendered <a href="https://videos.agentabrams.com" target="_blank" rel="noopener noreferrer">108 ideas in 8 minutes</a> for $2 of API cost.
</div>
</div>
<script>
const charCount = (input, counter, max) => {
input.addEventListener('input', () => counter.textContent = `${input.value.length} / ${max}`);
};
charCount(document.getElementById('h'), document.getElementById('hc'), 120);
charCount(document.getElementById('s'), document.getElementById('sc'), 800);
document.getElementById('orderForm').addEventListener('submit', async (e) => {
e.preventDefault();
const btn = document.getElementById('submit');
const status = document.getElementById('status');
btn.disabled = true; btn.textContent = 'Submitting…';
status.className = 'status show';
status.textContent = 'Submitting your order…';
const fd = new FormData(e.target);
const body = Object.fromEntries(fd);
try {
const r = await fetch('/api/order', { method: 'POST', headers: {'Content-Type':'application/json'}, body: JSON.stringify(body) });
const j = await r.json();
if (!r.ok) throw new Error(j.error || 'submit failed');
btn.textContent = 'Rendering…';
status.innerHTML = `Order accepted · job <code>${j.id}</code> · rendering started…`;
pollStatus(j.id);
} catch (err) {
status.className = 'status show err';
status.textContent = 'Error: ' + err.message;
btn.disabled = false; btn.textContent = 'Generate my pitch video →';
}
});
async function pollStatus(id) {
const status = document.getElementById('status');
const btn = document.getElementById('submit');
const start = Date.now();
while (Date.now() - start < 5 * 60 * 1000) {
await new Promise(r => setTimeout(r, 3000));
const r = await fetch('/api/status/' + id);
const job = await r.json();
if (job.status === 'done') {
status.className = 'status show ok';
status.innerHTML = `✓ Done · <a href="${job.video_url}" download style="color:#86efac">Download MP4</a><br>
<video controls preload="metadata"><source src="${job.video_url}" type="video/mp4"></video>`;
btn.disabled = false; btn.textContent = 'Generate another →';
return;
}
if (job.status === 'error') {
status.className = 'status show err';
status.textContent = '✗ Error: ' + (job.error || 'render failed');
btn.disabled = false; btn.textContent = 'Try again →';
return;
}
status.innerHTML = `Job <code>${id}</code> · status: <b>${job.status}</b>… (≈30 sec)`;
}
status.className = 'status show err';
status.textContent = 'Timeout — check back later. Job id: ' + id;
}
</script>
</body>
</html>