lakehouse/mcp-server/console.html
root 05f2e42c45 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.
2026-04-20 17:35:45 -05:00

467 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>
</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 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.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>