<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Alma AI Website Builder</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,300;9..144,400;9..144,500;9..144,600&family=Manrope:wght@300;400;500;600;700&display=swap" rel="stylesheet" />
<style>
:root{
--base:#0E0B08; --ink2:#141009; --surface:#1A140C; --surface2:#221A10;
--line:rgba(242,235,221,.12); --line2:rgba(242,235,221,.22);
--cream:#F2EBDD; --dim:#CBBEA6; --muted:#8E8268;
--accent:#D98E4F; --accent-soft:#E8A968; --accent-deep:#B5703A;
--ok:#7FB988;
--maxw:1200px; --ease:cubic-bezier(.22,.61,.36,1);
--shadow:0 40px 90px -40px rgba(0,0,0,.85);
}
*{box-sizing:border-box}
html,body{margin:0}
body{
background:
radial-gradient(1100px 700px at 84% -10%, rgba(217,142,79,.12), transparent 62%),
var(--base);
color:var(--cream);
font-family:"Manrope",system-ui,sans-serif;
font-size:16.5px; font-weight:400; line-height:1.62; -webkit-font-smoothing:antialiased;
}
h1,h2,h3,h4{font-family:"Fraunces",Georgia,serif;font-weight:400;line-height:1.04;margin:0;letter-spacing:-.012em}
a{color:inherit;text-decoration:none}
button,input,textarea,select{font-family:inherit}
.wrap{width:min(100% - 44px,var(--maxw));margin-inline:auto}
.serif{font-family:"Fraunces",Georgia,serif}
.eyebrow{font-size:.72rem;letter-spacing:.28em;text-transform:uppercase;font-weight:600;color:var(--accent)}
.muted{color:var(--muted)} .dim{color:var(--dim)}
.accent{color:var(--accent)}

.btn{display:inline-flex;align-items:center;justify-content:center;gap:9px;font-weight:600;font-size:.93rem;letter-spacing:.01em;padding:14px 26px;border-radius:2px;border:1px solid transparent;cursor:pointer;transition:transform .25s var(--ease),background .25s,color .25s,border-color .25s;white-space:nowrap}
.btn:focus-visible{outline:2px solid var(--accent);outline-offset:3px}
.btn:disabled{opacity:.45;cursor:not-allowed}
.btn-primary{background:var(--accent);color:#1a0f06}
.btn-primary:hover:not(:disabled){background:var(--accent-soft);transform:translateY(-2px)}
.btn-ghost{background:transparent;border-color:var(--line2);color:var(--cream)}
.btn-ghost:hover:not(:disabled){border-color:var(--accent);transform:translateY(-2px)}
.btn-line{background:transparent;border:none;color:var(--cream);padding:14px 4px;position:relative}
.btn-line:after{content:"";position:absolute;left:4px;right:4px;bottom:8px;height:1px;background:var(--accent);transform:scaleX(.4);transform-origin:left;transition:transform .3s var(--ease)}
.btn-line:hover:after{transform:scaleX(1)}
.btn-sm{padding:10px 16px;font-size:.84rem}

.screen{display:none;animation:fade .55s var(--ease)}
.screen.on{display:block}
@keyframes fade{from{opacity:0;transform:translateY(12px)}to{opacity:1;transform:none}}

/* top bar */
.topbar{position:sticky;top:0;z-index:50;backdrop-filter:blur(12px);background:linear-gradient(180deg,rgba(14,11,8,.94),rgba(14,11,8,.55));border-bottom:1px solid var(--line)}
.topbar-in{display:flex;align-items:center;gap:18px;padding:16px 0;width:min(100% - 44px,var(--maxw));margin-inline:auto}
.logo{display:flex;align-items:baseline;gap:11px;margin-right:auto}
.logo .nm{font-family:"Fraunces";font-size:1.35rem;font-weight:500;letter-spacing:-.01em}
.logo .nm b{color:var(--accent);font-weight:500}
.logo small{font-size:.64rem;letter-spacing:.26em;text-transform:uppercase;color:var(--muted)}
.steps{display:flex;gap:2px;align-items:center}
.steps .s{display:flex;align-items:center;gap:8px;font-size:.78rem;color:var(--muted);font-weight:500;padding:6px 12px}
.steps .s .n{font-family:"Fraunces";font-size:.92rem;color:var(--muted);width:22px;text-align:center}
.steps .s.active{color:var(--cream)} .steps .s.active .n{color:var(--accent)}
.steps .s.done .n{color:var(--cream)}
.steps .sep{width:16px;height:1px;background:var(--line2)}

/* intake */
.intake{padding:96px 0 110px}
.intake .eyebrow{margin-bottom:26px;display:block}
.intake h1{font-size:clamp(3rem,8vw,6.4rem);font-weight:300;letter-spacing:-.03em;max-width:16ch}
.intake h1 em{font-style:italic;color:var(--accent);font-weight:400}
.intake .sub{font-size:1.18rem;color:var(--dim);max-width:54ch;margin:30px 0 0;font-weight:300}
.intake-card{margin:50px 0 0;max-width:740px;border-top:1px solid var(--line2);padding-top:34px}
.field{margin-bottom:20px}
.field label{display:block;font-size:.7rem;letter-spacing:.2em;text-transform:uppercase;font-weight:600;color:var(--muted);margin-bottom:10px}
.field input{width:100%;background:transparent;border:none;border-bottom:1px solid var(--line2);color:var(--cream);font-size:1.4rem;font-family:"Fraunces";font-weight:300;padding:8px 0 12px;transition:.25s}
.field input:focus{outline:none;border-color:var(--accent)}
.field input::placeholder{color:var(--muted);font-style:italic}
.socials{display:grid;grid-template-columns:1fr 1fr;gap:20px 30px;margin-top:6px}
.socials .field{margin-bottom:0}
.socials input{font-size:1rem;font-family:"Manrope";font-weight:400}
.intake-actions{display:flex;gap:16px;align-items:center;margin-top:34px}
.hint{font-size:.86rem;color:var(--muted);margin-top:18px}
.feat-row{display:flex;gap:28px;flex-wrap:wrap;margin-top:64px;border-top:1px solid var(--line);padding-top:26px}
.feat{font-size:.82rem;color:var(--dim);font-weight:300}
.feat b{color:var(--cream);font-weight:500;font-family:"Fraunces"}

/* loading */
.loading{min-height:64vh;display:grid;place-items:center;text-align:center}
.mono{width:8px;height:8px;border-radius:50%;background:var(--accent);margin:0 auto 34px;box-shadow:0 0 0 0 rgba(217,142,79,.5);animation:beat 2.4s var(--ease) infinite}
@keyframes beat{0%,100%{box-shadow:0 0 0 0 rgba(217,142,79,.45);transform:scale(1)}50%{box-shadow:0 0 0 16px rgba(217,142,79,0);transform:scale(1.25)}}
.loading h2{font-size:clamp(1.8rem,4vw,2.6rem);font-weight:300}
.loading .ld-msg{color:var(--dim);font-size:1.06rem;margin-top:14px;min-height:1.6em;transition:opacity .3s;font-weight:300;font-style:italic;font-family:"Fraunces"}
.prog{max-width:440px;margin:34px auto 0}
.prog-bar{height:1px;background:var(--line2);overflow:hidden}
.prog-fill{height:100%;width:8%;background:var(--accent);transition:width .6s var(--ease)}
.prog-list{margin-top:24px;text-align:left;display:grid;gap:11px}
.prog-item{display:flex;align-items:center;gap:12px;font-size:.92rem;color:var(--muted);font-weight:300}
.prog-item.run{color:var(--cream)} .prog-item.ok{color:var(--dim)}
.prog-item .ico{font-family:"Fraunces";width:18px;color:var(--accent)}

/* generic section pad */
.pad{padding:64px 0 100px}
.head-2{max-width:64ch;margin-bottom:40px}
.head-2 .eyebrow{display:block;margin-bottom:18px}
.head-2 h2{font-size:clamp(2.2rem,5vw,3.6rem);font-weight:300}
.head-2 p{color:var(--dim);margin-top:16px;font-weight:300;font-size:1.08rem}

/* verify */
.verify-card{display:grid;grid-template-columns:1fr 300px;gap:50px;border-top:1px solid var(--line);padding-top:40px}
.vk h3{font-size:2.1rem;font-weight:300;margin-bottom:4px}
.vk .tag{color:var(--accent);font-weight:300;font-style:italic;font-family:"Fraunces";font-size:1.15rem}
.conf{display:flex;align-items:center;gap:12px;font-size:.84rem;margin:18px 0 24px;color:var(--muted)}
.conf .bar{flex:1;max-width:160px;height:1px;background:var(--line2)}
.conf .bar i{display:block;height:1px;background:var(--accent)}
.kv{display:grid;grid-template-columns:120px 1fr;gap:14px 20px;font-size:.96rem;padding:22px 0;border-top:1px solid var(--line);font-weight:300}
.kv dt{color:var(--muted);font-weight:500;font-size:.74rem;letter-spacing:.12em;text-transform:uppercase}
.kv dd{margin:0;color:var(--cream)}
.chips{display:flex;flex-wrap:wrap;gap:8px}
.chip{font-size:.82rem;padding:5px 13px;border:1px solid var(--line2);color:var(--dim);font-weight:300}
.editlink{background:none;border:none;color:var(--accent);font-weight:500;cursor:pointer;font-size:.9rem;padding:0;margin-top:18px}
.dz-title{font-size:.7rem;letter-spacing:.2em;text-transform:uppercase;color:var(--muted);margin-bottom:18px}
.swatches{display:flex;gap:0;margin:14px 0 20px;border:1px solid var(--line)}
.sw{flex:1;height:54px}
.fontline{padding:14px 0;border-top:1px solid var(--line)}
.fontline .l{font-size:.7rem;letter-spacing:.14em;text-transform:uppercase;color:var(--muted)}
.fontline .v{font-size:1.5rem;margin-top:4px}
.edit-grid{display:grid;gap:16px;margin-top:18px}
.edit-grid .field{margin:0}
.edit-grid input,.edit-grid textarea{width:100%;background:transparent;border:1px solid var(--line2);color:var(--cream);font-size:.96rem;padding:11px 13px;font-family:"Manrope"}
.edit-grid textarea{resize:vertical;min-height:64px}

/* audiences */
.aud-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:1px;background:var(--line);border:1px solid var(--line);margin-top:8px}
.aud{position:relative;background:var(--base);padding:26px 24px;cursor:pointer;transition:background .25s;min-height:170px;display:flex;flex-direction:column}
.aud:hover{background:var(--ink2)}
.aud.sel{background:var(--surface)}
.aud .num{font-family:"Fraunces";font-size:1.1rem;color:var(--muted)}
.aud.sel .num{color:var(--accent)}
.aud h4{font-size:1.3rem;font-weight:400;margin:8px 0 10px;max-width:90%}
.aud .who{font-size:.88rem;color:var(--dim);font-weight:300;margin-bottom:12px;flex:1}
.aud .why{font-size:.82rem;color:var(--muted);font-weight:300;border-top:1px solid var(--line);padding-top:12px}
.aud .why b{color:var(--accent);font-weight:500}
.aud .check{position:absolute;top:22px;right:22px;width:22px;height:22px;border:1px solid var(--line2);display:grid;place-items:center;font-size:.72rem;color:transparent;transition:.2s}
.aud.sel .check{background:var(--accent);border-color:var(--accent);color:#1a0f06}
.aud-bar{position:sticky;bottom:0;margin-top:30px;background:linear-gradient(180deg,rgba(14,11,8,.3),var(--base));backdrop-filter:blur(8px);border-top:1px solid var(--line);padding:22px 0;display:flex;align-items:center;justify-content:space-between;gap:16px;flex-wrap:wrap}
.aud-bar .cnt{font-size:.96rem;color:var(--dim);font-weight:300}
.aud-bar .cnt b{color:var(--cream);font-family:"Fraunces";font-size:1.2rem}

/* preview */
.preview-shell{display:grid;grid-template-columns:230px 1fr;gap:30px;align-items:start}
.rail{position:sticky;top:96px}
.rail h5{font-size:.68rem;letter-spacing:.2em;text-transform:uppercase;color:var(--muted);margin:0 0 14px}
.rail .navb{display:block;width:100%;text-align:left;background:none;border:none;border-left:1px solid var(--line);color:var(--muted);font-size:.92rem;font-weight:300;padding:10px 14px;cursor:pointer;transition:.18s}
.rail .navb:hover{color:var(--cream);border-color:var(--line2)}
.rail .navb.active{color:var(--cream);border-color:var(--accent)}
.rail .navb .vid{font-size:.72rem;color:var(--accent);float:right;opacity:.85}
.rail .grp{margin-bottom:22px}
.frame-wrap{border:1px solid var(--line2);overflow:hidden;box-shadow:var(--shadow);background:var(--ink2)}
.frame-top{display:flex;align-items:center;gap:10px;padding:13px 18px;border-bottom:1px solid var(--line)}
.frame-top .dots{display:flex;gap:7px}
.frame-top .dots i{width:10px;height:10px;border-radius:50%;background:var(--line2)}
.frame-top .url{flex:1;font-size:.82rem;color:var(--muted);text-align:center;font-weight:300}
.iframe-host{height:74vh;background:#fff}
.iframe-host iframe{width:100%;height:100%;border:0;display:block}

/* modal */
.modal-scrim{position:fixed;inset:0;background:rgba(8,5,2,.78);backdrop-filter:blur(4px);z-index:90;display:none;place-items:center;padding:20px}
.modal-scrim.on{display:grid}
.modal{width:min(580px,100%);max-height:86vh;overflow:auto;background:var(--ink2);border:1px solid var(--line2);padding:34px;box-shadow:var(--shadow)}
.modal h3{font-size:1.7rem;font-weight:300;margin-bottom:4px}
.modal .x{float:right;background:none;border:1px solid var(--line2);color:var(--cream);width:34px;height:34px;cursor:pointer}
.vid-block{border-top:1px solid var(--line);padding:16px 0;font-size:.95rem;font-weight:300}
.vid-block .lbl{font-size:.68rem;letter-spacing:.16em;text-transform:uppercase;color:var(--accent);font-weight:600;margin-bottom:8px}
.scene{border-left:1px solid var(--accent);padding:6px 0 6px 16px;margin:12px 0}
.scene b{color:var(--cream);font-family:"Fraunces"}

.toast{position:fixed;left:50%;bottom:26px;transform:translateX(-50%) translateY(120px);background:var(--surface2);border:1px solid var(--line2);color:var(--cream);padding:14px 22px;font-weight:500;font-size:.9rem;z-index:120;transition:transform .4s var(--ease);box-shadow:var(--shadow)}
.toast.show{transform:translateX(-50%) translateY(0)}
.err{border:1px solid rgba(217,142,79,.5);color:var(--accent-soft);padding:14px 16px;font-size:.9rem;margin-top:16px;font-weight:300}

@media (max-width:900px){
.verify-card,.preview-shell{grid-template-columns:1fr;gap:30px}
.steps .s .lbl{display:none}
.rail{position:static}
.iframe-host{height:60vh}
.socials{grid-template-columns:1fr}
}
@media (prefers-reduced-motion:reduce){*{animation:none!important;transition:none!important}}
</style>
</head>
<body>

<div class="topbar">
<div class="topbar-in">
<a class="logo" href="#">
<span class="nm">Alma<b>·</b>AI</span>
<small>Website Studio</small>
</a>
<div class="steps" id="steps">
<div class="s" data-step="input"><span class="n">01</span><span class="lbl">Business</span></div>
<span class="sep"></span>
<div class="s" data-step="verify"><span class="n">02</span><span class="lbl">Verify</span></div>
<span class="sep"></span>
<div class="s" data-step="audiences"><span class="n">03</span><span class="lbl">Audiences</span></div>
<span class="sep"></span>
<div class="s" data-step="preview"><span class="n">04</span><span class="lbl">Build</span></div>
</div>
</div>
</div>

<!-- STEP 1 -->
<section class="screen on" id="screen-input">
<div class="wrap intake">
<span class="eyebrow">Point · Build · Convert</span>
<h1>The website your business is <em>missing</em>.</h1>
<p class="sub">Give Alma a website, a social link, or just a name. She studies the business, finds the audiences they should be selling to, and builds a premium site — a dedicated page for each one.</p>

<div class="intake-card">
<div class="field">
<label>Website, social link, or company name</label>
<input id="in-main" placeholder="acme-roofing.com — or — Acme Roofing, Salt Lake City" autocomplete="off" />
</div>
<div class="socials">
<div class="field"><label>Facebook</label><input id="in-fb" placeholder="optional" /></div>
<div class="field"><label>Instagram</label><input id="in-ig" placeholder="optional" /></div>
<div class="field"><label>TikTok</label><input id="in-tt" placeholder="optional" /></div>
<div class="field"><label>LinkedIn</label><input id="in-li" placeholder="optional" /></div>
</div>
<div class="intake-actions">
<button class="btn btn-primary" id="btn-build">Research &amp; build →</button>
<span class="hint">Alma verifies the business before building. You approve everything.</span>
</div>
<div id="input-err"></div>
</div>

<div class="feat-row">
<span class="feat"><b>Up to 10</b> audience pages</span>
<span class="feat"><b>Question → Pain → Value → KPIs → CTA</b></span>
<span class="feat"><b>Original</b> design every build</span>
<span class="feat"><b>Lead forms</b> + chat</span>
<span class="feat"><b>Ad scripts</b> for TikTok &amp; Facebook</span>
</div>
</div>
</section>

<!-- LOADING -->
<section class="screen" id="screen-loading">
<div class="wrap loading">
<div>
<div class="mono"></div>
<h2 id="ld-title">Alma is working</h2>
<p class="ld-msg" id="ld-msg">Warming up…</p>
<div class="prog">
<div class="prog-bar"><div class="prog-fill" id="prog-fill"></div></div>
<div class="prog-list" id="prog-list"></div>
</div>
</div>
</div>
</section>

<!-- STEP 2 -->
<section class="screen" id="screen-verify">
<div class="wrap pad">
<div class="head-2">
<span class="eyebrow">Step 02 — Verify</span>
<h2>Is this the right business?</h2>
<p>Alma pulled this from their website and socials. Confirm or fix anything before we find audiences.</p>
</div>
<div class="verify-card">
<div id="verify-main"></div>
<div id="verify-side"></div>
</div>
<div style="display:flex;gap:18px;margin-top:36px;flex-wrap:wrap;align-items:center">
<button class="btn btn-primary" id="btn-to-audiences">Find the audiences →</button>
<button class="btn btn-line" id="btn-back-input">Start over</button>
</div>
</div>
</section>

<!-- STEP 3 -->
<section class="screen" id="screen-audiences">
<div class="wrap pad">
<div class="head-2">
<span class="eyebrow">Step 03 — Audiences</span>
<h2>The audiences they're <em class="serif accent" style="font-style:italic">not</em> selling to.</h2>
<p>Alma found the highest-value segments this business should own. Each becomes its own page on the Audience Engine Drop spine. Keep up to 10.</p>
</div>
<div class="aud-grid" id="aud-grid"></div>
<div class="aud-bar">
<div class="cnt"><b id="aud-count">0</b> &nbsp;of 10 audience pages selected</div>
<div style="display:flex;gap:18px;align-items:center">
<button class="btn btn-line" id="btn-back-verify">← Back</button>
<button class="btn btn-primary" id="btn-generate">Build the site →</button>
</div>
</div>
</div>
</section>

<!-- STEP 4 -->
<section class="screen" id="screen-preview">
<div class="wrap pad">
<div class="head-2" style="display:flex;justify-content:space-between;align-items:flex-end;gap:24px;flex-wrap:wrap;max-width:none">
<div>
<span class="eyebrow">Step 04 — The site</span>
<h2 id="pv-title" style="margin-top:14px">Site ready</h2>
</div>
<div style="display:flex;gap:16px;flex-wrap:wrap;align-items:center">
<button class="btn btn-line" id="btn-new">+ New build</button>
<button class="btn btn-ghost btn-sm" id="btn-open-tab">Open ↗</button>
<button class="btn btn-primary" id="btn-export">Export site</button>
</div>
</div>
<div class="preview-shell">
<div class="rail" id="rail"></div>
<div class="frame-wrap">
<div class="frame-top">
<div class="dots"><i></i><i></i><i></i></div>
<div class="url" id="frame-url">yourbusiness.com</div>
</div>
<div class="iframe-host"><iframe id="site-frame" title="Generated site preview"></iframe></div>
</div>
</div>
</div>
</section>

<div class="modal-scrim" id="vid-scrim">
<div class="modal" id="vid-modal">
<button class="x" id="vid-x"></button>
<span class="eyebrow">Short-form ad script</span>
<h3 id="vid-title" style="margin-top:8px">Video script</h3>
<div id="vid-body"></div>
</div>
</div>

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

<script>
/* ============================================================
ALMA AI WEBSITE STUDIO
White-label. End users see "Alma AI" only.
Output follows the Ogden Web Solutions House Style +
the Audience Engine Drop spine.
Model calls run through api.php (key stays server-side).
============================================================ */
const API_ENDPOINT = "api.php";
const MODEL = "claude-sonnet-4-6";

const DISPLAY_FONTS = ["Fraunces","Cormorant Garamond","Playfair Display","Libre Caslon Display"];
const BODY_FONTS = ["Manrope","Inter"];

async function alma(userPrompt, {search=false, system="", retry=true}={}){
const body = { model: MODEL, max_tokens: 1000, messages: [{ role:"user", content: userPrompt }] };
if(system) body.system = system;
if(search) body.tools = [{ type:"web_search_20250305", name:"web_search" }];
const res = await fetch(API_ENDPOINT, {
method:"POST", headers:{ "Content-Type":"application/json" }, body: JSON.stringify(body)
});
if(!res.ok){ throw new Error("Alma couldn't reach the studio service (" + res.status + ")."); }
const data = await res.json();
return (data.content||[]).filter(b=>b.type==="text").map(b=>b.text).join("\n").trim();
}
function extractJSON(txt){
if(!txt) throw new Error("empty");
let t = txt.replace(/```json/gi,"```").trim();
if(t.indexOf("```")>=0){ t = t.split("```").filter(s=>s.trim()).sort((a,b)=>b.length-a.length)[0]; }
const s=t.indexOf("{"), sa=t.indexOf("[");
let start=(sa>=0 && (sa<s||s<0))?sa:s;
if(start<0) throw new Error("no json");
const open=t[start], close=open==="["?"]":"}";
let depth=0,end=-1;
for(let i=start;i<t.length;i++){ if(t[i]===open)depth++; else if(t[i]===close){depth--; if(depth===0){end=i;break;}} }
if(end<0) end=t.length;
return JSON.parse(t.slice(start,end+1));
}
async function almaJSON(prompt, opts={}){
let txt = await alma(prompt, opts);
try{ return extractJSON(txt); }
catch(e){
if(opts.retry===false) throw e;
txt = await alma(prompt + "\n\nReturn ONLY valid JSON. No prose, no markdown.", {...opts, retry:false});
return extractJSON(txt);
}
}

const S = { input:{}, biz:null, audiences:[], pages:{}, siteHTML:"", activePage:"home" };

const screens = ["input","loading","verify","audiences","preview"];
function show(name){ screens.forEach(s=>document.getElementById("screen-"+s).classList.toggle("on", s===name)); window.scrollTo({top:0,behavior:"smooth"}); }
function setSteps(active){
const order=["input","verify","audiences","preview"], ai=order.indexOf(active);
document.querySelectorAll("#steps .s").forEach(el=>{ const i=order.indexOf(el.dataset.step); el.classList.toggle("active",i===ai); el.classList.toggle("done",i<ai); });
}
function toast(msg){ const t=document.getElementById("toast"); t.textContent=msg; t.classList.add("show"); clearTimeout(t._t); t._t=setTimeout(()=>t.classList.remove("show"),2600); }

let ldRot=null;
function startLoading(title, steps, msgs){
show("loading");
document.getElementById("ld-title").textContent = title;
document.getElementById("prog-list").innerHTML = steps.map((s,i)=>`<div class="prog-item" data-i="${i}"><span class="ico">·</span><span>${s}</span></div>`).join("");
setProg(6);
let m=0; const el=document.getElementById("ld-msg"); el.textContent=msgs[0];
clearInterval(ldRot);
ldRot=setInterval(()=>{ m=(m+1)%msgs.length; el.style.opacity=0; setTimeout(()=>{el.textContent=msgs[m];el.style.opacity=1;},220); },2700);
}
function setProg(p){ document.getElementById("prog-fill").style.width=Math.min(98,p)+"%"; }
function stepState(i,st){ const el=document.querySelector('.prog-item[data-i="'+i+'"]'); if(!el)return; el.className="prog-item "+(st==="ok"?"ok":st==="run"?"run":""); el.querySelector(".ico").textContent=st==="ok"?"✓":st==="run"?"›":"·"; }
function stopLoading(){ clearInterval(ldRot); }

/* ---------- research ---------- */
async function runResearch(){
const main=document.getElementById("in-main").value.trim();
if(!main){ document.getElementById("input-err").innerHTML='<div class="err">Add a website, social link, or company name to start.</div>'; return; }
S.input={ main, fb:val("in-fb"), ig:val("in-ig"), tt:val("in-tt"), li:val("in-li") };
startLoading("Alma is studying the business",
["Finding & verifying the business","Reading the website & socials","Choosing a house-style direction"],
["Reading their website…","Studying their social profiles…","Listening for their voice…","Choosing type, base & accent…","Cross-checking the details…"]);
stepState(0,"run"); setProg(14);
try{
const links=[S.input.fb,S.input.ig,S.input.tt,S.input.li].filter(Boolean).join(", ");
const prompt=
`You are Alma, a B2B brand & audience strategist. Research this business with web search and capture an accurate profile.

INPUT: "${S.input.main}"
${links?("KNOWN SOCIAL LINKS: "+links):""}

Search the web (website, socials, directories) to verify the real business and gather facts. If info is thin, infer sensibly from category + location and lower "confidence".

Return ONLY JSON:
{
"name":"", "verified":true, "confidence":0.0, "domain":"", "category":"",
"tagline":"short brand line", "description":"2 sentences on what they do",
"services":["3-7 concrete services"], "location":"city, state if known",
"phone":"", "email":"", "tone":"one phrase voice e.g. confident & warm",
"currentAudiences":["who their current site seems to speak to"],
"design":{
"base":"#hex DEEP near-black, subtly warmed or cooled toward the brand's world",
"accent":"#hex — ONE characterful accent pulled from the brand (their craft, logo, region). Not generic blue.",
"cream":"#hex — soft off-white / warm cream for text on the base",
"displayFont":"one of: ${DISPLAY_FONTS.join(", ")}",
"bodyFont":"one of: ${BODY_FONTS.join(", ")}",
"vibe":"3-5 word art direction e.g. quiet rugged confident",
"idea":"the ONE strong idea this site should express in 4-8 words"
}
}
The look is premium editorial: near-black base, cream text, a single accent used sparingly. Make base & accent genuinely fit THIS brand.`;
const biz=await almaJSON(prompt,{search:true});
stepState(0,"ok"); stepState(1,"ok"); stepState(2,"run"); setProg(72);
biz.design=biz.design||{};
if(!DISPLAY_FONTS.includes(biz.design.displayFont)) biz.design.displayFont="Fraunces";
if(!BODY_FONTS.includes(biz.design.bodyFont)) biz.design.bodyFont="Manrope";
biz.design.base=cleanHex(biz.design.base)||"#0E0B08";
biz.design.accent=cleanHex(biz.design.accent)||"#C98A4A";
biz.design.cream=cleanHex(biz.design.cream)||"#F2EBDD";
biz.socials=S.input;
S.biz=biz;
stepState(2,"ok"); setProg(100); stopLoading();
renderVerify(); setSteps("verify"); show("verify");
}catch(e){
stopLoading(); show("input");
document.getElementById("input-err").innerHTML='<div class="err">'+(e.message||"Something went wrong")+' Try a more specific name or paste the website URL.</div>';
}
}
function val(id){ return document.getElementById(id).value.trim(); }

function renderVerify(){
const b=S.biz, d=b.design, conf=Math.round((b.confidence||0.7)*100);
document.getElementById("verify-main").innerHTML=
'<div class="vk"><h3>'+esc(b.name||"Business")+'</h3>'+
'<div class="tag">'+esc(b.tagline||b.category||"")+'</div>'+
'<div class="conf"><span>Match confidence</span><span class="bar"><i style="width:'+conf+'%"></i></span><span style="color:var(--cream)">'+conf+'%</span></div>'+
'</div>'+
'<p class="dim" style="font-weight:300;font-size:1.08rem;max-width:54ch;margin:0 0 6px">'+esc(b.description||"")+'</p>'+
'<dl class="kv">'+kv("Category",b.category)+kv("Location",b.location)+kv("Phone",b.phone)+kv("Email",b.email)+kv("Voice",b.tone)+'</dl>'+
'<div class="kv"><dt>Services</dt><dd><div class="chips">'+(b.services||[]).map(s=>'<span class="chip">'+esc(s)+'</span>').join("")+'</div></dd></div>'+
'<button class="editlink" id="btn-edit">✎ Edit these details</button><div id="edit-zone"></div>';
document.getElementById("verify-side").innerHTML=
'<div class="dz-title">House-style direction</div>'+
'<h3 class="serif" style="font-weight:300;font-size:1.7rem;font-style:italic">"'+esc(d.idea||d.vibe||"")+'"</h3>'+
'<div class="muted" style="font-size:.84rem;margin:6px 0 0;font-weight:300">'+esc(d.vibe||"")+'</div>'+
'<div class="swatches"><span class="sw" style="background:'+d.base+'"></span><span class="sw" style="background:'+d.accent+'"></span><span class="sw" style="background:'+d.cream+'"></span></div>'+
'<div class="fontline"><div class="l">Display</div><div class="v serif" style="font-weight:300">'+esc(d.displayFont)+'</div></div>'+
'<div class="fontline"><div class="l">Body</div><div class="v" style="font-family:Manrope;font-size:1.15rem">'+esc(d.bodyFont)+'</div></div>'+
'<p class="muted" style="font-size:.82rem;margin-top:16px;font-weight:300">Near-black base · cream text · one accent. Every build is original.</p>';
document.getElementById("btn-edit").onclick=openEdit;
}
function openEdit(){
const b=S.biz;
document.getElementById("btn-edit").style.display="none";
document.getElementById("edit-zone").innerHTML=
'<div class="edit-grid">'+ef("Name","name",b.name)+ef("Tagline","tagline",b.tagline)+ef("Location","location",b.location)+ef("Phone","phone",b.phone)+ef("Email","email",b.email)+
'<div class="field"><label>Description</label><textarea id="ed-description">'+esc(b.description||"")+'</textarea></div>'+
'<div class="field"><label>Services (comma separated)</label><textarea id="ed-services">'+esc((b.services||[]).join(", "))+'</textarea></div>'+
'<button class="btn btn-ghost btn-sm" id="btn-save-edit" style="width:fit-content">Save details</button></div>';
document.getElementById("btn-save-edit").onclick=()=>{
["name","tagline","location","phone","email","description"].forEach(k=>{const el=document.getElementById("ed-"+k); if(el)b[k]=el.value.trim();});
b.services=document.getElementById("ed-services").value.split(",").map(x=>x.trim()).filter(Boolean);
renderVerify(); toast("Details updated");
};
}
function ef(l,k,v){ return '<div class="field"><label>'+l+'</label><input id="ed-'+k+'" value="'+escA(v||"")+'"></div>'; }
function kv(k,v){ return v?('<dt>'+k+'</dt><dd>'+esc(v)+'</dd>'):""; }

/* ---------- audiences ---------- */
async function runAudiences(){
startLoading("Alma is finding the audiences they're missing",
["Mapping the buyer landscape","Ranking the highest-value segments","Selecting the strongest fits"],
["Mapping who they could sell to…","Scoring each segment by value & fit…","Finding the pain their site ignores…","Ranking the strongest opportunities…"]);
stepState(0,"run"); setProg(20);
try{
const b=S.biz;
const prompt=
`You are Alma, an ICP & demand strategist. For this business, identify the highest-value distinct AUDIENCE SEGMENTS it should sell to but whose specific pain its current website does NOT speak to. Each becomes its own landing page.

BUSINESS: ${b.name} — ${b.category}. ${b.description}
SERVICES: ${(b.services||[]).join(", ")}
LOCATION: ${b.location||"n/a"}
CURRENTLY SPEAKS TO: ${(b.currentAudiences||[]).join(", ")||"unclear / generic"}

Return ONLY a JSON array of EXACTLY 10 items (or as many distinct, non-overlapping ones as truly exist), strongest first:
[{ "name":"specific segment", "who":"1 sentence", "whyNow":"why target them now", "painHint":"the core tension they feel" }]
Prefer named verticals/roles over vague groups.`;
let auds=await almaJSON(prompt,{});
if(!Array.isArray(auds)) auds=[];
auds=auds.slice(0,10).map((a,i)=>({...a,id:"aud"+i,selected:true}));
S.audiences=auds;
stepState(0,"ok"); stepState(1,"ok"); stepState(2,"ok"); setProg(100); stopLoading();
renderAudiences(); setSteps("audiences"); show("audiences");
}catch(e){ stopLoading(); renderVerify(); setSteps("verify"); show("verify"); toast("Couldn't load audiences — try again"); }
}
function renderAudiences(){
const grid=document.getElementById("aud-grid");
grid.innerHTML=S.audiences.map((a,i)=>
'<div class="aud '+(a.selected?'sel':'')+'" data-id="'+a.id+'"><div class="check">✓</div>'+
'<div class="num">'+String(i+1).padStart(2,"0")+'</div><h4>'+esc(a.name)+'</h4>'+
'<div class="who">'+esc(a.who||"")+'</div>'+
'<div class="why"><b>Why now — </b>'+esc(a.whyNow||a.painHint||"")+'</div></div>').join("");
grid.querySelectorAll(".aud").forEach(el=>el.onclick=()=>toggleAud(el.dataset.id));
updateAudCount();
}
function toggleAud(id){
const a=S.audiences.find(x=>x.id===id), sel=S.audiences.filter(x=>x.selected).length;
if(!a.selected && sel>=10){ toast("10 audience pages is the max"); return; }
a.selected=!a.selected; renderAudiences();
}
function updateAudCount(){ document.getElementById("aud-count").textContent=S.audiences.filter(a=>a.selected).length; }

/* ---------- generate ---------- */
async function runGenerate(){
const chosen=S.audiences.filter(a=>a.selected);
if(!chosen.length){ toast("Select at least one audience"); return; }
S.pages={};
startLoading("Alma is building the site",
chosen.map(a=>"Writing the "+shortName(a.name)+" page").concat(["Setting type, composing the layout"]),
["Writing hooks that stop the scroll…","Naming the pain each buyer feels…","Translating value into their language…","Lining up the proof…","Composing the editorial layout…"]);
const b=S.biz;
try{
for(let i=0;i<chosen.length;i++){
const a=chosen[i];
stepState(i,"run"); setProg(10+(i/chosen.length)*80);
const prompt=
`You are Alma, a direct-response copywriter using the "Audience Engine Drop" spine. Write a landing page for "${b.name}" (${b.category}) aimed ONLY at: ${a.name} — ${a.who}. Core pain: ${a.painHint||a.whyNow}.
Business value: ${b.description} Services: ${(b.services||[]).join(", ")}.

Return ONLY JSON following the spine:
{
"question":"a provocative HEADLINE phrased as a question that hooks THIS audience",
"subhead":"one supporting line",
"painIntro":"1-2 empathetic sentences naming what they're feeling",
"pains":["3 specific pain points, short"],
"valueProps":[{"title":"","body":"1 sentence in the CUSTOMER's language"} , 3 to 4 items],
"kpis":[{"stat":"e.g. 38%","label":"what it measures"} , exactly 3 — only credible numbers, no fabrication],
"ctaHeadline":"action-oriented close",
"ctaButton":"2-4 words",
"formTitle":"short form heading",
"formFields":["3-4 short field labels"]
}
Voice: ${b.tone}. Specific, confident, calm. No hype.`;
try{ S.pages[a.id]=await almaJSON(prompt,{}); }catch(err){ S.pages[a.id]=fallbackPage(a,b); }
stepState(i,"ok");
}
stepState(chosen.length,"run"); setProg(94);
S.siteHTML=buildSite(b,chosen,S.pages);
stepState(chosen.length,"ok"); setProg(100); stopLoading();
renderPreview(chosen); setSteps("preview"); show("preview");
}catch(e){ stopLoading(); renderAudiences(); setSteps("audiences"); show("audiences"); toast("Build hit a snag — try again"); }
}
function fallbackPage(a,b){
return { question:"Is "+a.name+" getting what they need from "+b.name+"?", subhead:b.tagline||"", painIntro:a.painHint||"",
pains:[a.painHint||"Underserved needs","Generic messaging","No clear next step"],
valueProps:(b.services||["Tailored service","Proven results","Local & responsive"]).slice(0,3).map(s=>({title:s,body:""})),
kpis:[{stat:"100%",label:"focused on you"},{stat:"24h",label:"response time"},{stat:"5★",label:"client rating"}],
ctaHeadline:"Let's talk", ctaButton:"Get started", formTitle:"Request a callback", formFields:["Name","Email","Phone"] };
}

/* ============================================================
SITE ASSEMBLER — Ogden Web Solutions House Style
============================================================ */
function buildSite(b, auds, pages){
const d=b.design||{};
const base=d.base||"#0E0B08", accent=d.accent||"#C98A4A", cream=d.cream||"#F2EBDD";
const disp=d.displayFont||"Fraunces", body=d.bodyFont||"Manrope";
const fp=encodeURIComponent(disp)+":opsz,wght@9..144,300;9..144,400;9..144,500&family="+encodeURIComponent(body)+":wght@300;400;500;600";
const idea=d.idea||b.tagline||b.category||"";

const navLinks=['<a href="#home">Home</a>','<a href="#idea">Approach</a>']
.concat(auds.map(a=>'<a href="#'+a.id+'">'+escA(shortName(a.name))+'</a>'))
.concat(['<a href="#about">About</a>','<a href="#contact">Contact</a>']);

const p=[];
p.push('<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0">');
p.push('<title>'+escA(b.name)+(b.tagline?' — '+escA(b.tagline):'')+'</title>');
p.push('<meta name="description" content="'+escA(b.description||b.tagline||b.name)+'">');
p.push('<link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>');
p.push('<link href="https://fonts.googleapis.com/css2?family='+fp+'&display=swap" rel="stylesheet">');
p.push('<style>'+siteCSS(base,accent,cream,disp,body)+'</style></head><body>');

// nav
p.push('<header class="nav"><div class="container nav-in"><a href="#home" class="brand">'+escA(b.name)+'</a>'+
'<nav class="nlinks" id="nlinks">'+navLinks.join("")+'</nav>'+
'<a href="#contact" class="navcta">'+escA(b.cta||"Start a conversation")+' →</a>'+
'<button class="burger" id="burger" aria-label="Menu">☰</button></div></header>');

// hero
p.push('<section id="home" class="hero"><div class="container">'+
'<div class="hero-grid"><div class="hero-copy">'+
'<span class="eyebrow">'+escA(b.location||b.category||"")+'</span>'+
'<h1>'+heroHeadline(b)+'</h1>'+
'<p class="lead">'+escA(b.description||"")+'</p>'+
'<div class="hero-cta"><a href="#contact" class="btn-fill">'+escA(b.cta||"Start a conversation")+'</a>'+
'<a href="#'+(auds[0]?auds[0].id:'idea')+'" class="btn-underline">See the work</a></div>'+
'</div><div class="hero-art"><div class="art-frame"><span class="art-mark">'+escA(initials(b.name))+'</span><svg viewBox="0 0 200 200" class="art-arc"><circle cx="100" cy="100" r="86" /><circle cx="100" cy="100" r="60" /></svg></div></div></div>'+
'</div></section>');

// the idea / approach + services
p.push('<section id="idea" class="band"><div class="container idea-in">'+
'<div class="idea-l"><span class="eyebrow">The idea</span><h2 class="serif idea-h">'+escA(idea)+'</h2></div>'+
'<div class="idea-r"><div class="svc-list">'+(b.services||[]).map((s,i)=>'<div class="svc"><span class="svc-n">'+String(i+1).padStart(2,"0")+'</span><span class="svc-t">'+escA(s)+'</span></div>').join("")+'</div></div>'+
'</div></section>');

// audience pages — Audience Engine Drop spine
auds.forEach((a,idx)=>{
const pg=pages[a.id]||{};
p.push('<section id="'+a.id+'" class="aud-page"><div class="container">'+
'<div class="ap-head"><span class="eyebrow">For '+escA(a.name)+'</span>'+
'<span class="ap-idx">'+String(idx+1).padStart(2,"0")+' / '+String(auds.length).padStart(2,"0")+'</span></div>'+
'<h2 class="hook">'+escA(pg.question||"")+'</h2>'+
(pg.subhead?'<p class="hook-sub">'+escA(pg.subhead)+'</p>':"")+
'<div class="ap-body">'+
'<div class="ap-pain"><span class="ap-label">The pain</span><p>'+escA(pg.painIntro||"")+'</p><ul>'+
(pg.pains||[]).map(x=>'<li>'+escA(x)+'</li>').join("")+'</ul></div>'+
'<div class="ap-value"><span class="ap-label">What changes</span>'+
(pg.valueProps||[]).map(v=>'<div class="vp"><b>'+escA(v.title||"")+'</b>'+(v.body?'<span>'+escA(v.body)+'</span>':"")+'</div>').join("")+'</div>'+
'</div>'+
'<div class="kpis">'+(pg.kpis||[]).map(k=>'<div class="kpi"><div class="stat">'+escA(k.stat||"")+'</div><div class="klabel">'+escA(k.label||"")+'</div></div>').join("")+'</div>'+
'<div class="cta-band"><div class="cta-l"><h3>'+escA(pg.ctaHeadline||"Ready when you are")+'</h3></div>'+
'<form class="lead-form" onsubmit="return almaLead(event,\''+escA(a.name)+'\')">'+
'<div class="lf-title">'+escA(pg.formTitle||"Get in touch")+'</div>'+
(pg.formFields||["Name","Email","Phone"]).map(f=>fieldFor(f)).join("")+
'<button class="btn-fill lf-btn" type="submit">'+escA(pg.ctaButton||"Send")+'</button>'+
'<div class="lf-ok">Thank you — we\'ll be in touch shortly.</div>'+
'</form></div>'+
'</div></section>');
});

// about
p.push('<section id="about" class="band"><div class="container about-in">'+
'<div><span class="eyebrow">About</span><h2 class="serif about-h">'+escA(b.name)+'</h2>'+
'<p class="lead">'+escA(b.description||"")+'</p>'+(b.location?'<p class="muted-line">'+escA(b.location)+'</p>':"")+'</div>'+
'<div class="about-panel"><span class="ap-vibe serif">'+escA(d.vibe||"crafted")+'</span>'+(b.tone?'<p>'+escA(b.tone)+'</p>':"")+'</div>'+
'</div></section>');

// contact
p.push('<section id="contact" class="contact"><div class="container contact-in">'+
'<div><span class="eyebrow">Contact</span><h2 class="serif contact-h">Let\'s talk.</h2>'+
(b.phone?'<a class="cline" href="tel:'+escA(b.phone)+'">'+escA(b.phone)+'</a>':"")+
(b.email?'<a class="cline" href="mailto:'+escA(b.email)+'">'+escA(b.email)+'</a>':"")+
(b.location?'<div class="cline muted-line">'+escA(b.location)+'</div>':"")+'</div>'+
'<form class="lead-form" onsubmit="return almaLead(event,\'General\')">'+
'<div class="lf-title">Send a message</div>'+fieldFor("Name")+fieldFor("Email")+fieldFor("Phone")+fieldFor("Message")+
'<button class="btn-fill lf-btn" type="submit">Send message</button>'+
'<div class="lf-ok">Thank you — we\'ll be in touch shortly.</div></form>'+
'</div></section>');

// footer
p.push('<footer class="ftr"><div class="container ftr-in"><div class="brand">'+escA(b.name)+'</div>'+
'<div class="muted-line">© '+new Date().getFullYear()+' '+escA(b.name)+(b.location?' · '+escA(b.location):"")+'</div></div></footer>');

// chat widget
p.push('<button id="cBtn" class="chat-btn" aria-label="Chat">Ask</button>'+
'<div id="cBox" class="chat-box"><div class="chat-head"><b>Ask us anything</b><button id="cClose">✕</button></div>'+
'<div id="cLog" class="chat-log"><div class="cm bot">Hi — how can we help you today?</div></div>'+
'<div class="chat-in"><input id="cInput" placeholder="Type a message…"><button id="cSend">→</button></div></div>');
p.push('<script>'+siteJS(b)+'<\/script></body></html>');
return p.join("");
}

function heroHeadline(b){
// one strong idea, big and thin, with the brand name as the anchor
const nm=escA(b.name);
const tl=b.tagline?('<span class="h-sub serif">'+escA(b.tagline)+'</span>'):"";
return nm+tl;
}
function initials(s){ s=String(s||"").trim().split(/\s+/).map(w=>w[0]).join("").slice(0,3).toUpperCase(); return s||"·"; }

function fieldFor(label){
const l=(label||"").toLowerCase();
if(l.indexOf("message")>=0||l.indexOf("challenge")>=0||l.indexOf("need")>=0||l.indexOf("project")>=0||l.indexOf("detail")>=0)
return '<label class="lf">'+escA(label)+'<textarea name="'+escA(label)+'" rows="3"></textarea></label>';
const t=l.indexOf("email")>=0?"email":(l.indexOf("phone")>=0?"tel":"text");
return '<label class="lf">'+escA(label)+'<input type="'+t+'" name="'+escA(label)+'"></label>';
}

function siteCSS(base,accent,cream,disp,body){
const onAccent=contrast(accent);
const line=withAlpha(cream,.14), line2=withAlpha(cream,.26), dim=withAlpha(cream,.72), muted=withAlpha(cream,.46);
const tintA=withAlpha(accent,.10), tintB=withAlpha(accent,.16);
return [
"*{box-sizing:border-box;margin:0;padding:0}",
"html{scroll-behavior:smooth}",
"body{background:"+base+";color:"+cream+";font-family:'"+body+"',system-ui,sans-serif;font-size:17px;font-weight:400;line-height:1.66;-webkit-font-smoothing:antialiased}",
"h1,h2,h3,h4{font-family:'"+disp+"',Georgia,serif;font-weight:400;line-height:1.04;letter-spacing:-.012em}",
".serif{font-family:'"+disp+"',Georgia,serif}",
".container{width:min(100% - 48px,1180px);margin-inline:auto}",
"a{color:inherit;text-decoration:none}",
".eyebrow{font-family:'"+body+"';font-size:.72rem;letter-spacing:.26em;text-transform:uppercase;font-weight:600;color:"+accent+"}",
".muted-line{color:"+muted+";font-weight:300}",
"section{scroll-margin-top:72px}",
".btn-fill{display:inline-block;background:"+accent+";color:"+onAccent+";font-family:'"+body+"';font-weight:600;font-size:.92rem;padding:15px 30px;letter-spacing:.01em;transition:transform .25s,background .25s}",
".btn-fill:hover{transform:translateY(-2px)}",
".btn-underline{display:inline-block;font-family:'"+body+"';font-weight:500;font-size:.92rem;padding:15px 4px;position:relative}",
".btn-underline:after{content:'';position:absolute;left:4px;right:4px;bottom:9px;height:1px;background:"+accent+";transform:scaleX(.35);transform-origin:left;transition:transform .3s}",
".btn-underline:hover:after{transform:scaleX(1)}",
/* nav */
".nav{position:sticky;top:0;z-index:40;background:"+withAlpha(base,.86)+";backdrop-filter:blur(12px);border-bottom:1px solid "+line+"}",
".nav-in{display:flex;align-items:center;gap:22px;padding:18px 0}",
".brand{font-family:'"+disp+"';font-weight:500;font-size:1.4rem;margin-right:auto;letter-spacing:-.01em}",
".nlinks{display:flex;gap:2px;flex-wrap:wrap}",
".nlinks a{font-family:'"+body+"';font-size:.85rem;font-weight:400;color:"+dim+";padding:8px 12px;transition:.2s}",
".nlinks a:hover{color:"+cream+"}",
".navcta{font-family:'"+body+"';font-size:.85rem;font-weight:600;color:"+accent+";white-space:nowrap}",
".burger{display:none;background:none;border:1px solid "+line2+";color:"+cream+";font-size:1.1rem;padding:7px 12px;cursor:pointer}",
/* hero */
".hero{padding:clamp(80px,12vw,150px) 0 clamp(70px,10vw,130px);position:relative}",
".hero-grid{display:grid;grid-template-columns:1.5fr .9fr;gap:50px;align-items:center}",
".hero h1{font-size:clamp(3rem,8vw,6.4rem);font-weight:300;letter-spacing:-.03em;margin:22px 0 0}",
".hero h1 .h-sub{display:block;font-size:clamp(1.3rem,3vw,2.2rem);font-style:italic;color:"+accent+";font-weight:300;margin-top:14px;letter-spacing:-.01em}",
".lead{font-size:1.2rem;color:"+dim+";max-width:50ch;margin-top:30px;font-weight:300}",
".hero-cta{display:flex;gap:24px;align-items:center;margin-top:38px;flex-wrap:wrap}",
".hero-art{display:flex;justify-content:center}",
".art-frame{position:relative;width:min(320px,80%);aspect-ratio:3/4;background:linear-gradient(160deg,"+tintB+","+tintA+");border:1px solid "+line2+";display:grid;place-items:center;overflow:hidden}",
".art-mark{font-family:'"+disp+"';font-size:clamp(3rem,7vw,5rem);font-weight:300;color:"+cream+";z-index:2;letter-spacing:.04em}",
".art-arc{position:absolute;width:130%;height:130%;opacity:.5}.art-arc circle{fill:none;stroke:"+accent+";stroke-width:.5}",
/* idea band */
".band{padding:clamp(80px,11vw,150px) 0;border-top:1px solid "+line+"}",
".idea-in{display:grid;grid-template-columns:1fr 1fr;gap:60px;align-items:start}",
".idea-h{font-size:clamp(2rem,4.5vw,3.4rem);font-weight:300;margin-top:18px;max-width:14ch}",
".svc-list{display:grid;gap:0}",
".svc{display:flex;gap:20px;align-items:baseline;padding:20px 0;border-top:1px solid "+line+"}",
".svc:last-child{border-bottom:1px solid "+line+"}",
".svc-n{font-family:'"+disp+"';color:"+accent+";font-size:1rem;font-weight:400}",
".svc-t{font-size:1.25rem;font-weight:300;font-family:'"+disp+"'}",
/* audience page */
".aud-page{padding:clamp(80px,11vw,150px) 0;border-top:1px solid "+line+"}",
".ap-head{display:flex;justify-content:space-between;align-items:baseline;gap:16px}",
".ap-idx{font-family:'"+disp+"';color:"+muted+";font-size:.95rem}",
".hook{font-size:clamp(2.2rem,5.2vw,4rem);font-weight:300;max-width:17ch;margin:18px 0}",
".hook-sub{font-size:1.25rem;color:"+dim+";max-width:52ch;font-weight:300;margin-bottom:8px}",
".ap-body{display:grid;grid-template-columns:1fr 1fr;gap:48px;margin:50px 0 10px}",
".ap-label{font-family:'"+body+"';font-size:.7rem;letter-spacing:.2em;text-transform:uppercase;color:"+accent+";display:block;margin-bottom:18px}",
".ap-pain p{color:"+dim+";font-weight:300;margin-bottom:18px;font-size:1.08rem}",
".ap-pain ul{list-style:none;display:grid;gap:14px}",
".ap-pain li{padding-left:24px;position:relative;color:"+cream+";font-weight:300}",
".ap-pain li:before{content:'—';position:absolute;left:0;color:"+accent+"}",
".ap-value{display:grid;gap:0;align-content:start}",
".vp{padding:18px 0;border-top:1px solid "+line+"}",
".vp b{display:block;font-family:'"+disp+"';font-size:1.3rem;font-weight:400;margin-bottom:4px}",
".vp span{color:"+dim+";font-size:.98rem;font-weight:300}",
".kpis{display:grid;grid-template-columns:repeat(3,1fr);gap:1px;background:"+line+";border:1px solid "+line+";margin:54px 0}",
".kpi{background:"+base+";padding:34px 24px;text-align:center}",
".stat{font-family:'"+disp+"';font-size:clamp(2.6rem,6vw,4.4rem);font-weight:300;color:"+accent+";line-height:1}",
".klabel{color:"+muted+";font-size:.88rem;margin-top:10px;font-weight:300}",
".cta-band{display:grid;grid-template-columns:1fr 1fr;gap:48px;align-items:center;border-top:1px solid "+line2+";padding-top:54px}",
".cta-l h3{font-size:clamp(1.8rem,3.6vw,2.8rem);font-weight:300}",
/* forms */
".lead-form{}",
".lf-title{font-family:'"+disp+"';font-weight:400;font-size:1.3rem;margin-bottom:18px}",
".lf{display:block;font-family:'"+body+"';font-size:.68rem;font-weight:600;letter-spacing:.16em;text-transform:uppercase;color:"+muted+";margin-bottom:18px}",
".lf input,.lf textarea{width:100%;margin-top:8px;background:transparent;border:none;border-bottom:1px solid "+line2+";color:"+cream+";font-family:'"+body+"';font-size:1rem;font-weight:400;padding:8px 0;text-transform:none;letter-spacing:0}",
".lf input:focus,.lf textarea:focus{outline:none;border-color:"+accent+"}",
".lf-btn{border:none;cursor:pointer;margin-top:8px;width:100%;text-align:center}",
".lf-ok{display:none;margin-top:16px;color:"+accent+";font-weight:500}",
/* about / contact */
".about-in,.contact-in{display:grid;grid-template-columns:1.1fr .9fr;gap:56px;align-items:center}",
".about-h,.contact-h{font-size:clamp(2rem,4.5vw,3.2rem);font-weight:300;margin:16px 0 22px}",
".about-panel{aspect-ratio:4/3;background:linear-gradient(160deg,"+tintB+","+tintA+");border:1px solid "+line2+";padding:40px;display:flex;flex-direction:column;justify-content:flex-end}",
".ap-vibe{font-size:2.2rem;font-weight:300;text-transform:capitalize}",
".about-panel p{color:"+dim+";font-weight:300;margin-top:8px}",
".contact{padding:clamp(80px,11vw,150px) 0;border-top:1px solid "+line+"}",
".cline{display:block;font-size:1.35rem;font-family:'"+disp+"';font-weight:300;margin:12px 0;transition:.2s}",
"a.cline:hover{color:"+accent+"}",
/* footer */
".ftr{padding:46px 0;border-top:1px solid "+line+"}",
".ftr-in{display:flex;justify-content:space-between;gap:18px;flex-wrap:wrap;align-items:center}",
".ftr .brand{font-size:1.2rem}",
/* chat */
".chat-btn{position:fixed;right:22px;bottom:22px;background:"+accent+";color:"+onAccent+";font-family:'"+body+"';font-weight:600;font-size:.9rem;border:none;padding:14px 22px;cursor:pointer;z-index:60;box-shadow:0 16px 34px -14px rgba(0,0,0,.6)}",
".chat-box{position:fixed;right:22px;bottom:78px;width:min(340px,calc(100vw - 44px));background:"+base+";border:1px solid "+line2+";display:none;flex-direction:column;overflow:hidden;z-index:60;box-shadow:0 30px 70px -24px rgba(0,0,0,.7)}",
".chat-box.open{display:flex}",
".chat-head{background:"+accent+";color:"+onAccent+";padding:14px 16px;display:flex;justify-content:space-between;align-items:center;font-weight:600}",
".chat-head button{background:none;border:none;color:inherit;cursor:pointer}",
".chat-log{padding:14px;max-height:300px;overflow:auto;display:flex;flex-direction:column;gap:9px}",
".cm{padding:10px 13px;font-size:.92rem;max-width:86%;font-weight:300}",
".cm.bot{background:"+withAlpha(cream,.06)+";align-self:flex-start}",
".cm.me{background:"+accent+";color:"+onAccent+";align-self:flex-end}",
".chat-in{display:flex;gap:8px;padding:12px;border-top:1px solid "+line+"}",
".chat-in input{flex:1;background:transparent;border:1px solid "+line2+";color:"+cream+";padding:10px 12px;font-family:'"+body+"'}",
".chat-in button{background:"+accent+";color:"+onAccent+";border:none;width:42px;cursor:pointer}",
"@media(max-width:860px){.hero-grid,.idea-in,.ap-body,.kpis,.cta-band,.about-in,.contact-in{grid-template-columns:1fr}.hero-art{display:none}.kpis{grid-template-columns:1fr 1fr}.nlinks{display:none;position:absolute;top:64px;left:0;right:0;background:"+base+";flex-direction:column;padding:14px 24px;border-bottom:1px solid "+line+"}.nlinks.open{display:flex}.burger{display:block}.navcta{display:none}}"
].join("");
}

function siteJS(b){
var lead=(b.email||"").replace(/'/g,"")||"owner@example.com";
return [
"var LEAD_EMAIL='"+lead+"';",
"var ALMA_CHAT_ENDPOINT='';",
"document.getElementById('burger').onclick=function(){document.getElementById('nlinks').classList.toggle('open');};",
"document.querySelectorAll('#nlinks a').forEach(function(a){a.onclick=function(){document.getElementById('nlinks').classList.remove('open');};});",
"window.almaLead=function(e,aud){e.preventDefault();var f=e.target;var fd={};Array.prototype.forEach.call(f.elements,function(el){if(el.name)fd[el.name]=el.value;});",
"fd['Audience Page']=aud;var b=f.querySelector('button');b.disabled=true;b.textContent='Sending…';",
"fetch('https://formsubmit.co/ajax/'+LEAD_EMAIL,{method:'POST',headers:{'Content-Type':'application/json',Accept:'application/json'},body:JSON.stringify(fd)})",
".then(function(r){if(!r.ok)throw 0;f.querySelectorAll('label,button').forEach(function(x){x.style.display='none';});f.querySelector('.lf-ok').style.display='block';})",
".catch(function(){b.disabled=false;b.textContent='Try again';alert('Please email us at '+LEAD_EMAIL);});return false;};",
"var cb=document.getElementById('cBox');document.getElementById('cBtn').onclick=function(){cb.classList.toggle('open');};",
"document.getElementById('cClose').onclick=function(){cb.classList.remove('open');};",
"function add(t,w){var d=document.createElement('div');d.className='cm '+w;d.textContent=t;var l=document.getElementById('cLog');l.appendChild(d);l.scrollTop=l.scrollHeight;}",
"function sc(){var i=document.getElementById('cInput');var v=i.value.trim();if(!v)return;add(v,'me');i.value='';",
"if(!ALMA_CHAT_ENDPOINT){setTimeout(function(){add('Thanks! For the fastest answer, call or email us and a team member will help right away.','bot');},500);return;}",
"fetch(ALMA_CHAT_ENDPOINT,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({message:v,business:'"+escJS(b.name)+"'})}).then(function(r){return r.json();}).then(function(d){add(d.reply||'…','bot');}).catch(function(){add('Sorry, please try again or contact us directly.','bot');});}",
"document.getElementById('cSend').onclick=sc;document.getElementById('cInput').addEventListener('keydown',function(e){if(e.key==='Enter')sc();});",
"var io=new IntersectionObserver(function(es){es.forEach(function(e){if(e.isIntersecting){e.target.style.opacity=1;e.target.style.transform='none';io.unobserve(e.target);}})},{threshold:.08});",
"document.querySelectorAll('section > .container').forEach(function(el){el.style.opacity=0;el.style.transform='translateY(20px)';el.style.transition='opacity .8s cubic-bezier(.22,.61,.36,1),transform .8s cubic-bezier(.22,.61,.36,1)';io.observe(el);});"
].join("\n");
}

/* ---------- preview ---------- */
function renderPreview(auds){
const b=S.biz;
document.getElementById("pv-title").textContent=b.name+" — ready";
document.getElementById("frame-url").textContent=(b.domain||slug(b.name)+".com");
const rail=document.getElementById("rail");
rail.innerHTML=
'<div class="grp"><h5>Pages</h5>'+navbBtn("home","Home")+navbBtn("idea","Approach")+'</div>'+
'<div class="grp"><h5>Audience pages</h5>'+auds.map(a=>navbBtn(a.id,shortName(a.name),true)).join("")+'</div>'+
'<div class="grp"><h5>More</h5>'+navbBtn("about","About")+navbBtn("contact","Contact")+'</div>';
rail.querySelectorAll(".navb").forEach(el=>el.onclick=()=>jumpTo(el.dataset.t));
loadFrame(); setActiveNav("home");
}
function navbBtn(id,label,aud){ return '<button class="navb" data-t="'+id+'">'+esc(label)+(aud?'<span class="vid" onclick="event.stopPropagation();showVideo(\''+id+'\')">ad ▶</span>':'')+'</button>'; }
function loadFrame(){ document.getElementById("site-frame").srcdoc=S.siteHTML; }
function jumpTo(id){ setActiveNav(id); const f=document.getElementById("site-frame"); try{ const el=f.contentDocument.getElementById(id); if(el) el.scrollIntoView({behavior:"smooth"}); }catch(e){} }
function setActiveNav(id){ document.querySelectorAll(".navb").forEach(el=>el.classList.toggle("active",el.dataset.t===id)); }

/* ---------- video ---------- */
async function showVideo(id){
const a=S.audiences.find(x=>x.id===id); if(!a) return;
document.getElementById("vid-scrim").classList.add("on");
document.getElementById("vid-title").textContent=a.name+" — TikTok / Facebook";
const bodyEl=document.getElementById("vid-body");
if(a._video){ bodyEl.innerHTML=renderVideo(a._video); return; }
bodyEl.innerHTML='<div class="vid-block" style="text-align:center;color:var(--muted)">Alma is writing the script…</div>';
try{
const b=S.biz;
const prompt=`Write a 25-second vertical short-form video ad (TikTok/Facebook/Reels) for "${b.name}" targeting ${a.name} (${a.who}). Pain: ${a.painHint||a.whyNow}.
Return ONLY JSON: {"hook":"first-2-second scroll-stopper","scenes":[{"visual":"what's on screen","vo":"voiceover line"} x3-4],"cta":"closing call to action","captions":["3 punchy on-screen captions"]}`;
a._video=await almaJSON(prompt,{}); bodyEl.innerHTML=renderVideo(a._video);
}catch(e){ bodyEl.innerHTML='<div class="err">Couldn\'t generate the script — try again.</div>'; }
}
function renderVideo(v){
return '<div class="vid-block"><div class="lbl">Hook · 0–2s</div><b class="serif" style="font-size:1.2rem">'+esc(v.hook||"")+'</b></div>'+
'<div class="vid-block"><div class="lbl">Scenes</div>'+(v.scenes||[]).map((s,i)=>'<div class="scene"><b>Scene '+(i+1)+' · </b>'+esc(s.visual||"")+'<br><span class="dim" style="font-weight:300">VO — '+esc(s.vo||"")+'</span></div>').join("")+'</div>'+
'<div class="vid-block"><div class="lbl">Close</div><b class="serif" style="font-size:1.1rem">'+esc(v.cta||"")+'</b></div>'+
'<div class="vid-block"><div class="lbl">On-screen captions</div>'+(v.captions||[]).map(c=>'· '+esc(c)).join("<br>")+'</div>';
}

/* ---------- export ---------- */
function exportSite(){
const blob=new Blob([S.siteHTML],{type:"text/html"}), url=URL.createObjectURL(blob);
const a=document.createElement("a"); a.href=url; a.download=slug(S.biz.name)+"-website.html"; a.click(); URL.revokeObjectURL(url);
toast("Site exported — ready to host");
}

/* ---------- utils ---------- */
function esc(s){ return String(s==null?"":s).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;"); }
function escA(s){ return esc(s).replace(/"/g,"&quot;"); }
function escJS(s){ return String(s==null?"":s).replace(/\\/g,"\\\\").replace(/'/g,"\\'").replace(/\n/g," "); }
function slug(s){ return String(s||"site").toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-|-$/g,"").slice(0,40)||"site"; }
function shortName(s){ s=String(s||""); return s.length>22?s.slice(0,20)+"…":s; }
function cleanHex(h){ if(!h)return null; h=String(h).trim(); const m=h.match(/#?[0-9a-fA-F]{6}/); return m?("#"+m[0].replace("#","")):null; }
function hexToRgb(hex){ if(!hex)return null; hex=hex.replace("#",""); if(hex.length===3)hex=hex.split("").map(x=>x+x).join(""); if(hex.length<6)return null; return {r:parseInt(hex.slice(0,2),16),g:parseInt(hex.slice(2,4),16),b:parseInt(hex.slice(4,6),16)}; }
function isDark(hex){ const c=hexToRgb(hex); return c?(c.r*.299+c.g*.587+c.b*.114)<140:true; }
function contrast(hex){ return isDark(hex)?"#ffffff":"#16110a"; }
function withAlpha(hex,a){ const c=hexToRgb(hex); return c?("rgba("+c.r+","+c.g+","+c.b+","+a+")"):hex; }

/* ---------- wire ---------- */
document.getElementById("btn-build").onclick=runResearch;
document.getElementById("in-main").addEventListener("keydown",e=>{ if(e.key==="Enter") runResearch(); });
document.getElementById("btn-to-audiences").onclick=runAudiences;
document.getElementById("btn-back-input").onclick=()=>{ setSteps("input"); show("input"); };
document.getElementById("btn-back-verify").onclick=()=>{ setSteps("verify"); show("verify"); };
document.getElementById("btn-generate").onclick=runGenerate;
document.getElementById("btn-export").onclick=exportSite;
document.getElementById("btn-new").onclick=()=>{ S.biz=null;S.audiences=[];S.pages={};S.siteHTML=""; document.getElementById("input-err").innerHTML=""; document.getElementById("in-main").value=""; setSteps("input"); show("input"); };
document.getElementById("btn-open-tab").onclick=()=>{ const w=window.open("","_blank"); if(w){ w.document.open(); w.document.write(S.siteHTML); w.document.close(); } };
document.getElementById("vid-x").onclick=()=>document.getElementById("vid-scrim").classList.remove("on");
document.getElementById("vid-scrim").onclick=e=>{ if(e.target.id==="vid-scrim") e.currentTarget.classList.remove("on"); };
window.showVideo=showVideo;
setSteps("input");
</script>
</body>
</html>