lakehouse/mcp-server/console.html
root 0ff091c173 Honesty fixes — no hard-coded counts, dynamic sample CSV
- generateSampleRosterCSV(): 120-180 randomized rows per call, timestamp-prefixed IDs (no dedup on re-upload, no static 25 row lie)
- /system/summary: truth via SQL COUNT(*), surfaces manifest_drift (caught candidates: manifest 100K, actual 1K)
- search.html: loadSystemSummary() hydrates live counts; removed hard-coded 500K strings
- MCP tool description: "candidates (100K)" → "candidates (1K)", added "workers_500k (500K)"
2026-04-20 19:07:47 -05:00

471 lines
26 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en"><head>
<meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
<title>Lakehouse — What Your Staffing System Would Do</title>
<style>
*{margin:0;padding:0;box-sizing:border-box}
body{font-family:'Inter',-apple-system,system-ui,sans-serif;background:#090c10;color:#b0b8c4;font-size:14px;line-height:1.55;-webkit-font-smoothing:antialiased}
a{color:#58a6ff;text-decoration:none}
a:hover{color:#79c0ff}
.bar{background:#0d1117;padding:0 24px;height:56px;border-bottom:1px solid #171d27;display:flex;justify-content:space-between;align-items:center;position:sticky;top:0;z-index:10}
.bar h1{font-size:14px;font-weight:600;color:#e6edf3;letter-spacing:-0.2px}
.bar nav{display:flex;gap:2px}
.bar nav a{font-size:12px;color:#545d68;padding:6px 14px;border-radius:6px;transition:all 0.15s}
.bar nav a:hover{color:#e6edf3;background:#161b22}
.bar nav a.active{color:#e6edf3;background:#1c2333}
.bar .rt{font-size:11px;color:#545d68}
.wrap{max-width:1040px;margin:0 auto;padding:28px 20px 60px}
.chapter{margin-bottom:48px}
.chapter .num{color:#545d68;font-size:11px;font-weight:600;letter-spacing:1.6px;text-transform:uppercase;margin-bottom:6px}
.chapter h2{color:#e6edf3;font-size:24px;font-weight:700;letter-spacing:-0.4px;margin-bottom:8px;line-height:1.2}
.chapter .lede{color:#8b949e;font-size:14px;margin-bottom:18px;max-width:680px;line-height:1.6}
.card{background:#0d1117;border:1px solid #171d27;border-radius:12px;padding:20px;margin-bottom:12px}
.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:10px}
.stat-lg{padding:18px 20px}
.stat-lg .n{font-size:30px;font-weight:800;color:#e6edf3;letter-spacing:-1px;line-height:1}
.stat-lg .l{font-size:10px;color:#545d68;text-transform:uppercase;letter-spacing:1.2px;margin-top:8px;font-weight:600}
.stat-lg .sub{font-size:12px;color:#8b949e;margin-top:4px}
.pill{display:inline-block;padding:3px 10px;border-radius:10px;font-size:10px;font-weight:600;letter-spacing:0.3px;margin-right:6px}
.pill.real{background:#0d2818;color:#3fb950;border:1px solid #2ea04380}
.pill.synth{background:#1a1a2e;color:#bc8cff;border:1px solid #5530988c}
.pill.flow{background:#0d2340;color:#58a6ff;border:1px solid #1f6feb80}
.row{display:flex;justify-content:space-between;align-items:center;gap:12px;padding:10px 14px;background:#0d1117;border:1px solid #171d27;border-radius:8px;margin-bottom:6px;font-size:13px}
.row:hover{border-color:#21262d}
.row .title{color:#e6edf3;font-weight:500}
.row .meta{color:#8b949e;font-size:11px;margin-top:2px}
.row .val{color:#58a6ff;font-weight:600;white-space:nowrap}
details{background:#0d1117;border:1px solid #171d27;border-radius:8px;padding:10px 14px;margin-bottom:6px}
details summary{cursor:pointer;font-size:13px;color:#e6edf3;font-weight:500;list-style:none}
details summary::-webkit-details-marker{display:none}
details .body{padding-top:10px;font-size:12px;color:#8b949e}
.accent-l{border-left:3px solid #2ea043}
.accent-b{border-left:3px solid #1f6feb}
.accent-a{border-left:3px solid #bc8cff}
.accent-w{border-left:3px solid #d29922}
.worker{display:flex;align-items:center;gap:10px;padding:8px 10px;background:#161b22;border-radius:6px;margin-bottom:4px;font-size:12px}
.worker .av{width:28px;height:28px;border-radius:6px;background:#1a2744;display:flex;align-items:center;justify-content:center;font-weight:600;color:#e6edf3;font-size:10px;flex-shrink:0}
.worker .info{flex:1;min-width:0}
.worker .nm{color:#e6edf3;font-weight:500}
.worker .why{color:#545d68;font-size:11px;margin-top:1px}
.worker .score{color:#58a6ff;font-size:11px;font-weight:600;white-space:nowrap}
.mem-chip{background:#0d2818;border:1px solid #2ea04360;border-radius:6px;padding:8px 12px;font-size:11px;color:#86efac;line-height:1.5;margin-top:6px}
.mem-chip .l{color:#3fb950;font-weight:600;margin-right:6px}
.boost-chip{display:inline-block;margin-left:6px;padding:2px 7px;border-radius:9px;font-size:9px;font-weight:600;background:#0d2818;border:1px solid #2ea043;color:#3fb950;vertical-align:middle}
.try-box{background:#0d1117;border:1px solid #171d27;border-radius:12px;padding:16px}
.try-box input{width:100%;padding:12px 16px;background:#161b22;border:1px solid #21262d;border-radius:8px;color:#e6edf3;font-size:13px;outline:none;margin-bottom:10px}
.try-box input:focus{border-color:#388bfd}
.try-box button{padding:10px 20px;background:#1f6feb;border:none;border-radius:8px;color:#fff;font-size:13px;font-weight:600;cursor:pointer}
.try-box button:hover{background:#388bfd}
.try-box button:disabled{opacity:0.5;cursor:wait}
.footer{border-top:1px solid #171d27;padding:20px;text-align:center;color:#3d444d;font-size:11px}
.loading{color:#484f58;font-style:italic;padding:20px 0;text-align:center}
.err{color:#f85149;font-size:12px;padding:10px}
.narr{color:#8b949e;font-size:13px;line-height:1.7;margin:10px 0;padding:10px 14px;border-left:2px solid #21262d}
.narr strong{color:#c9d1d9;font-weight:600}
.step-label{color:#58a6ff;font-size:12px;margin-top:12px;font-weight:600}
.step-body{color:#c9d1d9;font-size:13px;margin-top:2px}
@media(max-width:720px){
.wrap{padding:20px 12px 40px}
.chapter h2{font-size:20px}
.bar nav{display:none}
}
</style></head>
<body>
<div class="bar">
<h1>Lakehouse — What Your Staffing System Would Do</h1>
<nav>
<a href=".">Dashboard</a>
<a href="console" class="active">Walkthrough</a>
<a href="proof">Architecture</a>
<a href="spec">Spec</a>
<a href="onboard">Onboard</a>
<a href="alerts">Alerts</a>
<a href="workspaces">Workspaces</a>
</nav>
<div class="rt" id="hdr-time">Reading live state…</div>
</div>
<div class="wrap">
<div class="chapter">
<div class="num">Chapter 1</div>
<h2>Right now, this system is already thinking</h2>
<div class="lede">Before you touched anything, it pulled real Chicago building-permit data, measured demand, checked your bench, and began flagging roles that need attention. This isn't theoretical — open your browser network tab and watch the fetches land.</div>
<div class="grid" id="ch1-stats"><div class="loading">Fetching live state…</div></div>
<div class="narr" id="ch1-narr"></div>
</div>
<div class="chapter">
<div class="num">Chapter 2</div>
<h2>The demand signal is real, not made up</h2>
<div class="lede">Chicago's Department of Buildings publishes every permit they issue. Below are the largest categories of construction filed in the last 30 days. If a staffer doesn't believe our numbers, they can verify at <a href="https://data.cityofchicago.org/Buildings/Building-Permits/ydr8-5enu" target="_blank" rel="noopener">data.cityofchicago.org</a>.</div>
<div id="ch2-permits"><div class="loading">Loading permit feed…</div></div>
</div>
<div class="chapter">
<div class="num">Chapter 3</div>
<h2>Where your own data would live</h2>
<div class="lede">The system stores data in labeled catalogs. Purple pills = synthetic stand-ins you'd swap for your real ATS/CRM/call-log exports. Blue pills = data the system generates about itself (playbooks, audit trails). Nothing else in the pipeline changes — only the source.</div>
<div id="ch3-datasets"><div class="loading">Enumerating catalog…</div></div>
<div class="narr">
<strong>The swap path.</strong> workers_500k → your ATS export (same schema shape). candidates → your CRM. call_log → your phone system's CDR. timesheets → your payroll export. Once ingested, every behavior you see on the dashboard applies to your real data. No re-training. No replatform.
</div>
</div>
<div class="chapter">
<div class="num">Chapter 4</div>
<h2>Watch the system rank candidates in real time</h2>
<div class="lede">This takes the most recent Chicago permit, derives the staffing need, pulls ranked candidates from the bench, and shows you why each one ranked. Everything below loaded in about 3 seconds against the live system.</div>
<div id="ch4-demo"><div class="loading">Running demo query…</div></div>
</div>
<div class="chapter">
<div class="num">Chapter 5</div>
<h2>Every action compounds — the CRM-killer</h2>
<div class="lede">A CRM stores. This system compounds. Every successful fill, every no-show, every phone call becomes a re-ranking signal on the next query. Below is the live playbook memory state. The number grows as the app gets used.</div>
<div id="ch5-memory"><div class="loading">Reading playbook memory…</div></div>
</div>
<div class="chapter">
<div class="num">Chapter 6</div>
<h2>Try it yourself</h2>
<div class="lede">Type any staffing question. The system picks the right search path (smart-parse, semantic discovery, analytics), shows what it understood, and returns ranked results with memory signal.</div>
<div class="try-box">
<input type="text" id="try-q" placeholder="e.g. reliable forklift operators in Chicago with OSHA certs" onkeydown="if(event.key==='Enter')runTry()">
<button id="try-btn" onclick="runTry()">Ask</button>
<div id="try-out" style="margin-top:16px"></div>
</div>
</div>
</div>
<div class="footer">Lakehouse · demo instance · <a href="proof">architecture &amp; benchmarks</a></div>
<script>
var P=location.pathname.indexOf('/lakehouse')>=0?'/lakehouse':'';
var A=location.origin+P;
// DOM helpers — all dynamic content goes through these. No innerHTML
// anywhere in the script; every API-derived string passes through
// textContent so no injection path regardless of upstream data.
function el(tag, cls, text){
var e=document.createElement(tag);
if(cls) e.className=cls;
if(text!==undefined && text!==null) e.textContent=String(text);
return e;
}
function setAttrs(e, obj){
for(var k in obj) if(Object.prototype.hasOwnProperty.call(obj,k)) e.setAttribute(k,obj[k]);
return e;
}
function api(path, body){
return fetch(A+path,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(body||{})}).then(function(r){return r.json()});
}
function apiGet(path){
return fetch(A+path).then(function(r){return r.json()});
}
window.addEventListener('load',function(){
loadChapter1();
loadChapter2();
loadChapter3();
loadChapter4();
loadChapter5();
});
// ─── Chapter 1 ────────────────────────────────────────────
function loadChapter1(){
Promise.all([
api('/intelligence/staffing_forecast',{}).catch(function(){return {}}),
apiGet('/api/vectors/playbook_memory/stats').catch(function(){return {}}),
apiGet('/api/catalog/datasets').catch(function(){return []}),
]).then(function(all){
var f=all[0]||{}, mem=all[1]||{}, ds=all[2]||[];
var totalRows=0;
if(Array.isArray(ds)) ds.forEach(function(d){totalRows+=(d.row_count||0)});
var host=document.getElementById('ch1-stats');host.textContent='';
addStat(host,'$'+(f.total_cost||0).toLocaleString('en-US',{maximumFractionDigits:0}),'Construction pipeline',(f.permit_count||0)+' permits last 30 days','accent-b');
addStat(host,(f.total_estimated_workers||0).toLocaleString(),'Predicted worker demand','split across '+((f.forecast||[]).length)+' roles','accent-w');
addStat(host,(totalRows||0).toLocaleString(),'Rows under management',(Array.isArray(ds)?ds.length:0)+' datasets · real + synthetic','accent-a');
addStat(host,(mem.entries||0).toLocaleString(),'Playbooks remembered',(mem.total_names_endorsed||0)+' endorsed worker-tags','accent-l');
var narrEl=document.getElementById('ch1-narr');
narrEl.textContent='';
var critical=(f.critical_roles||0), tight=(f.tight_roles||0);
if(critical>0){
narrEl.appendChild(document.createTextNode('The system has already flagged '));
narrEl.appendChild(el('strong',null,critical+' role'+(critical!==1?'s':'')+' at critical coverage'));
narrEl.appendChild(document.createTextNode(' — supply is below demand. A CRM would surface this only if someone ran the report. This system surfaces it before you log in.'));
} else if(tight>0){
narrEl.appendChild(document.createTextNode('The system flagged '));
narrEl.appendChild(el('strong',null,tight+' role'+(tight!==1?'s':'')+' as tight'));
narrEl.appendChild(document.createTextNode(' — watch for supply compression. Pre-emptive signal, no query required.'));
} else {
narrEl.textContent='No coverage gaps right now — bench exceeds predicted demand across all roles. The system is tracking this continuously; check back tomorrow and the numbers will have moved.';
}
}).catch(function(e){
var h=document.getElementById('ch1-stats');h.textContent='';h.appendChild(el('div','err','Live state unavailable: '+(e.message||e)));
});
document.getElementById('hdr-time').textContent='Snapshot · '+new Date().toLocaleTimeString();
}
function addStat(host,n,l,sub,cls){
var d=el('div','card stat-lg '+(cls||''));
d.appendChild(el('div','n',n));
d.appendChild(el('div','l',l));
d.appendChild(el('div','sub',sub||''));
host.appendChild(d);
}
// ─── Chapter 2 ────────────────────────────────────────────
function loadChapter2(){
api('/intelligence/market',{}).then(function(r){
var host=document.getElementById('ch2-permits');host.textContent='';
if(r.error){host.appendChild(el('div','err','Permit feed error: '+r.error));return}
var byType=r.by_type||[];
if(byType.length===0){host.appendChild(el('div','narr','No permits returned by the Chicago API right now.'));return}
byType.slice(0,8).forEach(function(t){
var det=document.createElement('details');
var summary=document.createElement('summary');
summary.appendChild(el('span','pill real','LIVE · DATA.CITYOFCHICAGO.ORG'));
summary.appendChild(document.createTextNode(' '));
var wt=el('strong',null,t.work_type||'General construction');
summary.appendChild(wt);
summary.appendChild(document.createTextNode(' · '+(t.permits||0)+' permits · $'+(t.total_cost||0).toLocaleString('en-US',{maximumFractionDigits:0})+' · est '+(t.estimated_workers||0)+' workers needed'));
det.appendChild(summary);
var body=el('div','body');
body.appendChild(document.createTextNode('Likely roles: '+((t.needed_roles||[]).join(', ')||'—')+'. '));
body.appendChild(document.createTextNode('At industry heuristic (~$150K per worker), this work type alone implies '));
body.appendChild(el('strong',null,(t.estimated_workers||0)+' staffing opportunities'));
body.appendChild(document.createTextNode(' over the next 30-60 days.'));
det.appendChild(body);
host.appendChild(det);
});
}).catch(function(e){
var h=document.getElementById('ch2-permits');h.textContent='';h.appendChild(el('div','err',e.message||String(e)));
});
}
// ─── Chapter 3 ────────────────────────────────────────────
function loadChapter3(){
apiGet('/api/catalog/datasets').then(function(ds){
var host=document.getElementById('ch3-datasets');host.textContent='';
if(!Array.isArray(ds)){host.appendChild(el('div','err','No datasets returned.'));return}
var bigOnes=ds.filter(function(d){return (d.row_count||0)>=500}).sort(function(a,b){return (b.row_count||0)-(a.row_count||0)});
var playbookKeys=['successful_playbooks','playbook_memory','kb_'];
bigOnes.slice(0,12).forEach(function(d){
var row=el('div','row');
var left=document.createElement('div');left.style.flex='1';left.style.minWidth='0';
var t=el('div','title');
var pillCls='synth', pillLabel='SWAP FOR YOUR DATA';
if(playbookKeys.some(function(k){return (d.name||'').indexOf(k)>=0})){pillCls='flow';pillLabel='SYSTEM-GENERATED'}
else if((d.name||'').indexOf('threat_')===0){pillCls='flow';pillLabel='SYSTEM-GENERATED'}
t.appendChild(el('span','pill '+pillCls,pillLabel));
t.appendChild(document.createTextNode(' '+(d.name||'(unnamed)')));
var m=el('div','meta',(d.columns?d.columns.length+' columns · ':'')+(d.objects?d.objects.length+' parquet file'+(d.objects.length!==1?'s':''):''));
left.appendChild(t);left.appendChild(m);
var v=el('div','val',(d.row_count||0).toLocaleString()+' rows');
row.appendChild(left);row.appendChild(v);host.appendChild(row);
});
}).catch(function(e){
var h=document.getElementById('ch3-datasets');h.textContent='';h.appendChild(el('div','err','Catalog unavailable: '+(e.message||e)));
});
}
// ─── Chapter 4 ────────────────────────────────────────────
function loadChapter4(){
api('/intelligence/permit_contracts',{}).then(function(r){
var host=document.getElementById('ch4-demo');host.textContent='';
if(!r.contracts||r.contracts.length===0){host.appendChild(el('div','loading','No contracts returned.'));return}
var c=r.contracts[0];
var p=c.permit||{}, prop=c.proposed||{}, tl=c.timeline||{};
var card=el('div','card accent-b');
var title=el('div',null,'$'+(p.cost||0).toLocaleString()+' · '+(p.work_type||''));
title.style.cssText='font-size:15px;font-weight:600;color:#e6edf3';
card.appendChild(title);
var addr=el('div',null,(p.address||'')+' · Chicago, IL · filed '+(p.issue_date||''));
addr.style.cssText='color:#8b949e;font-size:12px;margin-top:2px';
card.appendChild(addr);
card.appendChild(el('div','step-label','STEP 1 · Derive staffing need'));
var s1=el('div','step-body');
s1.appendChild(document.createTextNode('Industry heuristic: ~1 worker per $150K of permit cost, capped 2-8. Resulting contract: '));
s1.appendChild(el('strong',null,(prop.count||0)+'× '+(prop.role||'')));
s1.appendChild(document.createTextNode(' in Chicago, IL.'));
card.appendChild(s1);
card.appendChild(el('div','step-label','STEP 2 · Narrow via SQL ('+((prop.pool_size||0).toLocaleString())+' candidates match)'));
card.appendChild(el('div','step-body',"role = '"+(prop.role||'')+"' AND state = 'IL' AND city = 'Chicago' AND CAST(availability AS DOUBLE) > 0.5"));
card.appendChild(el('div','step-label','STEP 3 · Rank via semantic match + playbook boost'));
host.appendChild(card);
var list=document.createElement('div');list.style.marginTop='6px';
(prop.candidates||[]).slice(0,5).forEach(function(cand,i){
var w=el('div','worker');
var initials=(cand.name||'?').split(' ').map(function(s){return (s[0]||'').toUpperCase()}).join('').substring(0,2);
w.appendChild(el('div','av',initials));
var info=el('div','info');
var nm=el('div','nm',cand.name||cand.doc_id||'?');
if((cand.playbook_boost||0)>0){
var ncit=(cand.playbook_citations||[]).length;
nm.appendChild(el('span','boost-chip','Endorsed · '+ncit+' past fill'+(ncit!==1?'s':'')));
}
info.appendChild(nm);
var why=cand.doc_id+' · '+(cand.playbook_boost>0?'boosted +'+cand.playbook_boost.toFixed(3)+' by memory · ':'')+'semantic score '+(cand.score||0).toFixed(3);
info.appendChild(el('div','why',why));
w.appendChild(info);
w.appendChild(el('div','score','#'+(i+1)));
list.appendChild(w);
});
card.appendChild(list);
if(c.discovered_pattern||c.pattern_matched>0){
var mem=el('div','mem-chip');
mem.appendChild(el('span','l','MEMORY ('+(c.pattern_matched||0)+' past playbooks):'));
mem.appendChild(document.createTextNode(' '+(c.discovered_pattern||'memory is sparse for this role+geo')));
card.appendChild(mem);
}
if(tl.days_to_deadline!==undefined){
var tm=document.createElement('div');
tm.style.cssText='margin-top:12px;padding-top:10px;border-top:1px solid #171d27;font-size:12px;color:#8b949e';
tm.appendChild(el('strong',null,'Timeline. '));
tm.childNodes[0].style && (tm.childNodes[0].style.color='#c9d1d9');
var dd=tl.days_to_deadline;
var when=(dd<=0?Math.abs(dd)+' days overdue':dd+' days out');
tm.appendChild(document.createTextNode('Staffing window opens '+(tl.staffing_window_opens||'')+' ('+when+'). Construction starts '+(tl.estimated_construction_start||'')+'. Urgency: '));
tm.appendChild(el('strong',null,(tl.urgency||'').toUpperCase()));
tm.appendChild(document.createTextNode('.'));
card.appendChild(tm);
}
var note=el('div','narr');
note.appendChild(el('strong',null,'This is the CRM gap. '));
note.appendChild(document.createTextNode('A CRM would tell you the permit exists. It would NOT narrow to matching candidates, would NOT boost past-successful workers, would NOT flag patterns across similar past fills, would NOT count down to the deadline. All that happens here, pre-computed, before the staffer clicks anything.'));
host.appendChild(note);
}).catch(function(e){
var h=document.getElementById('ch4-demo');h.textContent='';h.appendChild(el('div','err','Demo failed: '+(e.message||e)));
});
}
// ─── Chapter 5 ────────────────────────────────────────────
function loadChapter5(){
apiGet('/api/vectors/playbook_memory/stats').then(function(mem){
var host=document.getElementById('ch5-memory');host.textContent='';
var card=el('div','card accent-l');
var big=el('div',null,(mem.entries||0).toLocaleString());
big.style.cssText='font-size:30px;font-weight:800;color:#e6edf3;line-height:1';
card.appendChild(big);
var lb=el('div',null,'Playbooks in memory');
lb.style.cssText='font-size:10px;color:#545d68;text-transform:uppercase;letter-spacing:1.2px;margin-top:8px;font-weight:600';
card.appendChild(lb);
var sub=el('div',null,(mem.total_names_endorsed||0).toLocaleString()+' endorsed worker-tags · each one boosts future matching queries');
sub.style.cssText='font-size:12px;color:#8b949e;margin-top:4px';
card.appendChild(sub);
host.appendChild(card);
if(mem.sample&&mem.sample.length){
var hdr=document.createElement('div');
hdr.style.cssText='color:#545d68;font-size:11px;text-transform:uppercase;letter-spacing:1.4px;font-weight:600;margin:14px 0 8px';
hdr.textContent='Sample playbooks held in memory right now';
host.appendChild(hdr);
mem.sample.slice(0,5).forEach(function(pb){
var row=el('div','row');
var left=document.createElement('div');left.style.flex='1';left.style.minWidth='0';
left.appendChild(el('div','title',pb.operation||'(no operation)'));
left.appendChild(el('div','meta',(pb.city||'')+', '+(pb.state||'')+' · endorsed: '+((pb.endorsed||[]).slice(0,3).join(', ')||'—')));
row.appendChild(left);host.appendChild(row);
});
}
var narr=el('div','narr');
narr.appendChild(el('strong',null,'What this means for a staffer. '));
narr.appendChild(document.createTextNode('When you log a successful fill, this counter grows by one. Next time someone searches for a similar role+city, the system finds your past pick and boosts them to the top. Over months, the system becomes a record of who has actually worked out — not just who is on the roster.'));
host.appendChild(narr);
}).catch(function(e){
var h=document.getElementById('ch5-memory');h.textContent='';h.appendChild(el('div','err','Memory unavailable: '+(e.message||e)));
});
}
// ─── Chapter 6 ────────────────────────────────────────────
function runTry(){
var q=document.getElementById('try-q').value.trim();if(!q)return;
var btn=document.getElementById('try-btn'),out=document.getElementById('try-out');
btn.disabled=true;btn.textContent='Thinking…';out.textContent='';out.appendChild(el('div','loading','Running…'));
api('/intelligence/chat',{message:q}).then(function(d){
btn.disabled=false;btn.textContent='Ask';out.textContent='';
var card=el('div','card accent-b');
if(d.understood&&d.understood.length){
var tags=document.createElement('div');tags.style.cssText='display:flex;gap:6px;flex-wrap:wrap;margin-bottom:10px';
d.understood.forEach(function(u){
var tg=el('span',null,u);
tg.style.cssText='padding:3px 10px;border-radius:10px;font-size:11px;background:#1a274420;color:#58a6ff;border:1px solid #1a274480';
tags.appendChild(tg);
});
card.appendChild(tags);
}
var sum=el('div',null,d.summary||'(no summary)');
sum.style.cssText='color:#c9d1d9;font-size:13px;margin-bottom:10px';
card.appendChild(sum);
if(d.pattern_playbooks_matched>0&&d.discovered_pattern){
var mem=el('div','mem-chip');
mem.appendChild(el('span','l','MEMORY ('+d.pattern_playbooks_matched+' playbooks):'));
mem.appendChild(document.createTextNode(' '+d.discovered_pattern));
card.appendChild(mem);
}
var workers=d.sql_results||d.vector_results||d.results||[];
workers.slice(0,5).forEach(function(w,i){
var row=el('div','worker');
var nm=w.name||(w.text||'').split('—')[0].trim()||w.doc_id||'?';
var initials=nm.split(' ').map(function(s){return (s[0]||'').toUpperCase()}).join('').substring(0,2);
row.appendChild(el('div','av',initials));
var info=el('div','info');
var n=el('div','nm',nm);
if((w.playbook_boost||0)>0){
n.appendChild(el('span','boost-chip','Endorsed · '+((w.playbook_citations||[]).length||'?')+' past fill(s)'));
}
info.appendChild(n);
var bits=[];
if(w.role) bits.push(w.role);
if(w.city&&w.state) bits.push(w.city+', '+w.state);
if(w.rel!==undefined) bits.push('reliability '+Math.round(w.rel*100)+'%');
if(w.avail!==undefined) bits.push('availability '+Math.round(w.avail*100)+'%');
info.appendChild(el('div','why',bits.join(' · ')||'AI semantic match'));
row.appendChild(info);
row.appendChild(el('div','score','#'+(i+1)));
card.appendChild(row);
});
var meta=el('div',null,(d.duration_ms||0)+'ms · type='+(d.type||'smart_search'));
meta.style.cssText='color:#545d68;font-size:11px;margin-top:10px';
card.appendChild(meta);
out.appendChild(card);
}).catch(function(e){
btn.disabled=false;btn.textContent='Ask';
out.textContent='';out.appendChild(el('div','err',e.message||String(e)));
});
}
</script>
</body></html>