Rebuild /console as narrative walkthrough for a skeptical staffer

Old console was a chat playground. New console is a guided,
chapter-based explanation that a non-technical staffing staffer
can read top-down and finish convinced — without needing to
understand any of the underlying technology.

Six chapters, each loading live data:

1. Right now, this system is already thinking
   Four stats cards pulled live: construction pipeline $, predicted
   worker demand, rows under management, playbooks remembered. Then
   a narrative that names the current alert posture (critical/tight/ok).

2. The demand signal is real, not made up
   Expandable rows per Chicago permit work_type, with a direct link to
   data.cityofchicago.org for verification. Pill labeled LIVE ·
   DATA.CITYOFCHICAGO.ORG leaves no ambiguity.

3. Where your own data would live
   Catalog enumerated with three pill classes:
   - SWAP FOR YOUR DATA (purple) — the synthetic tables that would
     be replaced by the client's ATS/CRM/call-log exports
   - SYSTEM-GENERATED (blue) — playbook memory, threat_intel, kb_*
     produced by the system itself
   Row counts + columns visible. Names it honestly.

4. Watch the system rank candidates in real time
   Takes the freshest Chicago permit, walks the staffer through all
   three steps (derive need → narrow via SQL → rank + boost), shows
   the top-5 workers with why, boost chip, memory chip, timeline,
   and a plain-English narrative of the CRM gap.

5. Every action compounds
   Playbook memory count + sample + narrative about what it means
   when the staffer logs a fill.

6. Try it yourself
   Free-text input hitting /intelligence/chat, renders response
   with memory chip + boost chips + ranked workers.

Security: all API-derived strings go through textContent or
el(tag,cls,text) helper. Zero innerHTML usage on dynamic content.
Passes security reminder hook.

File size: 419 → ~500 lines. Visual style matches the dashboard
(same palette, typography, chip styles) so the two pages feel
like one app.
This commit is contained in:
root 2026-04-20 17:35:45 -05:00
parent bb1b471c67
commit 05f2e42c45

View File

@ -1,419 +1,466 @@
<!DOCTYPE html>
<html><head>
<html lang="en"><head>
<meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
<title>Staffing Intelligence Console</title>
<title>Lakehouse — What Your Staffing System Would Do</title>
<style>
*{margin:0;padding:0;box-sizing:border-box}
body{font-family:-apple-system,system-ui,sans-serif;background:#06090f;color:#c9d1d9;font-size:14px;line-height:1.5}
.bar{background:#0d1117;padding:14px 20px;border-bottom:1px solid #1b2130;display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:8px}
.bar h1{font-size:15px;font-weight:600;color:#f0f6fc;letter-spacing:0.5px}
.bar .rt{font-size:11px;color:#484f58}
.wrap{max-width:960px;margin:0 auto;padding:20px 16px}
.brief-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:12px;margin-bottom:24px}
.bcard{background:#0d1117;border:1px solid #1b2130;border-radius:10px;padding:18px;position:relative;overflow:hidden}
.bcard .label{font-size:9px;text-transform:uppercase;letter-spacing:1.5px;color:#484f58;margin-bottom:8px}
.bcard .headline{font-size:16px;font-weight:700;color:#f0f6fc;margin-bottom:4px}
.bcard .sub{font-size:12px;color:#8b949e;margin-bottom:12px}
.bcard .big{font-size:36px;font-weight:800;line-height:1}
.bcard.pulse{border-left:3px solid #58a6ff}
.bcard.bench{border-left:3px solid #d29922}
.bcard.gems{border-left:3px solid #3fb950}
.bcard.risk{border-left:3px solid #f85149}
.bcard.talent{border-left:3px solid #bc8cff}
.bcard.supply{border-left:3px solid #79c0ff}
.arch-row{display:flex;gap:4px;flex-wrap:wrap;margin-top:10px}
.arch-pill{padding:3px 10px;border-radius:10px;font-size:11px;font-weight:500}
.bench-row{display:flex;align-items:center;gap:8px;margin-bottom:6px;font-size:12px}
.bench-row .st{width:24px;font-weight:600;color:#f0f6fc}
.bench-bar{flex:1;height:14px;background:#161b22;border-radius:4px;overflow:hidden;position:relative}
.bench-fill{height:100%;border-radius:4px;transition:width 0.8s}
.bench-row .pct{width:40px;text-align:right;font-size:11px;font-weight:600}
.wk{display:flex;align-items:center;gap:10px;padding:8px;background:#161b22;border-radius:6px;margin-bottom:4px}
.wk .av{width:32px;height:32px;border-radius:8px;display:flex;align-items:center;justify-content:center;font-weight:700;font-size:12px;color:#f0f6fc;flex-shrink:0}
.wk .info{flex:1;min-width:0}
.wk .nm{font-weight:600;color:#f0f6fc;font-size:13px}
.wk .det{color:#8b949e;font-size:11px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.wk .badge{padding:2px 8px;border-radius:8px;font-size:10px;font-weight:600;flex-shrink:0}
.prompts{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:16px}
.prompt-btn{padding:8px 16px;background:#161b22;border:1px solid #1b2130;border-radius:20px;color:#8b949e;font-size:12px;cursor:pointer;transition:all 0.2s}
.prompt-btn:hover{border-color:#58a6ff;color:#58a6ff;background:#0d1525}
.chat-box{background:#0d1117;border:1px solid #1b2130;border-radius:12px;overflow:hidden;margin-bottom:16px}
.chat-messages{min-height:60px;max-height:600px;overflow-y:auto;padding:16px}
.msg{margin-bottom:16px;animation:fadeIn 0.3s ease}
@keyframes fadeIn{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}
.msg.user{text-align:right}
.msg.user .bubble{display:inline-block;background:#1f3d68;color:#c9d6ff;padding:10px 16px;border-radius:16px 16px 4px 16px;max-width:70%;text-align:left;font-size:13px}
.msg.system .bubble{background:#161b22;border:1px solid #1b2130;border-radius:4px 16px 16px 16px;padding:16px;font-size:13px}
.msg .meta{font-size:10px;color:#484f58;margin-top:6px}
.msg .steps{margin-bottom:10px}
.msg .step{font-size:11px;color:#58a6ff;padding:2px 0}
.msg .summary{font-size:14px;font-weight:600;color:#f0f6fc;margin-bottom:8px}
.msg .note{font-size:11px;color:#8b949e;font-style:italic;margin-top:8px;padding:8px;background:#0d1117;border-radius:6px}
.result-card{background:#0d1117;border:1px solid #1b2130;border-radius:8px;padding:12px;margin-bottom:6px}
.result-card .rc-name{font-weight:600;color:#f0f6fc;font-size:13px}
.result-card .rc-detail{font-size:11px;color:#8b949e;margin-top:2px}
.result-card .rc-score{font-size:10px;color:#58a6ff;margin-top:4px}
.result-card .rc-skills{display:flex;gap:4px;flex-wrap:wrap;margin-top:6px}
.result-card .rc-skill{padding:2px 8px;background:#1a2744;color:#58a6ff;border-radius:8px;font-size:10px}
.result-card .rc-cert{padding:2px 8px;background:#1a3a2a;color:#3fb950;border-radius:8px;font-size:10px}
.source-card{background:#1a1a00;border:1px solid #854d0e;border-radius:8px;padding:12px;margin-bottom:10px}
.whatif-severity{display:inline-block;padding:3px 10px;border-radius:8px;font-size:11px;font-weight:700;margin-left:8px}
.input-bar{display:flex;gap:8px;padding:12px 16px;border-top:1px solid #1b2130;background:#0d1117}
.input-bar input{flex:1;padding:12px 16px;background:#06090f;border:1px solid #1b2130;border-radius:10px;color:#f0f6fc;font-size:14px;outline:none}
.input-bar input:focus{border-color:#58a6ff}
.input-bar button{padding:12px 20px;background:#58a6ff;border:none;border-radius:10px;color:#0d1117;font-weight:700;font-size:14px;cursor:pointer}
.input-bar button:hover{background:#79c0ff}
.input-bar button:disabled{opacity:0.4;cursor:default}
.thinking{display:flex;align-items:center;gap:8px;color:#58a6ff;font-size:12px;padding:12px}
.dots span{animation:blink 1.4s infinite both;margin:0 1px}
.dots span:nth-child(2){animation-delay:0.2s}
.dots span:nth-child(3){animation-delay:0.4s}
@keyframes blink{0%,80%,100%{opacity:0.2}40%{opacity:1}}
.supply-row{display:flex;justify-content:space-between;align-items:center;padding:5px 8px;font-size:12px;border-bottom:1px solid #0d1117}
.supply-row:last-child{border-bottom:none}
.supply-role{color:#f0f6fc;font-weight:500}
.supply-nums{color:#8b949e;font-size:11px}
.ft{text-align:center;padding:16px;color:#2d333b;font-size:10px}
@media(max-width:768px){.brief-grid{grid-template-columns:1fr}.msg.user .bubble{max-width:90%}}
</style></head><body>
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>Staffing Intelligence Console</h1>
<div class="rt" id="brief-time">Analyzing 500,000 profiles...</div>
<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>
</nav>
<div class="rt" id="hdr-time">Reading live state…</div>
</div>
<div class="wrap">
<div id="brief" class="brief-grid"><div style="grid-column:1/-1;text-align:center;padding:40px;color:#484f58">Scanning your entire workforce...</div></div>
<div class="prompts" id="prompts"></div>
<div class="chat-box">
<div class="chat-messages" id="chat"></div>
<div class="input-bar">
<input type="text" id="q" placeholder="Ask anything about your workforce..." onkeydown="if(event.key==='Enter'&&!event.shiftKey)send()">
<button id="send-btn" onclick="send()">Send</button>
<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="ft">Powered by Lakehouse — Hybrid SQL + Vector Search across 500,000 embedded worker profiles</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 500K 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.replace(/\/console\/?$/,'');
var P=location.pathname.indexOf('/lakehouse')>=0?'/lakehouse':'';
var A=location.origin+P;
var AC=['#1a2744','#1a3a2a','#2a1a3a','#3a2a1a','#1a3a3a','#2a2a1a','#3a1a2a','#1a2a3a'];
var briefData=null;
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()})
// 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 el(tag,css,text){var e=document.createElement(tag);if(css)e.style.cssText=css;if(text)e.textContent=text;return e}
function div(cls,text){var d=document.createElement('div');if(cls)d.className=cls;if(text)d.textContent=text;return d}
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',loadBrief);
window.addEventListener('load',function(){
loadChapter1();
loadChapter2();
loadChapter3();
loadChapter4();
loadChapter5();
});
function loadBrief(){
api('/intelligence/brief').then(function(d){
briefData=d;
var container=document.getElementById('brief');
container.textContent='';
document.getElementById('brief-time').textContent='Analyzed '+(d.pool.total||0).toLocaleString()+' profiles in '+(d.duration_ms||0)+'ms';
var pool=d.pool;
var relPct=pool.total?Math.round(pool.reliable/pool.total*100):0;
var elitePct=pool.total?Math.round(pool.elite/pool.total*100):0;
// ─── 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');
// Card 1: Workforce Pulse
var c1=makeCard('pulse','WORKFORCE PULSE','Your A-Team',pool.reliable.toLocaleString()+' workers with 80%+ reliability — '+relPct+'% of your pool');
var bigRow=el('div','display:flex;gap:20px;margin-bottom:12px');
addBigNum(bigRow,pool.total.toLocaleString(),'Total Workers','#f0f6fc');
addBigNum(bigRow,relPct+'%','Reliable','#3fb950');
addBigNum(bigRow,elitePct+'%','Elite (90%+)','#58a6ff');
c1.appendChild(bigRow);
if(d.archetypes&&d.archetypes.length){
var ar=div('arch-row');
var archColors={reliable:'#3fb950',communicator:'#58a6ff',flexible:'#d29922',leader:'#bc8cff',specialist:'#f0883e',improving:'#79c0ff',erratic:'#f85149',silent:'#484f58'};
d.archetypes.forEach(function(a){
var pct=pool.total?Math.round(a.cnt/pool.total*100):0;
var color=archColors[a.archetype]||'#484f58';
var pill=el('span','background:'+color+'20;color:'+color+';border:1px solid '+color+'40;padding:3px 10px;border-radius:10px;font-size:11px;font-weight:500',a.archetype+' '+pct+'%');
pill.className='arch-pill';ar.appendChild(pill);
});
c1.appendChild(ar);
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.';
}
container.appendChild(c1);
}).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);
}
// Card 2: Geographic Bench
var c2=makeCard('bench','GEOGRAPHIC BENCH','Where You\'re Strong — and Thin','Reliable worker density by state');
var sorted=(d.bench||[]).slice().sort(function(a,b){return (b.total?b.reliable/b.total:0)-(a.total?a.reliable/a.total:0)});
sorted.forEach(function(s){
var pct=s.total?Math.round(s.reliable/s.total*100):0;
var color=pct>45?'#3fb950':pct>35?'#d29922':'#f85149';
var row=div('bench-row');
row.appendChild(el('span','width:24px;font-weight:600;color:#f0f6fc',s.state));
var bar=el('div','flex:1;height:14px;background:#161b22;border-radius:4px;overflow:hidden');
bar.appendChild(el('div','height:100%;border-radius:4px;background:'+color+';width:'+pct+'%;transition:width 0.8s'));
row.appendChild(bar);
row.appendChild(el('span','width:40px;text-align:right;font-size:11px;font-weight:600;color:'+color,pct+'%'));
row.appendChild(el('span','font-size:10px;color:#484f58;width:60px',s.total.toLocaleString()));
c2.appendChild(row);
// ─── 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);
});
var weakest=sorted[sorted.length-1];
if(weakest){
var wpct=weakest.total?Math.round(weakest.reliable/weakest.total*100):0;
c2.appendChild(makeCallout('Warning: '+weakest.state+' is your thinnest bench at '+wpct+'% reliable. If a client there ramps up, you may struggle to fill.','#1a1500','#854d0e'));
}
container.appendChild(c2);
// Card 3: Comeback Watch
if(d.gems&&d.gems.length){
var c3=makeCard('gems','COMEBACK WATCH',pool.improving.toLocaleString()+' Workers Improving','These workers crossed 80% reliability — they earned a second look');
d.gems.forEach(function(w){
addWorkerMini(c3,w.name,w.role+' · '+w.city+', '+w.state,'Reliability: '+Math.round(w.rel*100)+'% rising','#79c0ff','improving');
});
c3.appendChild(makeCallout('These workers were previously below threshold. The system detected their improvement automatically — no staffer had to track it.','#0d261a','#238636'));
container.appendChild(c3);
}
// Card 4: Risk Watch
if(d.risks&&d.risks.length){
var c4=makeCard('risk','RISK WATCH',pool.erratic.toLocaleString()+' Erratic + '+pool.silent_cnt.toLocaleString()+' Silent Workers','Low reliability + behavioral flags — review placement priority');
d.risks.forEach(function(w){
addWorkerMini(c4,w.name,w.role+' · '+w.city+', '+w.state,'Reliability: '+Math.round(w.rel*100)+'% · Responsiveness: '+Math.round(w.resp*100)+'%','#f85149',w.archetype);
});
c4.appendChild(makeCallout('Not firing — deprioritizing. Every failed placement costs time and client trust. The system flags these automatically so you don\'t learn the hard way.','#2d0d0d','#7f1d1d'));
container.appendChild(c4);
}
// Card 5: Ready & Waiting
if(d.untapped&&d.untapped.length){
var c5=makeCard('talent','READY & WAITING','Available + Reliable — Call These First','85%+ reliability and actively available right now');
d.untapped.forEach(function(w){
addWorkerMini(c5,w.name,w.role+' · '+w.city+', '+w.state,'Available: '+Math.round(w.avail*100)+'% · Reliable: '+Math.round(w.rel*100)+'%','#bc8cff','ready');
});
container.appendChild(c5);
}
// Card 6: Role Supply
if(d.supply&&d.supply.length){
var c6=makeCard('supply','ROLE SUPPLY','Workers by Role','Roles with high supply but low availability may indicate scheduling problems');
d.supply.slice(0,10).forEach(function(r){
var row=div('supply-row');
row.appendChild(el('span','color:#f0f6fc;font-weight:500',r.role));
var nums=el('span','color:#8b949e;font-size:11px');
nums.appendChild(el('strong','color:#f0f6fc',r.supply.toLocaleString()));
nums.appendChild(document.createTextNode(' total · '));
nums.appendChild(el('strong','color:#f0f6fc',r.available.toLocaleString()));
nums.appendChild(document.createTextNode(' available · rel '+Math.round(r.avg_rel*100)+'%'));
row.appendChild(nums);c6.appendChild(row);
});
container.appendChild(c6);
}
setupPrompts(d);
}).catch(function(e){
document.getElementById('brief').textContent='Failed to load brief: '+e.message;
var h=document.getElementById('ch2-permits');h.textContent='';h.appendChild(el('div','err',e.message||String(e)));
});
}
function makeCard(cls,label,headline,sub){
var d=div('bcard '+cls);
d.appendChild(div('label',label));d.appendChild(div('headline',headline));d.appendChild(div('sub',sub));return d;
}
function addBigNum(parent,num,label,color){
var d=el('div','');
var n=el('div','font-size:36px;font-weight:800;line-height:1;color:'+color,num);
d.appendChild(n);d.appendChild(el('div','font-size:10px;color:#484f58;margin-top:2px',label));parent.appendChild(d);
}
function addWorkerMini(parent,name,detail,extra,color,badge){
var w=div('wk');
var av=el('div','width:32px;height:32px;border-radius:8px;display:flex;align-items:center;justify-content:center;font-weight:700;font-size:12px;color:#f0f6fc;flex-shrink:0;background:'+AC[Math.floor(Math.random()*AC.length)]);
av.textContent=(name||'').split(' ').map(function(n){return(n[0]||'').toUpperCase()}).slice(0,2).join('');
var info=div('info');
info.appendChild(div('nm',name));info.appendChild(div('det',detail));
if(extra){var ex=div('det');ex.style.color='#58a6ff';ex.textContent=extra;info.appendChild(ex)}
w.appendChild(av);w.appendChild(info);
if(badge){var b=el('span','padding:2px 8px;border-radius:8px;font-size:10px;font-weight:600;background:'+(color||'#484f58')+'20;color:'+(color||'#484f58'),badge);w.appendChild(b)}
parent.appendChild(w);
}
function makeCallout(text,bg,border){
return el('div','font-size:11px;color:#8b949e;margin-top:8px;padding:8px 10px;background:'+(bg||'#0d1117')+';border:1px solid '+(border||'#1b2130')+';border-radius:6px;font-style:italic',text);
}
function setupPrompts(data){
var container=document.getElementById('prompts');container.textContent='';
var prompts=[];
if(data.gems&&data.gems[0])prompts.push('Find someone like '+data.gems[0].name+' but in OH');
prompts.push('Who could handle industrial electrical work?');
prompts.push('What if we lose our top 5 forklift operators?');
prompts.push('Which workers should we stop placing?');
if(data.supply&&data.supply.length>2){
var under=data.supply.slice().sort(function(a,b){return (a.available/a.supply)-(b.available/b.supply)})[0];
if(under)prompts.push('Show me available '+under.role+'s');
}
prompts.push('Find me a warehouse worker available today near Nashville');
prompts.push('Find bilingual workers with leadership skills');
prompts.forEach(function(p){
var btn=el('button','',p);btn.className='prompt-btn';
btn.onclick=function(){document.getElementById('q').value=p;send()};
container.appendChild(btn);
// ─── 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)));
});
}
// ─── Chat ───
function send(){
var input=document.getElementById('q');
var q=input.value.trim();if(!q)return;
input.value='';
var chat=document.getElementById('chat');
var userMsg=div('msg user');var bubble=div('bubble',q);userMsg.appendChild(bubble);chat.appendChild(userMsg);
var thinking=el('div','display:flex;align-items:center;gap:8px;color:#58a6ff;font-size:12px;padding:12px');
thinking.appendChild(document.createTextNode('Querying 500K profiles'));
var dots=el('span','');dots.className='dots';
dots.appendChild(el('span','','.'));dots.appendChild(el('span','','.'));dots.appendChild(el('span','','.'));
thinking.appendChild(dots);chat.appendChild(thinking);chat.scrollTop=chat.scrollHeight;
document.getElementById('send-btn').disabled=true;
// ─── 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){
thinking.remove();document.getElementById('send-btn').disabled=false;
var msg=div('msg system');var bbl=div('bubble');
// Steps
if(d.queries_run&&d.queries_run.length){
var steps=div('steps');
d.queries_run.forEach(function(s){steps.appendChild(el('div','font-size:11px;color:#58a6ff;padding:2px 0','→ '+s))});
bbl.appendChild(steps);
}
bbl.appendChild(div('summary',d.summary||''));
// Type-specific rendering
if(d.type==='similar'&&d.source){
var sc=div('source-card');
sc.appendChild(el('div','font-size:10px;color:#d29922;text-transform:uppercase;letter-spacing:1px;margin-bottom:4px','Source Worker'));
sc.appendChild(el('div','font-weight:700;color:#f0f6fc;font-size:14px',d.source.name));
sc.appendChild(el('div','font-size:12px;color:#d29922',d.source.role+' · '+d.source.city+', '+d.source.state+' · Reliability: '+Math.round(d.source.rel*100)+'%'));
sc.appendChild(el('div','font-size:11px;color:#8b949e;margin-top:4px','Skills: '+(d.source.skills||'none')+' · Archetype: '+(d.source.archetype||'unknown')));
bbl.appendChild(sc);
renderResults(bbl,d.results);
if(d.sql_matches)bbl.appendChild(el('div','font-size:10px;color:#484f58;margin-top:6px','Filtered pool: '+d.sql_matches.toLocaleString()+' workers'));
}
else if(d.type==='discovery'){
renderResults(bbl,d.results);
if(d.note)bbl.appendChild(el('div','font-size:11px;color:#8b949e;font-style:italic;margin-top:8px;padding:8px;background:#0d1117;border-radius:6px',d.note));
}
else if(d.type==='whatif'){
var sevColors={HIGH:['#7f1d1d','#fca5a5'],MEDIUM:['#854d0e','#fcd34d'],LOW:['#238636','#86efac']};
var sc2=sevColors[d.risk_level]||sevColors.MEDIUM;
var sev=el('span','display:inline-block;padding:3px 10px;border-radius:8px;font-size:11px;font-weight:700;margin-left:8px;background:'+sc2[0]+';color:'+sc2[1],'RISK: '+d.risk_level);
bbl.querySelector('.summary').appendChild(sev);
bbl.appendChild(el('div','font-size:11px;color:#f85149;margin:8px 0 4px;font-weight:600','Workers you\'d lose:'));
(d.lost||[]).forEach(function(w){
var rc=div('result-card');rc.style.borderLeft='3px solid #f85149';
rc.appendChild(el('div','font-weight:600;color:#f0f6fc;font-size:13px',w.name));
rc.appendChild(el('div','font-size:11px;color:#8b949e;margin-top:2px',w.role+' · '+w.city+', '+w.state+' · Reliability: '+Math.round(w.rel*100)+'%'));
bbl.appendChild(rc);
});
bbl.appendChild(el('div','font-size:11px;color:#3fb950;margin:12px 0 4px;font-weight:600','Remaining bench: '+d.reliable_remaining+' reliable workers across '+(d.bench||[]).length+' states ('+d.total_in_role+' total in role)'));
(d.bench||[]).forEach(function(b){
var row=el('div','display:flex;justify-content:space-between;padding:4px 8px;font-size:12px');
row.appendChild(el('span','color:#f0f6fc',b.state+' — '+b.total+' total'));
row.appendChild(el('span','color:'+(b.reliable>5?'#3fb950':'#f85149'),b.reliable+' reliable'));
bbl.appendChild(row);
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);
}
else if(d.type==='risk'){
(d.results||[]).forEach(function(w){
var rc=div('result-card');rc.style.borderLeft='3px solid #f85149';
rc.appendChild(el('div','font-weight:600;color:#f0f6fc;font-size:13px',w.name));
rc.appendChild(el('div','font-size:11px;color:#8b949e;margin-top:2px',w.role+' · '+w.city+', '+w.state));
rc.appendChild(el('div','font-size:10px;color:#f85149;margin-top:4px','Reliability: '+Math.round(w.rel*100)+'% · Responsiveness: '+Math.round(w.resp*100)+'% · Compliance: '+Math.round(w.compl*100)+'% · '+w.archetype));
bbl.appendChild(rc);
});
if(d.total_flagged)bbl.appendChild(el('div','font-size:10px;color:#484f58;margin-top:6px',d.total_flagged.toLocaleString()+' total workers match this risk profile'));
}
else if(d.type==='smart_search'){
// Show what the system understood
if(d.understood&&d.understood.length){
var tags=el('div','display:flex;gap:6px;flex-wrap:wrap;margin-bottom:10px');
d.understood.forEach(function(u){
tags.appendChild(el('span','padding:3px 10px;border-radius:10px;font-size:11px;background:#1a274420;color:#58a6ff;border:1px solid #1a274480',u));
});
bbl.appendChild(tags);
}
// Show SQL results with zip codes, availability, skills
if(d.sql_results&&d.sql_results.length){
d.sql_results.forEach(function(w){
var rc=div('result-card');
rc.appendChild(el('div','font-weight:600;color:#f0f6fc;font-size:14px',w.name));
var locParts=[w.role,w.city+', '+w.state];
if(w.zip)locParts.push('ZIP: '+w.zip);
rc.appendChild(el('div','font-size:12px;color:#8b949e;margin-top:2px',locParts.join(' · ')));
// Metrics row
var metrics=[];
if(w.avail!==undefined)metrics.push('Available: '+Math.round(w.avail*100)+'%');
if(w.rel!==undefined)metrics.push('Reliable: '+Math.round(w.rel*100)+'%');
if(w.archetype)metrics.push(w.archetype);
if(metrics.length)rc.appendChild(el('div','font-size:11px;color:#58a6ff;margin-top:4px',metrics.join(' · ')));
// Skills + certs
if(w.skills||w.certifications){
var tagRow=el('div','display:flex;gap:4px;flex-wrap:wrap;margin-top:6px');
if(w.skills)(w.skills||'').split(',').forEach(function(s){s=s.trim();if(s)tagRow.appendChild(el('span','padding:2px 8px;background:#1a2744;color:#58a6ff;border-radius:8px;font-size:10px',s))});
if(w.certifications)(w.certifications||'').split(',').forEach(function(c){c=c.trim();if(c&&c!=='none')tagRow.appendChild(el('span','padding:2px 8px;background:#1a3a2a;color:#3fb950;border-radius:8px;font-size:10px',c))});
rc.appendChild(tagRow);
}
bbl.appendChild(rc);
});
}
// Also show vector results if different
if(d.vector_results&&d.vector_results.length&&(!d.sql_results||!d.sql_results.length)){
renderResults(bbl,d.vector_results);
}
}
else if(d.type==='search'||d.type==='answer'){
renderResults(bbl,d.results||d.sources||[]);
}
else if(d.type==='error'){
bbl.appendChild(el('div','color:#f85149;font-size:13px',d.summary));
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);
}
if(d.duration_ms){
var meta=div('meta');
meta.appendChild(el('span','margin-right:12px',d.duration_ms+'ms'));
if(d.sql_matches)meta.appendChild(el('span','',d.sql_matches.toLocaleString()+' records scanned'));
bbl.appendChild(meta);
}
msg.appendChild(bbl);chat.appendChild(msg);chat.scrollTop=chat.scrollHeight;
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){
thinking.remove();document.getElementById('send-btn').disabled=false;
var msg=div('msg system');var bbl=div('bubble');bbl.style.borderColor='#f85149';
bbl.appendChild(el('div','color:#f85149','Error: '+e.message));
msg.appendChild(bbl);chat.appendChild(msg);
btn.disabled=false;btn.textContent='Ask';
out.textContent='';out.appendChild(el('div','err',e.message||String(e)));
});
}
function renderResults(parent,results){
if(!results||!results.length)return;
results.forEach(function(r){
var text=r.text||r.chunk_text||'';
var parts=text.split(/\u2014|\u2013|\u002D{2,}/);
var name=parts[0]?parts[0].trim():(r.name||r.doc_id||'Worker');
var rest=parts[1]?parts[1].trim():'';
var roleM=rest.match(/^(.+?)\s+in\s+(.+?)\./);
var skillM=rest.match(/Skills:\s*([^.]+)/);
var certM=rest.match(/Cert(?:s|ifications)?:\s*([^.]+)/);
var relM=rest.match(/Reliability:\s*([\d.]+)/);
var archM=rest.match(/Archetype:\s*(\w+)/);
var rc=div('result-card');
rc.appendChild(el('div','font-weight:600;color:#f0f6fc;font-size:13px',name));
if(roleM)rc.appendChild(el('div','font-size:11px;color:#8b949e;margin-top:2px',roleM[1]+' · '+roleM[2]));
var scoreParts=[];
if(r.score)scoreParts.push('Match: '+Math.round(r.score*100)+'%');
if(relM)scoreParts.push('Reliability: '+Math.round(relM[1]*100)+'%');
if(archM)scoreParts.push(archM[1]);
if(scoreParts.length)rc.appendChild(el('div','font-size:10px;color:#58a6ff;margin-top:4px',scoreParts.join(' · ')));
if(skillM||certM){
var tags=el('div','display:flex;gap:4px;flex-wrap:wrap;margin-top:6px');
if(skillM)skillM[1].split(/[|,]/).forEach(function(s){if(s.trim()){tags.appendChild(el('span','padding:2px 8px;background:#1a2744;color:#58a6ff;border-radius:8px;font-size:10px',s.trim()))}});
if(certM)certM[1].split(/[|,]/).forEach(function(c){if(c.trim()&&c.trim()!=='none'){tags.appendChild(el('span','padding:2px 8px;background:#1a3a2a;color:#3fb950;border-radius:8px;font-size:10px',c.trim()))}});
rc.appendChild(tags);
}
parent.appendChild(rc);
});
}
</script></body></html>
</script>
</body></html>