lakehouse/mcp-server/search.html
root b843a23433 mcp: contractor entity-brief drill-down + mobile UX pass
Adds /contractor page route plus /intelligence/contractor_profile
endpoint that fans out across OSHA, ticker, history, parent_link,
federal contracts, debarment, NLRB, ILSOS, news, diversity certs,
BLS macro — single per-contractor portfolio view across every
wired source.

search.html: mobile responsive layout, fixed bottom dock with
horizontal scroll-snap, legacy bridge row stacking, viewport
overflow guards.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 17:07:23 -05:00

2566 lines
156 KiB
HTML
Raw Permalink 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><head>
<meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
<title>Staffing Co-Pilot</title>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css">
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<style>
*{margin:0;padding:0;box-sizing:border-box}
html,body{overflow-x:hidden;max-width:100vw}
body{font-family:'Inter',-apple-system,system-ui,'Segoe UI',sans-serif;background:#090c10;color:#b0b8c4;font-size:14px;line-height:1.6;-webkit-font-smoothing:antialiased}
/* Top bar */
.bar{background:#0d1117;padding:0 24px;height:56px;border-bottom:1px solid #171d27;display:flex;justify-content:space-between;align-items:center}
.bar h1{font-size:14px;font-weight:600;color:#e6edf3;letter-spacing:-0.2px}
.bar .rt{font-size:11px;color:#545d68}
.bar nav{display:flex;gap:2px}
.bar nav a{font-size:12px;color:#545d68;text-decoration:none;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}
/* Layout */
.content{max-width:940px;margin:0 auto;padding:24px 20px 40px}
.section{margin-bottom:32px}
.section-header{display:flex;justify-content:space-between;align-items:baseline;margin-bottom:14px}
.section-title{font-size:11px;font-weight:600;color:#545d68;text-transform:uppercase;letter-spacing:1.2px}
.section-meta{font-size:10px;color:#3d444d}
/* Cards */
.card{background:#0d1117;border:1px solid #171d27;border-radius:10px;padding:20px;margin-bottom:12px}
.card.accent-red{border-left:3px solid #da3633}
.card.accent-green{border-left:3px solid #2ea043}
.card.accent-amber{border-left:3px solid #bf8700}
.card.accent-blue{border-left:3px solid #388bfd}
.card .card-label{font-size:9px;font-weight:600;color:#545d68;text-transform:uppercase;letter-spacing:1.4px;margin-bottom:6px}
.card .card-title{font-size:17px;font-weight:600;color:#e6edf3;margin-bottom:3px;letter-spacing:-0.3px}
.card .card-sub{font-size:12px;color:#545d68;margin-bottom:14px;line-height:1.5}
/* Keep old class names working */
.insight{background:#0d1117;border:1px solid #171d27;border-radius:10px;padding:20px;margin-bottom:12px}
.insight .label{font-size:9px;font-weight:600;color:#545d68;text-transform:uppercase;letter-spacing:1.4px;margin-bottom:6px}
.insight .headline{font-size:17px;font-weight:600;color:#e6edf3;margin-bottom:3px;letter-spacing:-0.3px}
.insight .sub{font-size:12px;color:#545d68;margin-bottom:14px}
.insight.urgent{border-left:3px solid #da3633}
.insight.opportunity{border-left:3px solid #2ea043}
.insight.warning{border-left:3px solid #bf8700}
.insight.info{border-left:3px solid #388bfd}
/* Workers */
.iworker{display:flex;align-items:center;gap:12px;padding:10px 12px;background:#161b22;border-radius:8px;margin-bottom:4px;transition:background 0.15s}
.iworker:hover{background:#1c2333}
.iworker .av{width:36px;height:36px;border-radius:8px;display:flex;align-items:center;justify-content:center;font-weight:600;font-size:12px;color:#e6edf3;flex-shrink:0}
.iworker .info{flex:1;min-width:0}
.iworker .nm{font-weight:600;color:#e6edf3;font-size:13px}
.iworker .detail{color:#545d68;font-size:11px}
.iworker .why{color:#388bfd;font-size:11px;margin-top:1px}
.iworker .acts{display:flex;gap:4px}
.ibtn{padding:5px 12px;border-radius:6px;font-size:10px;cursor:pointer;border:none;font-weight:600;transition:opacity 0.15s}
.ibtn:hover{opacity:0.8}
.ibtn.call{background:#1a2e4a;color:#58a6ff}
.ibtn.sms{background:#122b1e;color:#3fb950}
/* Stats */
.stats{display:grid;grid-template-columns:repeat(4,1fr);gap:8px;margin-bottom:24px}
.stat{background:#0d1117;border:1px solid #171d27;border-radius:8px;padding:16px 12px;text-align:center}
.stat .n{font-size:26px;font-weight:700;color:#e6edf3;letter-spacing:-1px}
.stat .l{font-size:9px;color:#545d68;text-transform:uppercase;letter-spacing:0.8px;margin-top:4px}
/* Search */
.sa{background:#0d1117;border:1px solid #171d27;border-radius:10px;overflow:hidden}
.sa summary{cursor:pointer;color:#545d68;font-size:12px;list-style:none;padding:14px 20px;transition:color 0.15s}
.sa summary:hover{color:#b0b8c4}
.sa summary::-webkit-details-marker{display:none}
.sa .inner{padding:0 20px 20px}
.sa input[type=text]{width:100%;padding:12px 16px;background:#161b22;border:1px solid #21262d;border-radius:8px;color:#e6edf3;font-size:13px;outline:none;margin-bottom:8px;transition:border 0.15s}
.sa input:focus{border-color:#388bfd}
.srow{display:flex;gap:8px;margin-bottom:10px}
.sa select{flex:1;padding:8px 12px;background:#161b22;border:1px solid #21262d;border-radius:6px;color:#b0b8c4;font-size:12px}
.sbtn{width:100%;padding:10px;background:#1f6feb;border:none;border-radius:8px;color:#fff;font-size:13px;font-weight:600;cursor:pointer;transition:background 0.15s}
.sbtn:hover{background:#388bfd}
#sresults{margin-top:14px}
/* Footer */
.ft{text-align:center;padding:24px;color:#3d444d;font-size:11px;border-top:1px solid #171d27;margin-top:32px}
.ft a{color:#545d68;text-decoration:none;transition:color 0.15s}
.ft a:hover{color:#e6edf3}
.ld{color:#3d444d;text-align:center;padding:40px;font-size:13px}
/* Bottom section-jump nav — mobile only */
/* Desktop: top nav handles navigation; this dock stays hidden. */
/* Mobile: top nav collapses (.bar nav:display:none below); this fixed dock */
/* gives one-thumb access to any section. Uses horizontal scroll-snap so */
/* additional sections never clip regardless of viewport width — staffers */
/* swipe the dock like an iOS tab bar. */
#mobile-dock{display:none}
#mobile-dock a,#mobile-dock button{
flex:0 0 auto;scroll-snap-align:start;
min-width:64px;text-align:center;padding:8px 6px;font-size:15px;font-weight:600;
color:#8b949e;text-decoration:none;border-radius:8px;background:none;
border:none;cursor:pointer;font-family:inherit;line-height:1;transition:all 0.1s
}
#mobile-dock a:active,#mobile-dock button:active{background:#1c2333;color:#e6edf3}
#mobile-dock .dock-label{font-size:9px;color:#8b949e;margin-top:4px;display:block;
text-transform:uppercase;letter-spacing:0.3px;font-weight:500;
white-space:nowrap}
/* Responsive */
@media(max-width:768px){
.stats{grid-template-columns:repeat(2,1fr);gap:6px;margin-bottom:16px}
.stat{padding:12px 8px}
.stat .n{font-size:22px}
.stat .l{font-size:8px}
.iworker{flex-direction:column;text-align:center}
.iworker .acts{justify-content:center}
.bar{padding:0 12px;height:48px;gap:10px}
.bar h1{font-size:13px}
.bar nav{display:none}
.bar .rt{font-size:10px;text-align:right;max-width:55%;line-height:1.3;color:#8b949e}
.content{padding:12px 10px 72px} /* bottom-pad reserved for mobile dock */
.section{margin-bottom:20px}
.card,.insight{padding:14px;margin-bottom:10px}
.card .card-title,.insight .headline{font-size:15px}
/* ─── Bottom section-jump dock: show on mobile only ─── */
/* Horizontal scroll-snap lets the row carry any number of section pills */
/* without clipping — swipe right to reveal more. Snap to pill edges so */
/* it feels solid, not jittery. */
#mobile-dock{
display:flex;position:fixed;bottom:0;left:0;right:0;
background:rgba(13,17,23,0.94);backdrop-filter:blur(10px);
-webkit-backdrop-filter:blur(10px);
border-top:1px solid #21262d;padding:4px 6px;gap:0;z-index:50;
overflow-x:auto;overflow-y:hidden;
scroll-snap-type:x mandatory;-webkit-overflow-scrolling:touch;
padding-bottom:max(4px,env(safe-area-inset-bottom)) /* iOS home-bar */
}
#mobile-dock::-webkit-scrollbar{height:0;display:none}
/* ─── Section ⓪ Legacy bridge: stack rows vertically ─── */
#legacy-bridge-rows > div{
grid-template-columns:1fr !important;gap:6px !important;padding:10px 12px !important
}
#legacy-bridge-rows > div > div:nth-child(2){display:none !important} /* hide → arrow */
#legacy-bridge-rows > div > div:nth-child(3){
border-top:1px dashed #1f2631;padding-top:8px
}
#legacy-bridge-section summary span:last-child{display:none} /* "click to collapse" hint */
#legacy-growth-strip{font-size:10px !important;width:100%;margin-top:4px}
/* ─── Section ① Live Market hero: stack clock above pulse ─── */
#live-market-hero{grid-template-columns:1fr !important;gap:14px !important}
#live-market-hero > div:first-child{align-items:center !important}
/* ─── Section ② contract cards: compact header + grid stacks ─── */
.insight > div[style*="display:flex;justify-content:space-between"]{
flex-direction:column;gap:6px
}
.insight > div[style*="grid-template-columns:repeat(4,1fr)"]{
grid-template-columns:repeat(2,1fr) !important
}
/* Fill probability markers: smaller font, allow wrap */
.insight span[style*="font-variant-numeric:tabular-nums"]{font-size:10px}
/* ─── Section ③ Worker Search: stack dropdowns + button ─── */
#worker-search-section .srow,
#worker-search-section div[style*="display:flex;gap:8px"]{
flex-direction:column !important;gap:8px !important
}
#worker-search-section .sbtn{width:100%}
/* ─── Section ⑤ Substrate Signals: auto-fit already; shrink padding ─── */
#arch-signals > div{padding:10px 12px !important}
/* Paragraph widths */
.section p{max-width:100% !important}
/* Break long monospace attribution strings so they don't overflow */
#legacy-bridge-rows div[style*="monospace"]{word-break:break-word;overflow-wrap:anywhere}
/* Any contract-card pill rows should wrap, not push horizontal */
.insight{overflow:hidden}
/* Footer condenses */
.ft{padding:16px 12px;font-size:10px}
}
@media(max-width:480px){
.stats{grid-template-columns:1fr 1fr}
.stat .n{font-size:20px}
.bar h1{font-size:12px}
.content{padding:10px 8px 72px}
.card,.insight{padding:12px}
/* Section ⓪ rows: even tighter */
#legacy-bridge-rows > div{padding:8px 10px !important}
#legacy-bridge-section{padding:12px 14px !important}
/* Hide the long header-strip info (growth) on very small screens — */
/* the section intro paragraph and per-row attributions cover it. */
/* Keep it accessible by expanding the <details>. */
}
</style></head><body>
<div class="bar">
<h1>Staffing Co-Pilot</h1>
<nav>
<a href="." class="active">Dashboard</a>
<a href="console">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="status">Loading...</div>
</div>
<div class="content">
<!-- ═══ ⓪ Legacy CRM → this system: translate the mental model ═══ -->
<!-- Written for staffers who land here from a legacy CRM (Avionte, Bullhorn, -->
<!-- Tempworks, etc). Their mental model is "every concept must be a visible -->
<!-- field or it doesn't exist." Ours is the opposite: concepts get indexed when -->
<!-- you use them, not when a form declares them. This strip names common -->
<!-- legacy surfaces and says what this system does in their place — with real -->
<!-- attribution numbers so the compounding claim is measurable, not marketed. -->
<!-- Placed FIRST so staffers see the translation before they see a thin UI and -->
<!-- conclude "my old CRM had more checkboxes." -->
<details class="section" id="legacy-bridge-section" open style="background:#0d1117;border:1px solid #171d27;border-radius:10px;padding:16px 20px;margin-bottom:14px">
<summary style="list-style:none;cursor:pointer;display:flex;align-items:baseline;gap:14px;flex-wrap:wrap;outline:none">
<span class="section-title" style="color:#e6edf3;font-size:13px;font-weight:600">⓪ Not a CRM — an index that learns from you</span>
<span id="legacy-growth-strip" style="font-size:11px;color:#8b949e;font-variant-numeric:tabular-nums">loading growth numbers…</span>
<span style="margin-left:auto;font-size:10px;color:#545d68">▾ click to collapse</span>
</summary>
<p style="color:#8b949e;font-size:11px;margin:10px 0 14px;line-height:1.55;max-width:780px">
If you've worked on a legacy staffing CRM, your mental model is <em>field inventory</em>
every concept must be a visible column, dropdown, or checkbox, or it doesn't exist. This
system works the opposite way: concepts don't need to be pre-declared because the
<strong>hybrid index + playbook memory</strong> learns them when you work a contract.
The rows below translate the familiar legacy surface into what actually happens here,
with real numbers for every claim.
</p>
<div id="legacy-bridge-rows"></div>
</details>
<div id="main"><div class="ld">Analyzing contracts and workers...</div></div>
<!-- ═══ 1. LIVE MARKET — Chicago hero (clock + permit pulse together) ═══ -->
<div class="section" id="live-market-section">
<div class="section-header">
<span class="section-title" style="color:#e6edf3;font-size:13px">① Live Market — Chicago right now</span>
<span class="section-meta" id="live-market-meta">Public permit data pulled live · clock aligned to current shift · refreshed every page load</span>
</div>
<p style="color:#8b949e;font-size:11px;margin:0 0 12px;line-height:1.5;max-width:680px">
The clock shows where we sit in the 24-hour cycle. Colored arcs mark the 4 standard
staffing shifts; the red needle is now. The panel beside it summarizes what Chicago's
public permit system is asking for right now — staffing demand before anyone's acted
on it. This is the <em>real world</em> the rest of the page is reacting to.
</p>
<div id="live-market-hero" style="display:grid;grid-template-columns:240px 1fr;gap:20px;align-items:start"><div class="ld" style="grid-column:1/-1">Loading live market…</div></div>
</div>
<div class="section" id="staffing-forecast-section">
<div class="section-header">
<span class="section-title">Staffing Forecast — Next 30 Days</span>
<span class="section-meta">Permits → predicted demand · Bench supply · Days to staffing deadline</span>
</div>
<div id="staffing-forecast"><div class="ld">Loading forecast...</div></div>
</div>
<!-- ═══ 2. STAFFER'S CONSOLE — what a coordinator acts on ═══ -->
<div class="section" id="live-contracts-section">
<div class="section-header">
<span class="section-title" style="color:#e6edf3;font-size:13px">② Staffer's Console — what's on your plate</span>
<span class="section-meta" id="live-contracts-meta">Real public permit data + worker bench + past playbook patterns</span>
</div>
<p style="color:#8b949e;font-size:11px;margin:0 0 12px;line-height:1.5;max-width:680px">
This is what a recruiter or coordinator sees when they open the console. Each card is
one open permit ranked against our 500K worker bench. The <strong>fill-probability bar</strong>
shows cumulative chance of filling by day; the <strong>economics panel</strong> projects
gross revenue, margin, and payout window; the <strong>over-bill pool</strong> flags workers
whose pay exceeds the contract's bill rate — they go into a margin-watch bucket instead
of being rejected outright.
</p>
<div id="live-contracts"><div class="ld">Loading live contracts...</div></div>
</div>
<div class="section" id="market-section">
<div class="section-header">
<span class="section-title">Market Intelligence</span>
<span class="section-meta">Public permit data · Updated live</span>
</div>
<div id="market"></div>
</div>
<!-- ═══ ③ Worker Search — natural-language lookup over the 500K bench ═══ -->
<div class="section" id="worker-search-section">
<div class="section-header">
<span class="section-title" style="color:#e6edf3;font-size:13px">③ Worker Search — find someone specific</span>
<span class="section-meta" id="worker-search-meta">Natural language search</span>
</div>
<p style="color:#8b949e;font-size:11px;margin:0 0 10px;line-height:1.5;max-width:680px">
Type a plain-English description — role, location, trait, certification.
The query hits the hybrid SQL + vector index over all 500K worker profiles
and ranks by semantic match, reliability, and availability. Try one of the
sample searches below or write your own.
</p>
<div id="ws-samples" style="display:flex;flex-wrap:wrap;gap:6px;margin-bottom:10px;font-size:11px"></div>
<input type="text" id="sq" placeholder="Try: reliable forklift operator available in Nashville" onkeydown="if(event.key==='Enter')doSearch()" style="width:100%;box-sizing:border-box;padding:10px 12px;background:#0d1117;border:1px solid #30363d;border-radius:6px;color:#e6edf3;font-size:13px;margin-bottom:8px">
<div class="srow" style="display:flex;gap:8px;margin-bottom:8px">
<select id="sst" style="flex:1;padding:8px;background:#0d1117;border:1px solid #30363d;border-radius:6px;color:#e6edf3"><option value="">Any State</option></select>
<select id="srl" style="flex:1;padding:8px;background:#0d1117;border:1px solid #30363d;border-radius:6px;color:#e6edf3"><option value="">Any Role</option></select>
<button class="sbtn" onclick="doSearch()" style="padding:8px 16px;background:#238636;border:none;border-radius:6px;color:#fff;font-weight:600;cursor:pointer">Find Workers</button>
</div>
<div id="sresults"></div>
</div>
<!-- ═══ ④ System Activity — how the substrate is learning ═══ -->
<div class="section" id="learning-section">
<div class="section-header">
<span class="section-title" style="color:#e6edf3;font-size:13px">④ System Activity — how the substrate learns</span>
<span class="section-meta">Playbook memory, pathway traces, self-tuning indices</span>
</div>
<p style="color:#8b949e;font-size:11px;margin:0 0 12px;line-height:1.5;max-width:680px">
Every completed fill, every accepted playbook, every rejected candidate feeds
back into the substrate. This strip shows what the system has learned since
the last run — which patterns are compounding, which memories are fresh,
which indices are being exercised. If it's empty, the system hasn't seen
enough traffic yet to form a memory worth showing.
</p>
<div id="learning"></div>
</div>
<!-- ═══ ⑤ Substrate signals — compact architecture-health strip (bottom) ═══ -->
<div class="section" id="arch-signals-section">
<div class="section-header">
<span class="section-title" style="color:#e6edf3;font-size:13px">⑤ Substrate Signals — architecture health</span>
<span class="section-meta">Live probes of the index, memory, and pathway layers</span>
</div>
<p style="color:#8b949e;font-size:11px;margin:0 0 10px;line-height:1.5;max-width:680px">
These tiles measure the architecture itself, not the staffing workload.
Instant-search latency, index shape, playbook-memory depth, pathway-matrix
compounding — four probes that answer "is the substrate healthy right now?"
</p>
<div id="arch-signals" style="display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:10px">
<div style="color:#8b949e;font-size:11px;grid-column:1/-1">Probing substrate…</div>
</div>
</div>
<div class="ft" id="footer">Staffing Co-Pilot · Hybrid SQL + Vector Search · loading scale… · <a href="console">Console</a> · <a href="proof">Architecture</a></div>
</div>
<!-- ═══ Mobile section-jump dock — visible < 768px only via CSS ═══ -->
<!-- One-thumb navigation for staffers scrolling through the long dashboard. -->
<!-- Each pill jumps to a section by ID; the top bar nav is hidden on mobile, -->
<!-- so this replaces it entirely. Icons stay terse (⓪①②③④⑤) and the label -->
<!-- below reminds what each section is. -->
<nav id="mobile-dock" aria-label="Jump to section">
<a href="#legacy-bridge-section" title="Why this isn't your legacy CRM"><span class="dock-label">Intro</span></a>
<a href="#live-market-section" title="Chicago clock + permit pulse"><span class="dock-label">Market</span></a>
<a href="#live-contracts-section" title="Open contracts + candidates"><span class="dock-label">Jobs</span></a>
<a href="#worker-search-section" title="Search 500K workers"><span class="dock-label">Search</span></a>
<a href="#learning-section" title="What the substrate has learned"><span class="dock-label">Learn</span></a>
<a href="#arch-signals-section" title="Live architecture health"><span class="dock-label">Stack</span></a>
</nav>
<script>
var P=location.pathname.indexOf('/lakehouse')>=0?'/lakehouse':'';
var A=location.origin+P;
var AC=['#1a2744','#1a3a2a','#2a1a3a','#3a2a1a','#1a3a3a','#2a2a1a'];
var lastQuery='';
window.addEventListener('load',function(){loadSystemSummary();loadLegacyBridge();loadDay();loadStaffingForecast();loadLiveContracts();loadMarket();loadLearning();loadWorkerSearchSamples();loadArchSignals()});
// Deep-link: visiting the dashboard with #open-briefs in the URL auto-
// expands every Entity Brief panel once the contract cards finish
// loading. Useful for headless snapshots + demo walkthroughs.
window.addEventListener('load',function(){
if(location.hash!=='#open-briefs') return;
var tries=0;
var t=setInterval(function(){
tries++;
var briefs=document.querySelectorAll('#live-contracts details');
if(briefs.length===0 && tries<40) return;
briefs.forEach(function(d){d.open=true;d.dispatchEvent(new Event('toggle'))});
clearInterval(t);
},250);
});
// ─── Legacy CRM → this system translator ─────────────────────────
// Staffers from legacy systems operate by field-inventory mental model:
// if a concept isn't pre-rendered as a dropdown/checkbox, they conclude
// the system can't do it. This function fills the ⓪ Section with
// side-by-side rows mapping common legacy surfaces to how the hybrid
// substrate handles the same concept — using LIVE attribution numbers
// so the compounding claim is measurable, not marketed. One row is an
// honest "we don't ship this yet" gap, because staffers trust a system
// that admits gaps faster than one that claims everything.
function loadLegacyBridge(){
var host=document.getElementById('legacy-bridge-rows');
if(!host) return;
var strip=document.getElementById('legacy-growth-strip');
Promise.all([
api('/intelligence/arch_signals',{}).catch(function(){return {}}),
api('/system/summary',{}).catch(function(){return {}})
]).then(function(results){
var s=results[0]||{}, sum=results[1]||{};
var pbm=s.playbook_memory||{}, pwm=s.pathway_memory||{}, idx=s.index||{};
var workers=(sum.workers_500k_rows||0).toLocaleString();
var chunks=(idx.chunk_count||0).toLocaleString();
var pbEntries=pbm.entries||0;
var pwTraces=pwm.total_pathways||0;
if(strip){
var parts=[workers+' profiles indexed'];
if(pbEntries) parts.push(pbEntries+' playbook'+(pbEntries===1?'':'s')+' from past fills');
if(pwTraces) parts.push(pwTraces+' pathway trace'+(pwTraces===1?'':'s'));
parts.push('grows per contract');
strip.textContent=parts.join(' · ');
}
var rows=[
{ legacy:'Certification checkboxes — tick every cert you need',
legacyWhy:'If a worker\'s cert isn\'t on the pre-declared list, you can\'t find them.',
here:'Type it in plain English — "forklift with OSHA 10"',
hereWhy:'Vector search scans free-text cert fields across every profile. No list to maintain.',
attribution:chunks+' vector chunks · nomic-embed-text 768d · built once at ingest',
anchor:'③ Worker Search' },
{ legacy:'Preferred-worker list you tag by hand',
legacyWhy:'Forget to tag someone → they stop rising to the top.',
here:'Workers with past fills get the green "Endorsed" chip automatically',
hereWhy:'Playbook memory boosts them on every future search in the same pattern. You never tag manually.',
attribution:pbEntries+' playbook'+(pbEntries===1?'':'s')+' active right now · grows with every Call you log',
anchor:'② Staffer\'s Console' },
{ legacy:'Shift dropdown: 1st / 2nd / 3rd / 4th',
legacyWhy:'Contracts with messy shift language fall through the cracks.',
here:'Shifts inferred from permit description text',
hereWhy:'The 24/7 clock above shows live distribution across shifts, pulled from real Chicago permits.',
attribution:'Permit parser runs on every live feed poll · zero config',
anchor:'① Live Market' },
{ legacy:'Blacklist checkbox — permanent, system-wide',
legacyWhy:'One bad day follows a worker forever, across every job type.',
here:'Tap "No-show" on any candidate card',
hereWhy:'Score drops 0.5× in that geo only. Soft, geo-scoped, reversible — not a ban.',
attribution:pwTraces+' pathway trace'+(pwTraces===1?'':'s')+' in the failure matrix · ADR-021 compounding',
anchor:'② Staffer\'s Console' },
{ legacy:'Side-by-side compare view',
legacyWhy:'Useful for interview prep. We\'re not pretending you don\'t need it.',
here:'Not shipped yet — honest gap',
hereWhy:'Workaround: every candidate card already carries rate, reliability, boost source, and cert summary.',
attribution:'On the roadmap · flagged so you know what\'s missing',
gap:true }
];
host.textContent='';
rows.forEach(function(r){
var row=document.createElement('div');
row.style.cssText='display:grid;grid-template-columns:1fr 22px 1.15fr;gap:12px;padding:11px 14px;background:#161b22;border:1px solid #1f2631;border-radius:8px;margin-bottom:6px;align-items:start';
// LEGACY column
var L=document.createElement('div');
var lTag=document.createElement('div');lTag.style.cssText='font-size:9px;color:#545d68;text-transform:uppercase;letter-spacing:1px;margin-bottom:3px';lTag.textContent='LEGACY CRM EXPECTS';
var lTxt=document.createElement('div');lTxt.style.cssText='color:#8b949e;font-size:12px;line-height:1.4;font-weight:500';lTxt.textContent=r.legacy;
var lWhy=document.createElement('div');lWhy.style.cssText='color:#545d68;font-size:10px;margin-top:3px;line-height:1.45';lWhy.textContent=r.legacyWhy;
L.appendChild(lTag);L.appendChild(lTxt);L.appendChild(lWhy);
// ARROW
var A=document.createElement('div');A.style.cssText='color:#388bfd;font-size:18px;text-align:center;padding-top:14px;font-weight:300';A.textContent='→';
// HERE column
var H=document.createElement('div');
var hTag=document.createElement('div');
hTag.style.cssText='font-size:9px;color:'+(r.gap?'#d29922':'#58a6ff')+';text-transform:uppercase;letter-spacing:1px;margin-bottom:3px';
hTag.textContent=r.gap?'HERE — HONEST GAP':'HERE';
var hTxt=document.createElement('div');
hTxt.style.cssText='color:'+(r.gap?'#d29922':'#e6edf3')+';font-size:12px;line-height:1.4;font-weight:600';
hTxt.textContent=r.here;
var hWhy=document.createElement('div');hWhy.style.cssText='color:#8b949e;font-size:11px;margin-top:3px;line-height:1.45';hWhy.textContent=r.hereWhy;
var attr=document.createElement('div');
attr.style.cssText='color:#545d68;font-size:10px;margin-top:5px;font-family:ui-monospace,SFMono-Regular,monospace';
attr.textContent='▸ '+r.attribution;
H.appendChild(hTag);H.appendChild(hTxt);H.appendChild(hWhy);H.appendChild(attr);
if(r.anchor){
var ptr=document.createElement('div');
ptr.style.cssText='color:#388bfd;font-size:10px;margin-top:3px';
ptr.textContent='see '+r.anchor+' below';
H.appendChild(ptr);
}
row.appendChild(L);row.appendChild(A);row.appendChild(H);
host.appendChild(row);
});
});
}
// ─── Worker Search sample chips ──────────────────────────────────
// Real-looking staffing queries grouped by intent. Clicking one drops
// the text into #sq and runs the search — lowers the floor for
// someone seeing the UI for the first time.
function loadWorkerSearchSamples(){
var el=document.getElementById('ws-samples');
if(!el) return;
var samples=[
{tag:'Role + geo', q:'reliable welder in Ohio with 3+ years'},
{tag:'Cert-gated', q:'forklift operator with OSHA 10 available in Chicago'},
{tag:'Urgent cover', q:'production worker for 3rd shift in Toledo tonight'},
{tag:'Trait-first', q:'punctual machine operator who can lead a line'},
{tag:'Specialist', q:'maintenance tech comfortable with Allen-Bradley PLCs'},
{tag:'High-volume', q:'warehouse associates available next week near Indianapolis'}
];
samples.forEach(function(s){
var chip=document.createElement('button');
chip.type='button';
chip.style.cssText='padding:5px 10px;border-radius:9px;background:#0d1117;border:1px solid #30363d;color:#8b949e;font-size:11px;cursor:pointer;display:inline-flex;align-items:center;gap:6px;transition:all 0.1s';
chip.onmouseover=function(){chip.style.borderColor='#58a6ff';chip.style.color='#e6edf3'};
chip.onmouseout=function(){chip.style.borderColor='#30363d';chip.style.color='#8b949e'};
var tag=document.createElement('span');tag.style.cssText='color:#545d68;font-size:9px;text-transform:uppercase;letter-spacing:1px';tag.textContent=s.tag;
var txt=document.createElement('span');txt.textContent=s.q;
chip.appendChild(tag);chip.appendChild(txt);
chip.onclick=function(){
var input=document.getElementById('sq');
if(input){input.value=s.q;doSearch();input.scrollIntoView({behavior:'smooth',block:'center'});}
};
el.appendChild(chip);
});
}
function loadSystemSummary(){
api('/system/summary',{}).then(function(s){
if(!s) return;
var totalRows=(s.total_rows||0).toLocaleString();
var workers=(s.workers_500k_rows||0).toLocaleString();
var chunks=(s.total_chunks||0).toLocaleString();
var ds=s.datasets||0;
var meta1=document.getElementById('live-contracts-meta');
if(meta1) meta1.textContent='Real public permit data + '+workers+' worker bench + past playbook patterns';
var meta2=document.getElementById('worker-search-meta');
if(meta2) meta2.textContent='Natural language · '+workers+' profiles across '+ds+' datasets';
var foot=document.getElementById('footer');
if(foot){
foot.textContent='';
foot.appendChild(document.createTextNode('Staffing Co-Pilot · Hybrid SQL + Vector Search · '+totalRows+' rows across '+ds+' datasets · '+chunks+' vector chunks · '));
var a1=document.createElement('a');a1.href='console';a1.textContent='Console';foot.appendChild(a1);
foot.appendChild(document.createTextNode(' · '));
var a2=document.createElement('a');a2.href='proof';a2.textContent='Architecture';foot.appendChild(a2);
}
// Also update the collapsible search box label if not yet populated
var sum=document.querySelector('.sa summary');
if(sum&&/Search all\s*\d*\s*workers/.test(sum.textContent)){
sum.textContent='Search all '+workers+' workers';
}
}).catch(function(){/* non-fatal */});
}
// ─── Substrate signals: render the 4 architecture-health tiles ───
function loadArchSignals(){
var el=document.getElementById('arch-signals');
api('/intelligence/arch_signals',{}).then(function(s){
el.textContent='';
function tile(label, big, sub, accent){
var t=document.createElement('div');
t.style.cssText='background:#0d1117;border:1px solid #171d27;border-radius:10px;padding:14px 16px;border-left:3px solid '+(accent||'#58a6ff');
var l=document.createElement('div');l.style.cssText='font-size:10px;text-transform:uppercase;letter-spacing:1px;color:#545d68;margin-bottom:6px';l.textContent=label;
var b=document.createElement('div');b.style.cssText='font-size:20px;font-weight:600;color:#e6edf3;line-height:1.1';b.textContent=big;
var u=document.createElement('div');u.style.cssText='font-size:10px;color:#8b949e;margin-top:6px;line-height:1.4';u.textContent=sub;
t.appendChild(l);t.appendChild(b);t.appendChild(u);
return t;
}
var idx=s.index||{};
var pbm=s.playbook_memory||{};
var pwm=s.pathway_memory||{};
// Tile 1 — instant search (the "we cleverly indexed at ingest" claim)
var latencyColor=s.instant_search_probe_ms<100?'#2ea043':s.instant_search_probe_ms<500?'#d29922':'#f85149';
el.appendChild(tile(
'Instant Search',
(s.instant_search_probe_ms||'?')+'ms',
'Live /vectors/hybrid probe · 500K-row index · '+(idx.chunk_count||0).toLocaleString()+' chunks',
latencyColor
));
// Tile 2 — index shape (hot-swap claim)
el.appendChild(tile(
'Index',
(idx.dimensions||768)+'d · '+(idx.model||'?'),
(idx.source||'?')+' → '+(idx.name||'?')+' · '+(idx.backend||'parquet'),
'#58a6ff'
));
// Tile 3 — self-regulating memory
el.appendChild(tile(
'Playbook Memory',
(pbm.entries||0).toLocaleString()+' entries',
pbm.entries>0?'Meta-index active · boosts candidates from past fills':'Empty · POST /vectors/playbook_memory/rebuild to populate',
pbm.entries>0?'#2ea043':'#d29922'
));
// Tile 4 — ADR-021 pathway compounding
el.appendChild(tile(
'Pathway Matrix',
(pwm.total_pathways||0)+' traces',
pwm.retired+' retired · '+pwm.total_replays+' replays · ADR-021 compounding',
'#58a6ff'
));
}).catch(function(e){
el.textContent='substrate signals unavailable: '+e.message;
});
}
// ─── Live Market hero: 24/7 shift clock (left) + Chicago permit pulse (right) ──
// Combines two previously-separate tiles into one coherent "what's happening in
// Chicago right now" surface. The clock anchors you in the 24-hour cycle; the
// pulse panel summarizes what the raw permit feed is asking for before anyone's
// acted on it.
function loadLiveMarket(contracts){
var root=document.getElementById('live-market-hero');
if(!root) return;
if(!contracts||!contracts.length){
root.textContent='No live permits on the board right now.';
return;
}
root.textContent='';
var SHIFT_COLORS={'1st':'#f9d171','2nd':'#f5894a','3rd':'#5f5fff','4th':'#2ea043'};
var now=new Date();
var hr=now.getHours()+now.getMinutes()/60;
var isWeekend=now.getDay()===0||now.getDay()===6;
function currentShift(){
if(isWeekend) return '4th';
if(hr>=6&&hr<14) return '1st';
if(hr>=14&&hr<22) return '2nd';
return '3rd';
}
var cs=currentShift();
// ── LEFT COLUMN: SVG 24/7 dial ──
var leftCol=document.createElement('div');
leftCol.style.cssText='display:flex;flex-direction:column;align-items:center;gap:8px';
var R=90, CX=100, CY=100;
var svgNS='http://www.w3.org/2000/svg';
var svg=document.createElementNS(svgNS,'svg');
svg.setAttribute('viewBox','0 0 200 200');svg.setAttribute('width','200');svg.setAttribute('height','200');
svg.style.cssText='display:block';
var bg=document.createElementNS(svgNS,'circle');
bg.setAttribute('cx',CX);bg.setAttribute('cy',CY);bg.setAttribute('r',R);
bg.setAttribute('fill','#0d1117');bg.setAttribute('stroke','#30363d');bg.setAttribute('stroke-width','1');
svg.appendChild(bg);
function arcPath(startHr,endHr){
function pt(h){
var ang=((h/24)*2*Math.PI)-Math.PI/2;
return [CX+R*Math.cos(ang), CY+R*Math.sin(ang)];
}
var p0=pt(startHr), p1=pt(endHr);
var largeArc=(endHr-startHr+24)%24>12?1:0;
return 'M '+p0[0]+' '+p0[1]+' A '+R+' '+R+' 0 '+largeArc+' 1 '+p1[0]+' '+p1[1];
}
var SHIFT_ARCS={'1st':[6,14],'2nd':[14,22],'3rd':null};
['1st','2nd','3rd'].forEach(function(shift){
var path=document.createElementNS(svgNS,'path');
if(shift==='3rd'){
path.setAttribute('d',arcPath(22,24)+' '+arcPath(0,6));
} else {
var hrs=SHIFT_ARCS[shift];
path.setAttribute('d',arcPath(hrs[0],hrs[1]));
}
path.setAttribute('fill','none');
path.setAttribute('stroke',SHIFT_COLORS[shift]);
path.setAttribute('stroke-width','10');
path.setAttribute('stroke-linecap','butt');
svg.appendChild(path);
});
[0,6,12,18].forEach(function(h){
var ang=((h/24)*2*Math.PI)-Math.PI/2;
var x1=CX+(R-7)*Math.cos(ang), y1=CY+(R-7)*Math.sin(ang);
var x2=CX+(R+2)*Math.cos(ang), y2=CY+(R+2)*Math.sin(ang);
var ln=document.createElementNS(svgNS,'line');
ln.setAttribute('x1',x1);ln.setAttribute('y1',y1);ln.setAttribute('x2',x2);ln.setAttribute('y2',y2);
ln.setAttribute('stroke','#8b949e');ln.setAttribute('stroke-width','1');
svg.appendChild(ln);
var xl=CX+(R-22)*Math.cos(ang), yl=CY+(R-22)*Math.sin(ang);
var tx=document.createElementNS(svgNS,'text');
tx.setAttribute('x',xl);tx.setAttribute('y',yl+3);
tx.setAttribute('text-anchor','middle');tx.setAttribute('fill','#8b949e');
tx.setAttribute('font-size','9');tx.setAttribute('font-family','monospace');
tx.textContent=String(h).padStart(2,'0');
svg.appendChild(tx);
});
var ang=((hr/24)*2*Math.PI)-Math.PI/2;
var nx=CX+(R-3)*Math.cos(ang), ny=CY+(R-3)*Math.sin(ang);
var needle=document.createElementNS(svgNS,'line');
needle.setAttribute('x1',CX);needle.setAttribute('y1',CY);
needle.setAttribute('x2',nx);needle.setAttribute('y2',ny);
needle.setAttribute('stroke','#f85149');needle.setAttribute('stroke-width','2');
svg.appendChild(needle);
var dot=document.createElementNS(svgNS,'circle');
dot.setAttribute('cx',CX);dot.setAttribute('cy',CY);dot.setAttribute('r','3');
dot.setAttribute('fill','#f85149');
svg.appendChild(dot);
var label=document.createElementNS(svgNS,'text');
label.setAttribute('x',CX);label.setAttribute('y',CY+30);
label.setAttribute('text-anchor','middle');label.setAttribute('fill',SHIFT_COLORS[cs]);
label.setAttribute('font-size','11');label.setAttribute('font-weight','600');
label.textContent=cs+' shift · '+now.toTimeString().slice(0,5);
svg.appendChild(label);
leftCol.appendChild(svg);
var clockCaption=document.createElement('div');
clockCaption.style.cssText='font-size:10px;color:#545d68;text-align:center;line-height:1.4;max-width:200px';
clockCaption.textContent='Arcs = 4 standard shifts. Red needle = now. The '+cs+' shift is live.';
leftCol.appendChild(clockCaption);
root.appendChild(leftCol);
// ── RIGHT COLUMN: Chicago permit pulse ──
var rightCol=document.createElement('div');
rightCol.style.cssText='display:flex;flex-direction:column;gap:10px;min-width:0';
// Aggregate: workers needed, implied hourly bill demand, role mix, urgency mix
var totalWorkers=0, totalBillPerHr=0;
var roleMix={}, urgencyMix={overdue:0,urgent:0,soon:0,scheduled:0};
var shiftCounts={'1st':0,'2nd':0,'3rd':0,'4th':0};
contracts.forEach(function(c){
var prop=c.proposed||{}, tl=c.timeline||{};
var cnt=prop.count||0;
totalWorkers+=cnt;
if(c.implied_bill_rate) totalBillPerHr+=c.implied_bill_rate*cnt;
if(prop.role) roleMix[prop.role]=(roleMix[prop.role]||0)+cnt;
var u=tl.urgency||'scheduled';
if(urgencyMix[u]!==undefined) urgencyMix[u]++;
(c.shifts_needed||['1st']).forEach(function(s){if(shiftCounts[s]!==undefined) shiftCounts[s]++;});
});
// Headline pulse tile
var headline=document.createElement('div');
headline.style.cssText='background:#0d1117;border:1px solid #171d27;border-radius:10px;padding:12px 14px';
var hdLabel=document.createElement('div');
hdLabel.style.cssText='font-size:10px;color:#545d68;text-transform:uppercase;letter-spacing:1px;margin-bottom:4px';
hdLabel.textContent='Chicago permit pulse — unclaimed demand';
var hdBig=document.createElement('div');
hdBig.style.cssText='font-size:18px;font-weight:700;color:#e6edf3;letter-spacing:-0.3px';
hdBig.textContent=contracts.length+' permit'+(contracts.length===1?'':'s')+' · ~'+totalWorkers+' worker'+(totalWorkers===1?'':'s')+' needed';
var hdSub=document.createElement('div');
hdSub.style.cssText='font-size:11px;color:#8b949e;margin-top:3px';
var hdParts=[];
if(totalBillPerHr>0) hdParts.push('$'+totalBillPerHr.toFixed(0)+'/hr combined bill demand');
if(urgencyMix.overdue>0) hdParts.push(urgencyMix.overdue+' overdue');
if(urgencyMix.urgent>0) hdParts.push(urgencyMix.urgent+' urgent');
hdSub.textContent=hdParts.length?hdParts.join(' · '):'scheduled only — no emergencies on the board';
headline.appendChild(hdLabel);headline.appendChild(hdBig);headline.appendChild(hdSub);
rightCol.appendChild(headline);
// Per-shift 2x2 grid
var shiftGrid=document.createElement('div');
shiftGrid.style.cssText='display:grid;grid-template-columns:repeat(2,1fr);gap:8px';
['1st','2nd','3rd','4th'].forEach(function(s){
var cell=document.createElement('div');
cell.style.cssText='background:#0d1117;border:1px solid #171d27;border-radius:8px;padding:8px 10px;border-left:3px solid '+SHIFT_COLORS[s]+(cs===s?';box-shadow:0 0 0 1px '+SHIFT_COLORS[s]:'');
var head=document.createElement('div');head.style.cssText='font-size:9px;color:'+SHIFT_COLORS[s]+';font-weight:600;text-transform:uppercase;letter-spacing:1px;margin-bottom:2px';
head.textContent=s+' shift'+(cs===s?' · live':'');
var big=document.createElement('div');big.style.cssText='font-size:16px;font-weight:600;color:#e6edf3';big.textContent=shiftCounts[s]+' permit'+(shiftCounts[s]===1?'':'s');
cell.appendChild(head);cell.appendChild(big);
shiftGrid.appendChild(cell);
});
rightCol.appendChild(shiftGrid);
// Role mix — top 4 roles
var roleEntries=Object.keys(roleMix).map(function(k){return [k,roleMix[k]];}).sort(function(a,b){return b[1]-a[1];}).slice(0,4);
if(roleEntries.length){
var roleBox=document.createElement('div');
roleBox.style.cssText='background:#0d1117;border:1px solid #171d27;border-radius:8px;padding:8px 12px';
var rhd=document.createElement('div');rhd.style.cssText='font-size:9px;color:#545d68;text-transform:uppercase;letter-spacing:1px;margin-bottom:6px';
rhd.textContent='Roles in demand';
roleBox.appendChild(rhd);
var maxN=roleEntries[0][1]||1;
roleEntries.forEach(function(pair){
var row=document.createElement('div');row.style.cssText='display:flex;align-items:center;gap:8px;margin:3px 0;font-size:11px';
var nm=document.createElement('span');nm.style.cssText='color:#e6edf3;flex:0 0 120px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis';
nm.textContent=pair[0];
var barWrap=document.createElement('div');barWrap.style.cssText='flex:1;height:6px;background:#161b22;border-radius:3px;overflow:hidden';
var bar=document.createElement('div');bar.style.cssText='height:100%;background:#58a6ff;width:'+Math.round((pair[1]/maxN)*100)+'%';
barWrap.appendChild(bar);
var qty=document.createElement('span');qty.style.cssText='color:#8b949e;font-size:11px;font-variant-numeric:tabular-nums;min-width:30px;text-align:right';
qty.textContent='×'+pair[1];
row.appendChild(nm);row.appendChild(barWrap);row.appendChild(qty);
roleBox.appendChild(row);
});
rightCol.appendChild(roleBox);
}
root.appendChild(rightCol);
}
function loadStaffingForecast(){
api('/intelligence/staffing_forecast',{}).then(function(r){
var el=document.getElementById('staffing-forecast');el.textContent='';
if(!r||!r.forecast){el.textContent='Forecast unavailable.';return}
// Header summary
var hdr=document.createElement('div');hdr.style.cssText='background:#0d1117;border:1px solid #171d27;border-radius:10px;padding:14px;margin-bottom:10px;display:flex;justify-content:space-between;gap:16px';
var left=document.createElement('div');
var big=document.createElement('div');big.style.cssText='font-size:22px;font-weight:700;color:#e6edf3;letter-spacing:-0.5px';
big.textContent='$'+(r.total_cost||0).toLocaleString('en-US',{maximumFractionDigits:0})+' in construction coming';
var sub=document.createElement('div');sub.style.cssText='color:#8b949e;font-size:12px;margin-top:4px';
sub.textContent=r.permit_count+' permits filed last 30 days · ~'+r.total_estimated_workers+' workers needed across roles';
left.appendChild(big);left.appendChild(sub);hdr.appendChild(left);
var right=document.createElement('div');right.style.cssText='text-align:right;font-size:11px';
if(r.critical_roles>0){
var c=document.createElement('div');c.style.cssText='color:#f85149;font-weight:700';
c.textContent=r.critical_roles+' CRITICAL role'+(r.critical_roles!==1?'s':'')+' — supply gap';right.appendChild(c);
} else if(r.tight_roles>0){
var t=document.createElement('div');t.style.cssText='color:#d29922;font-weight:700';
t.textContent=r.tight_roles+' tight role'+(r.tight_roles!==1?'s':'');right.appendChild(t);
} else {
var g=document.createElement('div');g.style.cssText='color:#3fb950;font-weight:600';g.textContent='bench covers predicted demand';right.appendChild(g);
}
var when=document.createElement('div');when.style.cssText='color:#545d68;margin-top:2px';
when.textContent='updated '+((r.duration_ms||0)/1000).toFixed(1)+'s ago';right.appendChild(when);
hdr.appendChild(right);el.appendChild(hdr);
// Per-role rows
r.forecast.forEach(function(f){
var row=document.createElement('div');
var riskColor={critical:'#f85149',tight:'#d29922',watch:'#d29922',ok:'#3fb950'}[f.risk]||'#8b949e';
row.style.cssText='background:#0d1117;border:1px solid #171d27;border-radius:8px;padding:10px 14px;margin-bottom:6px;border-left:3px solid '+riskColor+';display:flex;justify-content:space-between;gap:12px;font-size:12px;align-items:center';
var l=document.createElement('div');
var n=document.createElement('div');n.style.cssText='color:#e6edf3;font-weight:600;font-size:13px';
n.textContent=f.role;
var d=document.createElement('div');d.style.cssText='color:#8b949e;font-size:11px;margin-top:2px';
d.textContent=f.demand_permits+' permit'+(f.demand_permits!==1?'s':'')+' · est '+f.demand_workers+' workers · earliest staffing deadline '+f.earliest_staffing_deadline;
l.appendChild(n);l.appendChild(d);row.appendChild(l);
var r2=document.createElement('div');r2.style.cssText='text-align:right;white-space:nowrap';
var cov=document.createElement('div');cov.style.cssText='color:'+riskColor+';font-weight:700;font-size:13px';
cov.textContent=f.bench_available.toLocaleString()+' / '+f.demand_workers+' available ('+f.coverage_pct+'%)';
var days=document.createElement('div');days.style.cssText='color:'+(f.days_to_deadline<=0?'#f85149':f.days_to_deadline<=7?'#d29922':'#8b949e')+';font-size:11px;margin-top:2px';
days.textContent=f.days_to_deadline<=0?(Math.abs(f.days_to_deadline)+'d overdue'):(f.days_to_deadline+'d to deadline');
r2.appendChild(cov);r2.appendChild(days);row.appendChild(r2);
el.appendChild(row);
});
}).catch(function(e){
document.getElementById('staffing-forecast').textContent='Forecast error: '+(e.message||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()})
}
// Mirror of the Bun-side pay-rate formula so client-side renderers
// (main search, modal) can show a rate when they only have worker
// fields, not a pre-enriched hybrid source. Keep in sync with
// impliedPayRate() in mcp-server/index.ts.
var ROLE_BASE_PAY={Electrician:28,Welder:26,'Machine Operator':24,'Maintenance Tech':26,
'Forklift Operator':20,Loader:17,'Warehouse Associate':17,'Material Handler':18,
'Production Worker':18,'Quality Tech':23,'Line Lead':22,Assembler:18,'Shipping Clerk':19};
function computeImpliedPayRate(role,rel,archetype){
var base=ROLE_BASE_PAY[role||'']||19;
var r=typeof rel==='string'?parseFloat(rel):(rel||0.5);
var relBump=(isFinite(r)?r:0.5)*4;
var a=(archetype||'').toLowerCase();
var archBump=a==='specialist'?4:a==='leader'?3:a==='reliable'?1:0;
return Math.round((base+relBump+archBump)*100)/100;
}
// ─── Render one Project Index (custom Chicago build-signal portfolio) ──
// Layout:
// - Property row (BLDG ticker + address + owner placeholder)
// - Per-entity portfolio row:
// LLC·TICKER display_name [role badge] [risk pill]
// OSHA line (inspection count / most recent date / state list)
// ILSOS line (either data or "source unreachable" honest label)
// Conservative with color: green = clean, yellow = moderate, red = high.
function renderEntityBrief(host, brief){
host.textContent='';
if(!brief || !brief.entities){
var empty=document.createElement('div');empty.style.cssText='color:#545d68;font-size:11px;padding:6px 0';empty.textContent='No entities identified for this permit.';host.appendChild(empty);return;
}
// ─── MACRO TILE — Chicago construction employment context ─────────
// Same number applies to every permit; render as small strip above
// the project-index card so staffers see whether the broader market
// is helping or hurting them.
if(brief.macro && brief.macro.status==='ok' && brief.macro.latest){
var m=brief.macro;
var mLine=document.createElement('div');
var mc=m.trend==='growing'?'#3fb950':m.trend==='declining'?'#f85149':'#8b949e';
mLine.style.cssText='display:flex;align-items:center;gap:10px;padding:8px 12px;background:#0d1117;border:1px solid '+mc+'33;border-radius:6px;margin-bottom:8px;font-size:11px;color:#8b949e';
var mIcon=document.createElement('span');mIcon.style.cssText='font-size:14px';mIcon.textContent='📊';
var mTag=document.createElement('span');mTag.style.cssText='font-size:9px;color:#545d68;text-transform:uppercase;letter-spacing:1px';mTag.textContent='MACRO';
var mBody=document.createElement('span');mBody.style.cssText='flex:1';
var yoy=m.yoy_change_pct;
mBody.textContent='Chicago MSA construction: '+m.latest.value+'k workers ('+m.latest.period+')'+(yoy!==null?' · '+(yoy>=0?'+':'')+yoy.toFixed(1)+'% YoY':'');
var mChip=document.createElement('span');
mChip.style.cssText='padding:2px 8px;border-radius:9px;background:'+mc+'22;color:'+mc+';font-size:9px;font-weight:600;text-transform:uppercase;letter-spacing:0.5px';
mChip.textContent=m.trend;
var mLink=document.createElement('a');mLink.href='https://www.bls.gov/eag/eag.il_chicago_msa.htm';mLink.target='_blank';mLink.rel='noopener';
mLink.style.cssText='color:#58a6ff;font-size:10px;text-decoration:none';
mLink.textContent='BLS ↗';
mLine.appendChild(mIcon);mLine.appendChild(mTag);mLine.appendChild(mBody);mLine.appendChild(mChip);mLine.appendChild(mLink);
host.appendChild(mLine);
}
// ─── PROJECT INDEX SCORE — auditable matrix score at top ─────────
// Shows the 0-100 aggregate + every signal that contributed (sorted
// by absolute impact). Click-to-expand the contribution breakdown.
var idx=brief.index_score;
if(idx){
var BAND_COLORS={red:'#f85149',amber:'#d29922',neutral:'#8b949e',green:'#3fb950',strong:'#2ea043'};
var bc=BAND_COLORS[idx.band]||'#8b949e';
var iSec=document.createElement('details');
iSec.style.cssText='background:#0d1117;border:2px solid '+bc+'66;border-radius:8px;margin-bottom:10px';
var iSum=document.createElement('summary');
iSum.style.cssText='list-style:none;cursor:pointer;padding:12px 14px;display:flex;align-items:center;gap:12px;outline:none';
var iLabel=document.createElement('span');
iLabel.style.cssText='font-size:10px;color:#545d68;text-transform:uppercase;letter-spacing:1px;font-weight:600';
iLabel.textContent='PROJECT INDEX';
var iScore=document.createElement('span');
iScore.style.cssText='font-family:ui-monospace,SFMono-Regular,monospace;font-size:22px;font-weight:700;color:'+bc;
iScore.textContent=idx.score;
var iBand=document.createElement('span');
iBand.style.cssText='padding:4px 10px;border-radius:9px;font-size:10px;font-weight:600;background:'+bc+'22;color:'+bc+';text-transform:uppercase;letter-spacing:0.5px';
iBand.textContent=idx.band+(idx.partial?' · partial':'');
var iCount=document.createElement('span');
iCount.style.cssText='color:#545d68;font-size:10px;margin-left:auto';
iCount.textContent=idx.contributions.length+' signals · 50 = neutral · '+(idx.contributions.length>0?'click to audit':'no signals');
iSum.appendChild(iLabel);iSum.appendChild(iScore);iSum.appendChild(iBand);iSum.appendChild(iCount);
iSec.appendChild(iSum);
var iBody=document.createElement('div');
iBody.style.cssText='padding:0 14px 12px;font-size:11px';
if(idx.contributions.length===0){
var ec=document.createElement('div');ec.style.cssText='color:#545d68;font-style:italic';
ec.textContent='No signals available — sources may still be loading.';
iBody.appendChild(ec);
} else {
idx.contributions.forEach(function(c){
var r=document.createElement('div');
r.style.cssText='display:flex;gap:8px;align-items:baseline;padding:4px 0;border-top:1px dashed #1f2631';
var sign=c.contribution>0?'+':c.contribution<0?'':'';
var col=c.contribution>0?'#3fb950':c.contribution<0?'#f85149':'#545d68';
var pts=document.createElement('span');
pts.style.cssText='font-family:ui-monospace,monospace;font-size:11px;color:'+col+';font-weight:600;min-width:42px;text-align:right;font-variant-numeric:tabular-nums';
pts.textContent=sign+(Math.round(c.contribution*10)/10);
var w=document.createElement('span');
w.style.cssText='font-size:9px;color:#545d68;font-family:ui-monospace,monospace;min-width:30px';
w.textContent='w='+c.weight;
var note=document.createElement('span');note.style.cssText='color:#8b949e;flex:1;min-width:0';
note.textContent=c.note;
var sig=document.createElement('span');
sig.style.cssText='font-size:9px;color:#484f58;font-family:ui-monospace,monospace;text-align:right;white-space:nowrap';
sig.textContent=c.signal;
r.appendChild(pts);r.appendChild(w);r.appendChild(note);r.appendChild(sig);
iBody.appendChild(r);
});
}
iSec.appendChild(iBody);
host.appendChild(iSec);
}
// ─── STOCK TICKERS — portfolio row at the top ─────────
// Every publicly-traded entity related to this permit, sorted by
// cap-proxy desc. "Most profitable related company" surfaces first.
// No tickers? Render an explicit "all contractors private" line so
// staffers know we looked, rather than silently hiding the section.
var tickers=brief.tickers||[];
var tSec=document.createElement('div');
tSec.style.cssText='background:#0d2818;border:1px solid #2ea04340;border-radius:6px;padding:10px 12px;margin-bottom:8px';
var tHead=document.createElement('div');
tHead.style.cssText='display:flex;align-items:center;gap:8px;margin-bottom:6px;font-size:10px;color:#3fb950;text-transform:uppercase;letter-spacing:1px;font-weight:600';
tHead.textContent='STOCK TICKERS — RELATED PUBLIC COMPANIES';
var tMeta=document.createElement('span');tMeta.style.cssText='margin-left:auto;color:#545d68;font-size:9px;letter-spacing:0.5px';
tMeta.textContent=tickers.length+' issuer'+(tickers.length===1?'':'s')+' · most-profitable first';
tHead.appendChild(tMeta);
tSec.appendChild(tHead);
if(tickers.length===0){
var tEmpty=document.createElement('div');
tEmpty.style.cssText='color:#8b949e;font-size:11px;line-height:1.5';
tEmpty.textContent='No direct public equity on this permit. Contractors here are private LLCs — pending: SEC EDGAR Exhibit 21 (parent/subsidiary), Wikidata relationships, OpenCorporates corp tree. Public beneficiaries can often be 1-2 hops away (e.g. private GC → public parent).';
tSec.appendChild(tEmpty);
} else {
tickers.forEach(function(t,idx){
var row=document.createElement('div');
row.style.cssText='display:flex;align-items:center;gap:10px;font-size:11px;padding:4px 0;border-top:'+(idx?'1px dashed #2ea04322':'none');
// Rank
var rank=document.createElement('span');rank.style.cssText='color:#545d68;font-size:10px;min-width:16px;font-weight:600';rank.textContent='#'+(idx+1);
// Ticker symbol
var tk=document.createElement('span');
tk.style.cssText='font-family:ui-monospace,SFMono-Regular,monospace;background:#0d1117;padding:3px 8px;border-radius:4px;color:#3fb950;border:1px solid #3fb95066;font-weight:700;font-size:11px;min-width:60px;text-align:center';
tk.textContent=t.ticker;
// Company + exchange
var nm=document.createElement('span');nm.style.cssText='color:#e6edf3;font-weight:500;flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap';
nm.textContent=(t.company_name||t.ticker)+(t.exchange?' · '+t.exchange:'');
nm.title=t.sic_description?t.sic_description+' (SIC '+t.sic+')':'';
// Price
var px=document.createElement('span');px.style.cssText='color:#e6edf3;font-family:ui-monospace,monospace;font-size:11px;font-weight:600;min-width:70px;text-align:right';
px.textContent=t.price?'$'+t.price.toFixed(2):'—';
// Day change chip
var chg=document.createElement('span');
var ch=t.day_change_pct;
var chColor=(ch==null||isNaN(ch))?'#545d68':ch>=0?'#3fb950':'#f85149';
chg.style.cssText='font-family:ui-monospace,monospace;font-size:10px;color:'+chColor+';min-width:52px;text-align:right';
chg.textContent=(ch==null||isNaN(ch))?'':((ch>=0?'+':'')+ch.toFixed(2)+'%');
// External link
var lnk=document.createElement('a');lnk.href=t.stooq_url||'#';lnk.target='_blank';lnk.rel='noopener';
lnk.style.cssText='color:#58a6ff;text-decoration:none;font-size:10px;white-space:nowrap';
lnk.textContent='quote ↗';
row.appendChild(rank);row.appendChild(tk);row.appendChild(nm);row.appendChild(px);row.appendChild(chg);row.appendChild(lnk);
tSec.appendChild(row);
});
// Caveat — honest about cap-proxy vs real market cap
var caveat=document.createElement('div');
caveat.style.cssText='color:#545d68;font-size:9px;margin-top:6px;line-height:1.4;font-style:italic';
caveat.textContent='Ranked by price × volume (cap-proxy). Real market cap needs shares-outstanding from SEC XBRL — queued. Quote from Stooq ('+(tickers[0].price_date||'today')+'); profile from SEC EDGAR.';
tSec.appendChild(caveat);
}
host.appendChild(tSec);
// Property block — owner (Cook County Assessor) + violations + union
var prop=brief.property||{};
var pBlock=document.createElement('div');
pBlock.style.cssText='background:#161b22;border:1px solid #1f2631;border-radius:6px;padding:10px 12px;margin-bottom:8px;font-size:11px';
// Top row: ticker + address + owner inline
var pTop=document.createElement('div');
pTop.style.cssText='display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin-bottom:4px';
var pTicker=document.createElement('span');
pTicker.style.cssText='font-family:ui-monospace,SFMono-Regular,monospace;background:#0d1117;padding:2px 6px;border-radius:4px;color:#d29922;border:1px solid #d2992244;font-weight:600;font-size:10px;flex-shrink:0';
pTicker.textContent=prop.ticker||'BLDG·?';
var pAddr=document.createElement('span');pAddr.style.cssText='color:#e6edf3;flex:1;min-width:0;font-weight:500';pAddr.textContent=prop.address||'';
pTop.appendChild(pTicker);pTop.appendChild(pAddr);
pBlock.appendChild(pTop);
// Owner line (Cook County Assessor)
var owner=prop.owner;
if(owner){
var oLine=document.createElement('div');
oLine.style.cssText='font-size:10px;color:#8b949e;margin-top:4px;display:flex;gap:6px;align-items:baseline;flex-wrap:wrap';
var oTag=document.createElement('span');oTag.style.cssText='font-size:9px;color:#545d68;text-transform:uppercase;letter-spacing:1px;min-width:54px';oTag.textContent='OWNER';
var oBody=document.createElement('span');oBody.style.cssText='flex:1;min-width:0';
if(owner.status==='ok'){
var mailLoc=[owner.mailing_city,owner.mailing_state,owner.mailing_zip].filter(Boolean).join(', ');
var localFlag=owner.mailing_state&&owner.mailing_state!=='IL'?' · ✦ owner mails out of state ('+owner.mailing_state+')':'';
oBody.textContent='PIN '+(owner.pin||'?')+' · mail to '+(owner.mailing_address||'?')+(mailLoc?' · '+mailLoc:'')+(owner.ward?' · ward '+owner.ward:'')+localFlag;
} else if(owner.status==='no_match'){
oBody.style.color='#545d68';
oBody.textContent='no parcel match in Cook County Assessor';
} else {
oBody.style.color='#d29922';
oBody.textContent='lookup error: '+(owner.error||'unknown');
}
oLine.appendChild(oTag);oLine.appendChild(oBody);
pBlock.appendChild(oLine);
}
// Violations line (Chicago Building Violations)
var v=prop.violations;
if(v && v.status==='ok'){
var vLine=document.createElement('div');
vLine.style.cssText='font-size:10px;color:#8b949e;margin-top:4px;display:flex;gap:6px;align-items:baseline;flex-wrap:wrap';
var vTag=document.createElement('span');vTag.style.cssText='font-size:9px;color:#545d68;text-transform:uppercase;letter-spacing:1px;min-width:54px';vTag.textContent='VIOLATIONS';
var vBody=document.createElement('span');vBody.style.cssText='flex:1;min-width:0';
var vc=v.total_violations===0?'#3fb950':v.open_violations>0?'#d29922':v.stop_work_orders>0?'#f85149':'#8b949e';
vBody.style.color=vc;
if(v.total_violations===0){
vBody.textContent='no violations on record · clean';
} else {
var bits=[v.total_violations+' total'];
if(v.open_violations>0) bits.push(v.open_violations+' OPEN');
if(v.stop_work_orders>0) bits.push('⚠ '+v.stop_work_orders+' STOP-WORK');
if(v.most_recent_date) bits.push('most recent '+v.most_recent_date);
vBody.textContent=bits.join(' · ');
}
vLine.appendChild(vTag);vLine.appendChild(vBody);
pBlock.appendChild(vLine);
}
// Site context line (TIF / landmark / ward / lat-lon)
var sc=prop.site_context;
if(sc && sc.status==='ok'){
var scLine=document.createElement('div');
scLine.style.cssText='font-size:10px;color:#8b949e;margin-top:4px;display:flex;gap:6px;align-items:baseline;flex-wrap:wrap';
var scTag=document.createElement('span');scTag.style.cssText='font-size:9px;color:#545d68;text-transform:uppercase;letter-spacing:1px;min-width:54px';scTag.textContent='SITE CTX';
var scBody=document.createElement('span');scBody.style.cssText='flex:1;min-width:0';
var scBits=[];
if(sc.in_tif_district){
var tifChip=document.createElement('span');
tifChip.style.cssText='display:inline-block;padding:2px 7px;border-radius:9px;background:#0d2818;border:1px solid #3fb95066;color:#3fb950;font-weight:600;font-size:10px;margin-right:6px';
tifChip.textContent='💰 TIF · '+(sc.tif_district_name||'subsidy zone');
tifChip.title='Site is inside a Tax Increment Financing district — public-subsidy backing';
scBody.appendChild(tifChip);
}
if(sc.is_landmark){
var lmChip=document.createElement('span');
lmChip.style.cssText='display:inline-block;padding:2px 7px;border-radius:9px;background:#3a2a14;border:1px solid #d2992266;color:#d29922;font-weight:600;font-size:10px;margin-right:6px';
lmChip.textContent='🏛 LANDMARK · '+(sc.landmark_name||'historic district');
lmChip.title='Preservation review will extend project timeline';
scBody.appendChild(lmChip);
}
if(sc.nearest_cta_station && sc.nearest_cta_distance_m!=null){
var ctaTxt='🚇 '+sc.nearest_cta_station+(sc.nearest_cta_lines?' ('+sc.nearest_cta_lines+')':'')+' · '+sc.nearest_cta_distance_m+'m';
var ctaCol=sc.nearest_cta_distance_m<=800?'#3fb950':sc.nearest_cta_distance_m<=1500?'#8b949e':'#d29922';
var ctaChip=document.createElement('span');
ctaChip.style.cssText='display:inline-block;padding:2px 7px;border-radius:9px;background:#0d1117;border:1px solid '+ctaCol+'66;color:'+ctaCol+';font-weight:600;font-size:10px;margin-right:6px';
ctaChip.textContent=ctaTxt;
scBody.appendChild(ctaChip);
}
if(sc.nearby_permits_90d!=null && sc.nearby_permits_90d>0){
var npCol=sc.nearby_permits_90d>5?'#d29922':'#8b949e';
var npChip=document.createElement('span');
npChip.style.cssText='display:inline-block;padding:2px 7px;border-radius:9px;background:#0d1117;border:1px solid '+npCol+'66;color:'+npCol+';font-weight:600;font-size:10px;margin-right:6px';
var npM=sc.nearby_permits_value_90d&&sc.nearby_permits_value_90d>=1e6?' · $'+(sc.nearby_permits_value_90d/1e6).toFixed(1)+'M':'';
npChip.textContent=sc.nearby_permits_90d+' nearby permits 90d'+npM;
npChip.title='Permits issued within 0.5mi · 90d window · '+(sc.nearby_permits_value_90d||0).toLocaleString()+' total $';
scBody.appendChild(npChip);
}
if(sc.ward) scBits.push('ward '+sc.ward);
if(sc.latitude && sc.longitude) scBits.push(sc.latitude.slice(0,7)+', '+sc.longitude.slice(0,8));
if(scBits.length){
var rest=document.createElement('span');rest.textContent=scBits.join(' · ');
scBody.appendChild(rest);
}
if(!sc.in_tif_district && !sc.is_landmark && !scBits.length) scBody.textContent='no extra context';
scLine.appendChild(scTag);scLine.appendChild(scBody);
pBlock.appendChild(scLine);
}
// Mechanics liens placeholder
var liens=prop.liens;
if(liens && liens.status==='needs_setup'){
var lLine=document.createElement('div');
lLine.style.cssText='font-size:10px;color:#545d68;margin-top:4px;display:flex;gap:6px;align-items:baseline;font-style:italic';
var lTag=document.createElement('span');lTag.style.cssText='font-size:9px;color:#545d68;text-transform:uppercase;letter-spacing:1px;min-width:54px;font-style:normal';lTag.textContent='LIENS';
var lBody=document.createElement('span');lBody.style.cssText='flex:1;min-width:0';
lBody.textContent='Cook County Recorder mechanics-lien lookup queued';
lLine.appendChild(lTag);lLine.appendChild(lBody);
pBlock.appendChild(lLine);
}
// Union line (static lookup by trade)
var u=prop.union;
if(u){
var uLine=document.createElement('div');
uLine.style.cssText='font-size:10px;color:#8b949e;margin-top:4px;display:flex;gap:6px;align-items:baseline;flex-wrap:wrap';
var uTag=document.createElement('span');uTag.style.cssText='font-size:9px;color:#545d68;text-transform:uppercase;letter-spacing:1px;min-width:54px';uTag.textContent='UNIONS';
var uBody=document.createElement('span');uBody.style.cssText='flex:1;min-width:0';
uBody.textContent=u.trade+' → '+(u.primary_locals||[]).map(function(l){return l.name}).join(', ');
uBody.title=(u.primary_locals||[]).map(function(l){return l.name+' — '+l.jurisdiction}).join('\n');
uLine.appendChild(uTag);uLine.appendChild(uBody);
pBlock.appendChild(uLine);
// Training centers chip row, collapsible-ish
if(u.training_centers && u.training_centers.length){
var tcLine=document.createElement('div');
tcLine.style.cssText='font-size:10px;color:#8b949e;margin-top:4px;padding-left:60px';
var tcTag=document.createElement('div');tcTag.style.cssText='font-size:9px;color:#545d68;text-transform:uppercase;letter-spacing:1px;margin-bottom:2px';tcTag.textContent='Training centers (apprenticeship)';
tcLine.appendChild(tcTag);
u.training_centers.forEach(function(tc){
var t=document.createElement('div');
t.style.cssText='font-size:10px;color:#8b949e;line-height:1.4;padding:1px 0';
t.textContent='· '+tc.name+' — '+tc.address+' ('+tc.program_length+')';
tcLine.appendChild(t);
});
pBlock.appendChild(tcLine);
}
}
host.appendChild(pBlock);
// Each entity
brief.entities.forEach(function(e){
var row=document.createElement('div');
row.style.cssText='background:#0d1117;border:1px solid #1f2631;border-radius:6px;padding:10px 12px;margin-bottom:8px';
// Header: ticker · name · role · risk pill
var hd=document.createElement('div');
hd.style.cssText='display:flex;align-items:center;gap:8px;margin-bottom:6px;flex-wrap:wrap';
var tkr=document.createElement('span');
tkr.style.cssText='font-family:ui-monospace,SFMono-Regular,monospace;background:#161b22;padding:2px 6px;border-radius:4px;color:#58a6ff;border:1px solid #58a6ff44;font-weight:600;font-size:10px';
tkr.textContent=e.ticker||'LLC·?';
var nm=document.createElement('a');nm.href='/contractor?name='+encodeURIComponent(e.display_name||'');
nm.target='_blank';nm.rel='noopener';
nm.style.cssText='color:#e6edf3;font-weight:600;font-size:12px;text-decoration:none;border-bottom:1px dotted #58a6ff44';
nm.title='Open full contractor profile';
nm.textContent=e.display_name||'?';
var rl=document.createElement('span');rl.style.cssText='color:#8b949e;font-size:10px;text-transform:uppercase;letter-spacing:0.5px';rl.textContent=e.role||'';
hd.appendChild(tkr);hd.appendChild(nm);hd.appendChild(rl);
var spacer=document.createElement('span');spacer.style.cssText='flex:1';hd.appendChild(spacer);
// Risk pill
if(e.risk){
var s=e.risk.score, pill=document.createElement('span');
var riskColor=s===null?'#545d68':s<30?'#3fb950':s<60?'#d29922':'#f85149';
pill.style.cssText='padding:3px 8px;border-radius:9px;font-size:10px;font-weight:600;background:#0d1117;border:1px solid '+riskColor+'66;color:'+riskColor;
pill.textContent=(s===null?'risk n/a':'risk '+s)+(e.risk.partial?' · partial':'');
pill.title=(e.risk.factors||[]).join(' · ');
hd.appendChild(pill);
}
row.appendChild(hd);
// SVEP red flag (OSHA Severe Violator Enforcement Program)
var svep=e.svep;
if(svep && svep.flagged){
var svLine=document.createElement('div');
svLine.style.cssText='font-size:11px;color:#fca5a5;background:#3a1a1a;border:1px solid #f85149;border-radius:6px;padding:6px 10px;margin:4px 0;display:flex;gap:6px;align-items:baseline;flex-wrap:wrap;font-weight:600';
var svIcon=document.createElement('span');svIcon.style.fontSize='13px';svIcon.textContent='⚠';
var svTag=document.createElement('span');svTag.style.cssText='font-size:10px;color:#f85149;text-transform:uppercase;letter-spacing:1px';svTag.textContent='OSHA SVEP';
var svBody=document.createElement('span');svBody.style.cssText='flex:1;min-width:0';
svBody.textContent='Severe Violator Enforcement Program — '+svep.matched_entries.map(function(m){return m.name.split('/')[0]}).join(', ').slice(0,80);
svBody.title='Matched SVEP entries:\n'+svep.matched_entries.map(function(m){return m.name}).join('\n');
var svLink=document.createElement('a');svLink.href='https://www.osha.gov/enforcement/svep';svLink.target='_blank';svLink.rel='noopener';svLink.style.cssText='color:#58a6ff;font-size:10px;text-decoration:none';svLink.textContent='SVEP ↗';
svLine.appendChild(svIcon);svLine.appendChild(svTag);svLine.appendChild(svBody);svLine.appendChild(svLink);
row.appendChild(svLine);
}
// Parent-public-equity link — "private GC → public parent ticker"
// chain. Surfaces who actually benefits if the contract closes.
var pl=e.parent_link;
if(pl && pl.status==='ok'){
var plLine=document.createElement('div');
plLine.style.cssText='font-size:11px;color:#8b949e;margin:3px 0;display:flex;gap:6px;align-items:baseline;flex-wrap:wrap';
var plTag=document.createElement('span');plTag.style.cssText='font-size:9px;color:#545d68;text-transform:uppercase;letter-spacing:1px;min-width:54px';plTag.textContent='PARENT';
var plBody=document.createElement('span');plBody.style.cssText='flex:1;min-width:0;color:#3fb950';
var plBits=[pl.parent_name];
if(pl.parent_ticker) plBits.push(pl.parent_ticker);
if(pl.parent_exchange) plBits.push(pl.parent_exchange);
if(pl.parent_country) plBits.push(pl.parent_country);
plBody.textContent=plBits.filter(Boolean).join(' · ');
plBody.title=pl.link_source||'';
plLine.appendChild(plTag);plLine.appendChild(plBody);
row.appendChild(plLine);
} else if(pl && pl.status==='no_link'){
var plLine2=document.createElement('div');
plLine2.style.cssText='font-size:10px;color:#545d68;margin:3px 0;display:flex;gap:6px;align-items:baseline;flex-wrap:wrap';
var pl2Tag=document.createElement('span');pl2Tag.style.cssText='font-size:9px;color:#545d68;text-transform:uppercase;letter-spacing:1px;min-width:54px';pl2Tag.textContent='PARENT';
var pl2Body=document.createElement('span');pl2Body.style.cssText='flex:1;min-width:0;font-style:italic';
pl2Body.textContent=pl.reason||'no public parent identified';
plLine2.appendChild(pl2Tag);plLine2.appendChild(pl2Body);
row.appendChild(plLine2);
}
// USASpending federal contracts
var fed=e.federal;
if(fed && fed.status==='ok' && fed.total_awards_count>0){
var fLine=document.createElement('div');
fLine.style.cssText='font-size:11px;color:#8b949e;margin:3px 0;display:flex;gap:6px;align-items:baseline;flex-wrap:wrap';
var fTag=document.createElement('span');fTag.style.cssText='font-size:9px;color:#545d68;text-transform:uppercase;letter-spacing:1px;min-width:54px';fTag.textContent='FEDERAL';
var fBody=document.createElement('span');fBody.style.cssText='flex:1;min-width:0';
var dollars=fed.total_awards_value>=1e6?'$'+Math.round(fed.total_awards_value/1e6*10)/10+'M':'$'+Math.round(fed.total_awards_value/1e3)+'K';
var topAg=(fed.top_agencies||[]).slice(0,2).map(function(a){return a.agency.replace(/^DEPARTMENT OF /i,'')}).join(', ');
fBody.textContent=fed.total_awards_count+' awards · '+dollars+' total'+(topAg?' · '+topAg:'')+(fed.most_recent_award_date?' · most recent '+fed.most_recent_award_date:'');
var fLink=document.createElement('a');fLink.href=fed.source_url;fLink.target='_blank';fLink.rel='noopener';
fLink.style.cssText='color:#58a6ff;font-size:10px;text-decoration:none;white-space:nowrap';
fLink.textContent='usaspending ↗';
fLine.appendChild(fTag);fLine.appendChild(fBody);fLine.appendChild(fLink);
row.appendChild(fLine);
} else if(fed && fed.status==='no_match'){
var fLine2=document.createElement('div');
fLine2.style.cssText='font-size:10px;color:#545d68;margin:3px 0;display:flex;gap:6px;align-items:baseline';
var f2Tag=document.createElement('span');f2Tag.style.cssText='font-size:9px;color:#545d68;text-transform:uppercase;letter-spacing:1px;min-width:54px';f2Tag.textContent='FEDERAL';
var f2Body=document.createElement('span');f2Body.style.cssText='flex:1;min-width:0;font-style:italic';
f2Body.textContent='no federal contracts on record';
fLine2.appendChild(f2Tag);fLine2.appendChild(f2Body);
row.appendChild(fLine2);
}
// News mentions (Google News RSS) + sentiment chip
var news=e.news;
var ns=e.news_sentiment;
if(news && news.status==='ok' && news.recent_headlines && news.recent_headlines.length){
var nLine=document.createElement('div');
nLine.style.cssText='font-size:11px;color:#8b949e;margin:3px 0;display:flex;gap:6px;align-items:baseline;flex-wrap:wrap';
var nTag=document.createElement('span');nTag.style.cssText='font-size:9px;color:#545d68;text-transform:uppercase;letter-spacing:1px;min-width:54px';nTag.textContent='NEWS';
var nBody=document.createElement('span');nBody.style.cssText='flex:1;min-width:0';
nBody.textContent=news.total_mentions+' mentions · latest: '+(news.recent_headlines[0].title||'').slice(0,72);
nBody.title=news.recent_headlines.map(function(h){return h.title}).join('\n');
nLine.appendChild(nTag);nLine.appendChild(nBody);
// Sentiment chip
if(ns && (ns.positive||ns.negative)){
var sc=ns.score;
var sCol=sc>0.2?'#3fb950':sc<-0.2?'#f85149':'#8b949e';
var sChip=document.createElement('span');
sChip.style.cssText='padding:2px 7px;border-radius:9px;background:#0d1117;border:1px solid '+sCol+'66;color:'+sCol+';font-weight:600;font-size:9px';
sChip.textContent=(sc>=0?'+':'')+sc.toFixed(2)+' · '+ns.positive+'/'+ns.negative;
sChip.title='Sentiment score: positive headlines '+ns.positive+', negative '+ns.negative+', neutral '+ns.neutral+
(ns.flagged_headlines.length?'\n\n'+ns.flagged_headlines.map(function(h){return '['+h.polarity+'] '+h.title.slice(0,60)+(h.reasons.length?' ('+h.reasons.join(',')+')':'')}).join('\n'):'');
nLine.appendChild(sChip);
}
row.appendChild(nLine);
}
// NLRB cases (real scraper)
var nlrb=e.nlrb;
if(nlrb && nlrb.status==='ok' && nlrb.total_cases>0){
var nlLine=document.createElement('div');
nlLine.style.cssText='font-size:11px;color:#8b949e;margin:3px 0;display:flex;gap:6px;align-items:baseline';
var nlTag=document.createElement('span');nlTag.style.cssText='font-size:9px;color:#545d68;text-transform:uppercase;letter-spacing:1px;min-width:54px';nlTag.textContent='NLRB';
var nlBody=document.createElement('span');nlBody.style.cssText='flex:1;min-width:0';
nlBody.textContent=nlrb.total_cases+' case'+(nlrb.total_cases===1?'':'s')+' on file';
var nlLink=document.createElement('a');
nlLink.href='https://www.nlrb.gov/search/case?search_term='+encodeURIComponent(e.display_name);
nlLink.target='_blank';nlLink.rel='noopener';nlLink.style.cssText='color:#58a6ff;font-size:10px;text-decoration:none';
nlLink.textContent='nlrb.gov ↗';
nlLine.appendChild(nlTag);nlLine.appendChild(nlBody);nlLine.appendChild(nlLink);
row.appendChild(nlLine);
} else if(nlrb && nlrb.status==='needs_setup'){
var nlLine2=document.createElement('div');
nlLine2.style.cssText='font-size:10px;color:#545d68;margin:3px 0;display:flex;gap:6px;align-items:baseline;font-style:italic';
var nl2Tag=document.createElement('span');nl2Tag.style.cssText='font-size:9px;color:#545d68;text-transform:uppercase;letter-spacing:1px;min-width:54px;font-style:normal';nl2Tag.textContent='NLRB';
var nl2Body=document.createElement('span');nl2Body.style.cssText='flex:1;min-width:0';
nl2Body.textContent=nlrb.reason||'awaiting wire-up';
nlLine2.appendChild(nl2Tag);nlLine2.appendChild(nl2Body);
row.appendChild(nlLine2);
}
// Diversity cert (MBE/WBE/DBE)
var div2=e.diversity;
if(div2 && div2.status==='ok' && div2.certifications && div2.certifications.length){
var dLine=document.createElement('div');
dLine.style.cssText='font-size:11px;color:#3fb950;margin:3px 0;display:flex;gap:6px;align-items:baseline;flex-wrap:wrap';
var dTag=document.createElement('span');dTag.style.cssText='font-size:9px;color:#545d68;text-transform:uppercase;letter-spacing:1px;min-width:54px';dTag.textContent='MBE/WBE';
var dBody=document.createElement('span');dBody.style.cssText='flex:1;min-width:0';
dBody.textContent='✓ Certified: '+div2.certifications.map(function(c){return c.category}).join(', ');
dLine.appendChild(dTag);dLine.appendChild(dBody);
row.appendChild(dLine);
}
// Combined placeholders (debarment, OSHA SIR, diversity-needs_setup)
var deb=e.debarment, sir=e.osha_sir;
var pendingBits=[];
if(deb && deb.status==='needs_setup') pendingBits.push('SAM/IDOL debarment');
if(sir && sir.status==='needs_setup') pendingBits.push('OSHA Severe Injury');
if(div2 && div2.status==='needs_setup') pendingBits.push('MBE/WBE certs');
if(pendingBits.length){
var pLine=document.createElement('div');
pLine.style.cssText='font-size:10px;color:#484f58;margin:3px 0;display:flex;gap:6px;align-items:baseline;font-style:italic';
var pTag=document.createElement('span');pTag.style.cssText='font-size:9px;color:#545d68;text-transform:uppercase;letter-spacing:1px;min-width:54px;font-style:normal';pTag.textContent='PENDING';
var pBody=document.createElement('span');pBody.style.cssText='flex:1;min-width:0';
pBody.textContent=pendingBits.join(' · ')+' · awaiting API/scraper setup';
pLine.appendChild(pTag);pLine.appendChild(pBody);
row.appendChild(pLine);
}
// Chicago contractor history — activity velocity signal.
// Growing/declining/new annotations help the staffer judge whether
// this entity is a rising player, steady hand, or fresh LLC.
var hist=e.history;
if(hist && hist.status==='ok'){
var hl=document.createElement('div');
hl.style.cssText='font-size:11px;color:#8b949e;margin:3px 0;display:flex;gap:8px;flex-wrap:wrap;align-items:baseline';
var hTag=document.createElement('span');hTag.style.cssText='font-size:9px;color:#545d68;text-transform:uppercase;letter-spacing:1px;min-width:54px';hTag.textContent='CHI HIST';
var hBody=document.createElement('span');hBody.style.cssText='flex:1;min-width:0';
var dollars=hist.total_cost_last_24mo?' · $'+Math.round(hist.total_cost_last_24mo/1e6*10)/10+'M in 24mo':'';
hBody.textContent=hist.permits_last_180d+' in 180d · '+hist.permits_last_24mo+' in 24mo · '+hist.permits_historical_total+' all-time'+dollars;
var tChip=document.createElement('span');
var tColors={growing:'#3fb950',stable:'#58a6ff',declining:'#d29922',new:'#d29922',unknown:'#545d68'};
var tc=tColors[hist.trend]||'#545d68';
tChip.style.cssText='padding:2px 7px;border-radius:9px;font-size:9px;font-weight:600;background:#0d1117;border:1px solid '+tc+'66;color:'+tc;
tChip.textContent=hist.trend;
if(hist.trend==='new') tChip.title='≤3 permits ever — classic LLC-shuffle signature, investigate before committing workers';
hl.appendChild(hTag);hl.appendChild(hBody);hl.appendChild(tChip);
row.appendChild(hl);
}
// OSHA line
var osha=e.osha;
if(osha){
var o=document.createElement('div');
o.style.cssText='font-size:11px;color:#8b949e;margin:3px 0;display:flex;gap:8px;flex-wrap:wrap;align-items:baseline';
var oTag=document.createElement('span');oTag.style.cssText='font-size:9px;color:#545d68;text-transform:uppercase;letter-spacing:1px;min-width:54px';oTag.textContent='OSHA';
var oBody=document.createElement('span');oBody.style.cssText='flex:1;min-width:0';
if(osha.status==='ok'){
var n=osha.inspection_count;
var ageTxt='';
if(osha.most_recent_date){
var age=(Date.now()-Date.parse(osha.most_recent_date))/(86400000);
ageTxt=age<180?' · <'+Math.round(age)+'d ago':' · '+Math.round(age/30)+'mo ago';
}
oBody.textContent=n+' inspection'+(n===1?'':'s')+(osha.states_seen&&osha.states_seen.length?' across '+osha.states_seen.slice(0,5).join(', ')+(osha.states_seen.length>5?', …':''):'')+' · most recent '+(osha.most_recent_date||'?')+ageTxt;
} else if(osha.status==='no_match'){
oBody.style.color='#3fb950';
oBody.textContent='no inspections on record · clean';
} else {
oBody.style.color='#d29922';
oBody.textContent='fetch error: '+(osha.error||'unknown');
}
var oLink=document.createElement('a');
oLink.href=osha.source_url||'#';oLink.target='_blank';oLink.rel='noopener';
oLink.style.cssText='color:#58a6ff;font-size:10px;text-decoration:none;white-space:nowrap';
oLink.textContent='open on osha.gov ↗';
o.appendChild(oTag);o.appendChild(oBody);o.appendChild(oLink);
row.appendChild(o);
// Recent inspection rows (up to 3)
if(osha.recent_inspections && osha.recent_inspections.length){
var ul=document.createElement('div');
ul.style.cssText='margin-top:4px;font-size:10px;color:#545d68;padding-left:62px;line-height:1.5';
osha.recent_inspections.slice(0,3).forEach(function(i){
var lp=document.createElement('div');
var link=document.createElement('a');link.href=i.detail_url;link.target='_blank';link.rel='noopener';
link.style.cssText='color:#8b949e;text-decoration:none;font-family:ui-monospace,monospace';
link.textContent=i.id;
lp.appendChild(link);
lp.appendChild(document.createTextNode(' · '+i.date+' · '+i.state+' · '+i.type+' · '+i.scope));
ul.appendChild(lp);
});
row.appendChild(ul);
}
}
// ILSOS line
var ilsos=e.ilsos;
if(ilsos){
var il=document.createElement('div');
il.style.cssText='font-size:11px;color:#8b949e;margin:6px 0 0;display:flex;gap:8px;align-items:baseline;flex-wrap:wrap';
var ilTag=document.createElement('span');ilTag.style.cssText='font-size:9px;color:#545d68;text-transform:uppercase;letter-spacing:1px;min-width:54px';ilTag.textContent='ILSOS';
var ilBody=document.createElement('span');ilBody.style.cssText='flex:1;min-width:0';
if(ilsos.status==='ok'){
var bits=[];
if(ilsos.entity_name) bits.push(ilsos.entity_name);
if(ilsos.status_text) bits.push(ilsos.status_text);
if(ilsos.formation_date) bits.push('formed '+ilsos.formation_date);
ilBody.textContent=bits.join(' · ');
} else {
ilBody.style.color='#d29922';
ilBody.textContent='awaiting source · '+(ilsos.reason||ilsos.status);
}
il.appendChild(ilTag);il.appendChild(ilBody);
row.appendChild(il);
}
host.appendChild(row);
});
// Roadmap — what's coming next, from the brief. Rendered as a
// collapsible so it doesn't dominate. Staffers can see the direction
// without having to trust that "more is coming."
if(brief.roadmap && brief.roadmap.length){
var rm=document.createElement('details');
rm.style.cssText='margin-top:8px;background:#0d1117;border:1px dashed #21262d;border-radius:6px';
var rmSum=document.createElement('summary');
rmSum.style.cssText='list-style:none;cursor:pointer;padding:8px 12px;color:#8b949e;font-size:10px;text-transform:uppercase;letter-spacing:1px;font-weight:600;outline:none';
rmSum.textContent='▸ '+brief.roadmap.length+' additional public-data sources queued';
rm.appendChild(rmSum);
var rmBody=document.createElement('div');
rmBody.style.cssText='padding:0 12px 10px;font-size:10px;color:#8b949e;line-height:1.7';
brief.roadmap.forEach(function(line){
var li=document.createElement('div');
li.style.cssText='padding:2px 0';
li.textContent='· '+line;
rmBody.appendChild(li);
});
rm.appendChild(rmBody);
host.appendChild(rm);
}
// Footer: honest data-source summary
var foot=document.createElement('div');
foot.style.cssText='font-size:10px;color:#484f58;margin-top:8px;line-height:1.5';
foot.textContent='Brief generated '+new Date(brief.generated_at).toLocaleTimeString()+' · OSHA scraped live (cached 30d) · SEC EDGAR name→ticker + Stooq live quote · Chicago permit history fuzzy-matched across 2 years · ILSOS blocked at our ASN (pending VPN or OpenCorporates).';
host.appendChild(foot);
}
function loadLiveContracts(){
// Pair live Chicago permits with our 500K worker bench and the
// meta-index discovered patterns for each role+geo. This is the
// "real external data meets synthetic playbook learning" card set.
api('/intelligence/permit_contracts',{}).then(function(r){
var el=document.getElementById('live-contracts');el.textContent='';
if(!r||!r.contracts||r.contracts.length===0){
el.textContent='No permits returned.';return;
}
// Feed the Live Market hero (clock + Chicago permit pulse) before
// rendering cards so both land together and tell one coherent story.
loadLiveMarket(r.contracts);
r.contracts.forEach(function(c){
var p=c.permit||{}, prop=c.proposed||{}, tl=c.timeline||{};
var urg=tl.urgency||'scheduled';
var borderColor={overdue:'#f85149',urgent:'#d29922',soon:'#388bfd',scheduled:'#2ea043'}[urg]||'#388bfd';
var card=document.createElement('div');card.className='insight info';
card.style.cssText='background:#0d1117;border:1px solid #171d27;border-radius:10px;padding:16px;margin-bottom:10px;border-left:3px solid '+borderColor;
// Header — permit
var hdr=document.createElement('div');hdr.style.cssText='display:flex;justify-content:space-between;margin-bottom:8px;gap:12px';
var left=document.createElement('div');
var title=document.createElement('div');title.style.cssText='font-weight:600;color:#e6edf3;font-size:14px';
title.textContent='$'+(p.cost||0).toLocaleString()+' · '+(p.work_type||'');
var addr=document.createElement('div');addr.style.cssText='color:#8b949e;font-size:12px;margin-top:2px';
addr.textContent=(p.address||'')+' · Chicago, IL · filed '+(p.issue_date||'');
// Timeline chip
if(tl.days_to_deadline!==undefined){
var tmline=document.createElement('div');tmline.style.cssText='color:'+borderColor+';font-size:11px;font-weight:600;margin-top:4px';
var urgLabel={overdue:'OVERDUE',urgent:'URGENT',soon:'SOON',scheduled:'SCHEDULED'}[urg]||'SCHEDULED';
var dd=tl.days_to_deadline;
var txt=urgLabel+' · staffing window opens '+(tl.staffing_window_opens||'')+' ('+(dd<=0?Math.abs(dd)+'d overdue':dd+'d to deadline')+') · construction est '+(tl.estimated_construction_start||'');
tmline.textContent=txt;addr.appendChild(document.createElement('br'));left.appendChild(tmline);
}
left.appendChild(title);left.appendChild(addr);
var right=document.createElement('div');right.style.cssText='color:#58a6ff;font-size:12px;font-weight:600;text-align:right;white-space:nowrap';
right.textContent=prop.count+'× '+prop.role;
var sub=document.createElement('div');sub.style.cssText='color:#545d68;font-size:10px;text-align:right';
sub.textContent='pool: '+(prop.pool_size||'?').toLocaleString()+' available';
right.appendChild(sub);
// Rate awareness: show implied bill rate per contract
if(c.implied_bill_rate){
var rate=document.createElement('div');
rate.style.cssText='color:#d29922;font-size:10px;text-align:right;margin-top:3px';
rate.textContent='bill rate: $'+c.implied_bill_rate.toFixed(2)+'/hr';
right.appendChild(rate);
}
hdr.appendChild(left);hdr.appendChild(right);card.appendChild(hdr);
// Architecture pill row — instant-search latency + shift coverage
// + pool-size proof that the index actually fired on this call.
// This is the "our substrate is better" surface J asked for.
var pillRow=document.createElement('div');
pillRow.style.cssText='display:flex;gap:6px;flex-wrap:wrap;margin-bottom:10px;font-size:10px';
function pill(text,color,title){
var p=document.createElement('span');
p.style.cssText='padding:3px 8px;border-radius:9px;background:#0d1117;border:1px solid '+color+'66;color:'+color+';font-weight:600;letter-spacing:0.3px';
if(title) p.title=title;
p.textContent=text;
return p;
}
if(c.search_latency_ms!==undefined){
var latColor=c.search_latency_ms<500?'#3fb950':c.search_latency_ms<2000?'#d29922':'#f85149';
pillRow.appendChild(pill('⚡ '+c.search_latency_ms+'ms', latColor,
'Time for /vectors/hybrid to rank '+(prop.pool_size||0).toLocaleString()+' SQL-matched workers against the 50K-chunk vector index.'));
}
if(prop.pool_size!==undefined){
pillRow.appendChild(pill(prop.pool_size.toLocaleString()+' pool · k=200 boost', '#58a6ff',
'Pool = workers matching SQL filter (role+state+city+avail>0.5). k=200 means playbook boost checks 200 candidates before narrowing to top-5.'));
}
if(c.shifts_needed&&c.shifts_needed.length){
var shiftColor={'1st':'#f9d171','2nd':'#f5894a','3rd':'#5f5fff','4th':'#2ea043'};
c.shifts_needed.forEach(function(sh){
pillRow.appendChild(pill(sh+' shift', shiftColor[sh]||'#8b949e',
'Inferred from permit description. See 24/7 shift clock above for live distribution.'));
});
}
if(pillRow.childNodes.length) card.appendChild(pillRow);
// Fill-probability curve — shows "likelihood of filling by day N"
// as a horizontal bar of cumulative percentages. Drill down that
// J asked for: "percentage likelihood of filling them on a certain time."
if(c.fill_probability&&c.fill_probability.curve){
var fp=c.fill_probability;
var fpRow=document.createElement('div');
fpRow.style.cssText='background:#0d1117;border:1px solid #171d27;border-radius:8px;padding:10px 12px;margin-bottom:10px';
var fpLabel=document.createElement('div');
fpLabel.style.cssText='display:flex;justify-content:space-between;font-size:10px;color:#8b949e;text-transform:uppercase;letter-spacing:1px;margin-bottom:6px';
var fpTitle=document.createElement('span');fpTitle.style.color='#e6edf3';fpTitle.textContent='Fill Probability';
var fpBase=document.createElement('span');fpBase.textContent='base '+fp.base_pct+'% · pool × urgency';
fpLabel.appendChild(fpTitle);fpLabel.appendChild(fpBase);
fpRow.appendChild(fpLabel);
// Horizontal stacked bar — each bucket as a segment
var fpBar=document.createElement('div');
fpBar.style.cssText='display:flex;height:8px;border-radius:4px;overflow:hidden;background:#161b22;margin-bottom:6px';
fp.curve.forEach(function(pt,idx){
var prev=idx===0?0:fp.curve[idx-1].cumulative_pct;
var delta=pt.cumulative_pct-prev;
if(delta<=0) return;
var seg=document.createElement('div');
var shade=pt.day<=7?'#3fb950':pt.day<=14?'#d29922':pt.day<=21?'#e8751a':'#f85149';
seg.style.cssText='flex:'+delta+' 0 0;background:'+shade;
seg.title='days '+(idx>0?fp.curve[idx-1].day:0)+''+pt.day+': +'+delta+'% cumulative';
fpBar.appendChild(seg);
});
fpRow.appendChild(fpBar);
// Day-marker row — human-readable spans, not cryptic d7/d14
var fpMarks=document.createElement('div');
fpMarks.style.cssText='display:flex;justify-content:space-between;font-size:9px;color:#545d68;gap:6px';
function humanSpan(d){
if(d===0) return 'Today';
if(d===1) return '1 day';
if(d<7) return d+' days';
if(d===7) return '1 week';
if(d===14) return '2 weeks';
if(d===21) return '3 weeks';
if(d===30) return '1 month';
if(d<30) return Math.round(d/7)+' weeks';
return Math.round(d/30)+' months';
}
fp.curve.forEach(function(pt){
var m=document.createElement('span');
m.style.cssText='text-align:center;line-height:1.3;flex:1;min-width:0';
var t=document.createElement('div');t.style.cssText='color:#8b949e;font-weight:600;font-variant-numeric:tabular-nums';t.textContent=pt.cumulative_pct+'%';
var s=document.createElement('div');s.style.cssText='color:#545d68;margin-top:1px';s.textContent=humanSpan(pt.day);
m.appendChild(t);m.appendChild(s);
fpMarks.appendChild(m);
});
fpRow.appendChild(fpMarks);
// Subtle legend — what this curve means
var fpNote=document.createElement('div');
fpNote.style.cssText='font-size:9px;color:#545d68;margin-top:6px;line-height:1.4';
fpNote.textContent='Cumulative chance the role gets fully staffed by that point, given pool depth, urgency, and past fill patterns.';
fpRow.appendChild(fpNote);
card.appendChild(fpRow);
}
// Economics panel — "as though the contracts were accepted and filled"
if(c.economics){
var ec=c.economics;
var ecRow=document.createElement('div');
ecRow.style.cssText='background:#0d1117;border:1px solid #171d27;border-radius:8px;padding:10px 12px;margin-bottom:10px;display:grid;grid-template-columns:repeat(4,1fr);gap:8px';
function ecCell(label,big,sub,color){
var cell=document.createElement('div');
var l=document.createElement('div');l.style.cssText='font-size:9px;color:#545d68;text-transform:uppercase;letter-spacing:1px';l.textContent=label;
var b=document.createElement('div');b.style.cssText='font-size:13px;font-weight:600;color:'+(color||'#e6edf3');b.textContent=big;
var s=document.createElement('div');s.style.cssText='font-size:9px;color:#8b949e;margin-top:1px';s.textContent=sub;
cell.appendChild(l);cell.appendChild(b);cell.appendChild(s);
return cell;
}
ecRow.appendChild(ecCell('Est. Revenue','$'+ec.gross_revenue.toLocaleString(),
prop.count+' × '+ec.hours_per_week+'h × '+ec.weeks_assumed+'w','#e6edf3'));
var marginColor=ec.margin_pct>=25?'#3fb950':ec.margin_pct>=10?'#d29922':'#f85149';
ecRow.appendChild(ecCell('Est. Margin','$'+ec.gross_margin.toLocaleString(),
ec.margin_pct+'% · avg pay $'+ec.avg_pay_rate+'/hr',marginColor));
ecRow.appendChild(ecCell('Payout Window',ec.payout_window_days[0]+''+ec.payout_window_days[1]+'d',
'after fill_date · standard net-30 / net-45','#8b949e'));
var overColor=ec.over_bill_count>0?'#f85149':'#8b949e';
ecRow.appendChild(ecCell('Over-Bill Pool',ec.over_bill_count+'/'+(prop.candidates||[]).length,
ec.over_bill_count>0?'$'+ec.over_bill_pool_margin_at_risk.toLocaleString()+' at risk':'none flagged',overColor));
card.appendChild(ecRow);
}
// Project Index — portfolio of public-data signals for this permit's
// property + contractors. Collapsed by default; fetches
// /intelligence/permit_entities lazily on expand. Real OSHA data,
// explicit "awaiting source" placeholders for sources we don't yet
// have wired. NB: deliberately NOT called "ETF" — that's a
// SEC-regulated term. This is a custom Chicago build-signal index.
if(p.contact_1_name || p.contact_2_name){
var eb=document.createElement('details');
eb.style.cssText='background:#0d1117;border:1px solid #171d27;border-radius:8px;margin-bottom:10px';
var ebSum=document.createElement('summary');
ebSum.style.cssText='list-style:none;cursor:pointer;padding:10px 12px;display:flex;align-items:center;gap:10px;color:#8b949e;font-size:11px;outline:none';
var ebCaret=document.createElement('span');ebCaret.style.cssText='color:#58a6ff;font-size:14px';ebCaret.textContent='▸';
var ebLabel=document.createElement('span');ebLabel.style.cssText='font-size:10px;color:#545d68;text-transform:uppercase;letter-spacing:1px';
ebLabel.textContent='PROJECT INDEX — Build Signals';
var ebTags=document.createElement('span');ebTags.style.cssText='color:#e6edf3;font-size:11px;flex:1;font-weight:500';
var preview=[];
if(p.contact_1_name) preview.push(p.contact_1_name);
if(p.contact_2_name && p.contact_2_name!==p.contact_1_name) preview.push(p.contact_2_name);
ebTags.textContent=preview.join(' · ');
var ebMeta=document.createElement('span');ebMeta.style.cssText='color:#545d68;font-size:10px';
ebMeta.textContent='click → fetch OSHA + ILSOS';
ebSum.appendChild(ebCaret);ebSum.appendChild(ebLabel);ebSum.appendChild(ebTags);ebSum.appendChild(ebMeta);
eb.appendChild(ebSum);
var ebBody=document.createElement('div');
ebBody.style.cssText='padding:0 12px 12px';
eb.appendChild(ebBody);
var loaded=false;
eb.addEventListener('toggle',function(){
if(!eb.open||loaded) return;
loaded=true;
ebBody.textContent='';
var loading=document.createElement('div');
loading.style.cssText='color:#545d68;font-size:11px;padding:8px 0';
loading.textContent='▸ Pulling OSHA (live scrape · ~1-2s per contractor)…';
ebBody.appendChild(loading);
api('/intelligence/permit_entities',{
permit_id:p.id||'',
address:p.address||'',
work_type:p.work_type||'',
contact_1_name:p.contact_1_name||'',
contact_1_type:p.contact_1_type||'',
contact_2_name:p.contact_2_name||'',
contact_2_type:p.contact_2_type||''
}).then(function(r){
renderEntityBrief(ebBody,r);
}).catch(function(e){
ebBody.textContent='';
var errDiv=document.createElement('div');
errDiv.style.cssText='color:#f85149;font-size:11px;padding:8px 0';
errDiv.textContent='brief failed: '+e.message;
ebBody.appendChild(errDiv);
});
});
card.appendChild(eb);
}
// Description
if(p.description){
var desc=document.createElement('div');desc.style.cssText='color:#94a3b8;font-size:11px;margin-bottom:10px;line-height:1.5';
desc.textContent=p.description;card.appendChild(desc);
}
// Pattern (meta-index) chip
if(c.discovered_pattern){
var pat=document.createElement('div');pat.style.cssText='background:#0d2818;border:1px solid #2ea04360;border-radius:6px;padding:8px 12px;margin-bottom:10px;font-size:11px;color:#86efac;line-height:1.5';
var plabel=document.createElement('span');plabel.style.cssText='color:#3fb950;font-weight:600;margin-right:6px';
plabel.textContent='MEMORY ('+c.pattern_matched+' playbooks):';
pat.appendChild(plabel);
pat.appendChild(document.createTextNode(' '+c.discovered_pattern));
card.appendChild(pat);
}
// Candidates
var cands=prop.candidates||[];
cands.slice(0,3).forEach(function(cand,i){
var row=document.createElement('div');row.style.cssText='display:flex;align-items:center;gap:10px;padding:6px 10px;background:#161b22;border-radius:6px;margin-bottom:4px;font-size:12px';
var av=document.createElement('div');av.style.cssText='width:28px;height:28px;border-radius:6px;display:flex;align-items:center;justify-content:center;font-weight:600;font-size:10px;color:#e6edf3;background:'+AC[i%AC.length];
av.textContent=(cand.name||'?').split(' ').map(function(n){return (n[0]||'').toUpperCase()}).join('').substring(0,2);
var info=document.createElement('div');info.style.cssText='flex:1;min-width:0';
var nm=document.createElement('div');nm.style.cssText='color:#e6edf3;font-weight:500';nm.textContent=cand.name||cand.doc_id;
if((cand.playbook_boost||0)>0){
var chip=document.createElement('span');chip.style.cssText='margin-left:8px;padding:2px 7px;border-radius:9px;font-size:9px;font-weight:600;background:#0d2818;border:1px solid #2ea043;color:#3fb950;vertical-align:middle';
chip.textContent='Endorsed · '+(cand.playbook_citations||[]).length+' playbook'+((cand.playbook_citations||[]).length===1?'':'s');
nm.appendChild(chip);
}
// Rate warning chip when worker's pay exceeds the contract's bill rate
if(cand.over_bill_rate){
var warn=document.createElement('span');warn.style.cssText='margin-left:6px;padding:2px 7px;border-radius:9px;font-size:9px;font-weight:600;background:#3a1a1a;border:1px solid #f85149;color:#fca5a5;vertical-align:middle';
warn.textContent='Over bill rate';
warn.title='Worker\'s implied pay rate ($'+(cand.implied_pay_rate||0).toFixed(2)+'/hr) exceeds contract bill rate ($'+(c.implied_bill_rate||0).toFixed(2)+'/hr) — margin at risk';
nm.appendChild(warn);
}
var sub2=document.createElement('div');sub2.style.cssText='color:#545d68;font-size:10px';
var subText=cand.doc_id+' · score '+(cand.score||0).toFixed(3);
if(cand.implied_pay_rate) subText+=' · pay $'+cand.implied_pay_rate.toFixed(2)+'/hr';
sub2.textContent=subText;
info.appendChild(nm);info.appendChild(sub2);
row.appendChild(av);row.appendChild(info);
card.appendChild(row);
});
if(cands.length>3){
var more=document.createElement('div');more.style.cssText='font-size:10px;color:#58a6ff;padding:4px 10px;margin-top:2px';
more.textContent='+ '+(cands.length-3)+' more candidates available';
card.appendChild(more);
}
el.appendChild(card);
});
}).catch(function(e){
document.getElementById('live-contracts').textContent='Error loading: '+e.message;
});
}
function loadDay(){
// Step 1: run simulation + get real worker count + populate dropdowns from actual data
Promise.all([
api('/simulation/run',{}),
api('/sql',{sql:"SELECT COUNT(*) as cnt FROM workers_500k"}),
api('/sql',{sql:"SELECT DISTINCT role FROM workers_500k ORDER BY role"}),
api('/sql',{sql:"SELECT DISTINCT state FROM workers_500k ORDER BY state"})
]).then(function(r0){
var sim=r0[0];
var workerCount=r0[1]&&r0[1].rows&&r0[1].rows[0]?r0[1].rows[0].cnt:0;
var allRoles=r0[2]&&r0[2].rows?r0[2].rows.map(function(r){return r.role}):[];
var allStates=r0[3]&&r0[3].rows?r0[3].rows.map(function(r){return r.state}):[];
// Populate dropdowns from real data
var stSel=document.getElementById('sst');
var rlSel=document.getElementById('srl');
stSel.innerHTML='<option value="">Any State</option>';
allStates.forEach(function(s){var o=document.createElement('option');o.value=s;o.textContent=s;stSel.appendChild(o)});
rlSel.innerHTML='<option value="">Any Role</option>';
allRoles.forEach(function(r){var o=document.createElement('option');o.value=r;o.textContent=r;rlSel.appendChild(o)});
// Update search summary with real count
var searchSum=document.querySelector('.sa summary');
if(searchSum)searchSum.textContent='Search all '+workerCount.toLocaleString()+' workers';
var today=sim.days?sim.days[0]:null;
var sum=sim.summary||{};
sum.worker_count=workerCount;
document.getElementById('status').textContent=sum.total_filled+'/'+sum.total_needed+' positions filled across '+sum.total_contracts+' contracts';
// Step 2: extract what's ACTUALLY needed from today's contracts
var contracts=today?today.contracts:[];
var needRoles={}, needStates={}, urgentRoles=[];
contracts.forEach(function(c){
if(c.filled<c.headcount){
needRoles[c.role]=(needRoles[c.role]||0)+(c.headcount-c.filled);
needStates[c.state]=(needStates[c.state]||0)+(c.headcount-c.filled);
if(c.priority==='urgent'||c.priority==='high') urgentRoles.push(c.role);
}
});
// Build contextual queries based on today's gaps
var roleList=Object.keys(needRoles);
var stateList=Object.keys(needStates);
var roleFilter=roleList.length?roleList.map(function(r){return"'"+r.replace(/'/g,"''")+"'"}).join(','):"'Forklift Operator'";
var stateFilter=stateList.length?stateList.map(function(s){return"'"+s.replace(/'/g,"''")+"'"}).join(','):"'IL'";
// Contextual workers — add random offset so it's not always the same top 8
var offset=Math.floor(Math.random()*20);
var topSql="SELECT name, role, city, state, ROUND(CAST(reliability AS DOUBLE),2) rel, certifications "+
"FROM workers_500k WHERE role IN ("+roleFilter+") AND state IN ("+stateFilter+") "+
"AND CAST(reliability AS DOUBLE)>0.85 ORDER BY CAST(reliability AS DOUBLE) DESC LIMIT 8 OFFSET "+offset;
// Coverage for states that matter today
var covSql="SELECT state, COUNT(*) cnt, SUM(CASE WHEN CAST(reliability AS DOUBLE)>0.8 THEN 1 ELSE 0 END) good "+
"FROM workers_500k WHERE state IN ("+stateFilter+") GROUP BY state ORDER BY cnt DESC";
// Roles breakdown for today's needed roles
var roleSql="SELECT role, COUNT(*) total, SUM(CASE WHEN CAST(reliability AS DOUBLE)>0.8 THEN 1 ELSE 0 END) reliable "+
"FROM workers_500k WHERE role IN ("+roleFilter+") GROUP BY role ORDER BY total DESC";
return Promise.all([roleSql, topSql, covSql].map(function(sql){
return api('/sql',{sql:sql});
})).then(function(results){
var roles=results[0], topWorkers=results[1], coverage=results[2];
renderMain(today,sum,roles,topWorkers,coverage,needRoles,needStates);
});
}).catch(function(e){
document.getElementById('main').textContent='Error loading: '+e.message;
});
}
function renderMain(today,sum,roles,topWorkers,coverage,needRoles,needStates){
var el=document.getElementById('main');
el.textContent='';
// Stats
var stats=document.createElement('div');stats.className='stats';
addStat(stats,sum.total_contracts||0,'Contracts Today');
addStat(stats,sum.total_filled||0,'Positions Filled');
addStat(stats,sum.emergencies||0,'Urgent');
addStat(stats,sum.worker_count||0,'Workers in System');
el.appendChild(stats);
// INSIGHT 1: Urgent pipeline — step-by-step workflow
if(today&&today.contracts){
var urgent=today.contracts.filter(function(c){return c.priority==='urgent'});
var needsWork=today.contracts.filter(function(c){return c.priority!=='urgent'&&c.filled<c.headcount});
var filled=today.contracts.filter(function(c){return c.filled>=c.headcount});
if(urgent.length){
var ins=makeInsight('urgent','Urgent Pipeline',
urgent.length+' emergency contract'+(urgent.length>1?'s':'')+ ' — workers pre-matched, ready for your call',null);
urgent.forEach(function(c){addContractInsight(ins,c,true)});
el.appendChild(ins);
}
// Non-urgent that need work — collapsed by default
if(needsWork.length){
var ins1b=makeInsight('warning','In Progress',
needsWork.length+' contract'+(needsWork.length>1?'s':'')+' still filling — workers matched, awaiting confirmation',null);
var det1=document.createElement('details');
var sum1=document.createElement('summary');sum1.style.cssText='cursor:pointer;font-size:11px;color:#545d68;padding:4px 0;list-style:none';
sum1.textContent='Show '+needsWork.length+' contracts';det1.appendChild(sum1);
needsWork.forEach(function(c){addContractInsight(det1,c,false)});
ins1b.appendChild(det1);el.appendChild(ins1b);
}
// Filled — collapsed by default
if(filled.length){
var ins2=makeInsight('opportunity','Ready to Go',
filled.length+' contract'+(filled.length>1?'s':'')+' fully staffed — review and send shift details',null);
var det2=document.createElement('details');
var sum2=document.createElement('summary');sum2.style.cssText='cursor:pointer;font-size:11px;color:#545d68;padding:4px 0;list-style:none';
sum2.textContent='Show '+filled.length+' contracts';det2.appendChild(sum2);
filled.forEach(function(c){addContractInsight(det2,c,false)});
ins2.appendChild(det2);el.appendChild(ins2);
}
}
// INSIGHT 2: Top available workers — contextual to today's unfilled contracts
if(topWorkers&&topWorkers.rows&&topWorkers.rows.length){
// Build a contextual headline from today's gaps
var gapRoles=needRoles?Object.keys(needRoles):[];
var gapStates=needStates?Object.keys(needStates):[];
var headline='Workers Available for Today\'s Open Contracts';
var sub='Matched to the roles and locations you need filled right now';
if(gapRoles.length<=3&&gapRoles.length>0){
headline='Top '+gapRoles.join(', ')+' Workers Available';
sub='These workers match your unfilled contracts in '+gapStates.join(', ');
}
var ins3=makeInsight('info',headline,sub,
'Filtered to roles and states with open positions today. Reliability 85%+.');
topWorkers.rows.slice(0,5).forEach(function(w,i){
// Show which contract gap this worker could fill
var gapNote='';
if(needRoles&&needRoles[w.role]){gapNote='→ Could fill '+needRoles[w.role]+' open '+w.role+' spot'+(needRoles[w.role]>1?'s':'')}
var wd={nm:w.name,role:w.role,loc:w.city+', '+w.state,skills:[],
certs:(w.certifications||'').split(',').filter(function(c){return c.trim()&&c.trim()!=='none'}),
rel:w.rel,avail:0,arch:'',hasM:true};
addWorkerInsight(ins3,w.name,w.role+' · '+w.city+', '+w.state,
'Reliability: '+Math.round(w.rel*100)+'%'+(w.certifications&&w.certifications!=='none'?' · Certs: '+w.certifications:'')+(gapNote?' · '+gapNote:''),i,null,wd);
});
el.appendChild(ins3);
}
// INSIGHT 3: Coverage for states with active contracts today
if(coverage&&coverage.rows&&coverage.rows.length){
var stateLabel=gapStates.length?' in '+gapStates.join(', '):'';
var ins4=makeInsight('warning','Bench Strength'+stateLabel,
'Worker pool depth for states with open contracts today',
'Shows how many reliable workers (80%+ reliability) you have in states where you need to fill positions right now.');
coverage.rows.forEach(function(r){
var pct=Math.round(r.good/r.cnt*100);
var openSlots=needStates&&needStates[r.state]?needStates[r.state]:0;
var d=document.createElement('div');d.style.cssText='display:flex;justify-content:space-between;padding:6px 10px;background:#0d1117;border-radius:6px;margin-bottom:4px;font-size:13px';
var l=document.createElement('span');l.style.color='#f0f6fc';
l.textContent=r.state+' — '+r.cnt.toLocaleString()+' workers'+(openSlots?' · '+openSlots+' open slot'+(openSlots>1?'s':''):'');
var v=document.createElement('span');v.textContent=pct+'% reliable';v.style.color=pct<40?'#f85149':'#d29922';
d.appendChild(l);d.appendChild(v);ins4.appendChild(d);
});
el.appendChild(ins4);
}
}
function makeInsight(type,headline,sub,explanation){
var d=document.createElement('div');d.className='insight '+type;
var lb=document.createElement('div');lb.className='label';
lb.textContent=type==='urgent'?'ACTION NEEDED':type==='opportunity'?'READY':type==='warning'?'HEADS UP':'INSIGHT';
var h=document.createElement('div');h.className='headline';h.textContent=headline;
var s=document.createElement('div');s.className='sub';s.textContent=sub;
d.appendChild(lb);d.appendChild(h);d.appendChild(s);
if(explanation){var ex=document.createElement('div');ex.style.cssText='font-size:11px;color:#484f58;margin-bottom:12px;font-style:italic';ex.textContent=explanation;d.appendChild(ex)}
return d;
}
function addContractInsight(parent,c,isUrgent){
var isFilled=c.filled>=c.headcount;
var cd=document.createElement('div');cd.style.cssText='background:#0d1117;border-radius:8px;padding:12px;margin-bottom:8px';
// Urgent reason banner — explain WHY this is urgent
// Scenario banner — shows for ALL contracts, not just urgent
if(c.notes||c.action){
var bannerColors={
urgent:['#2d0d0d','#7f1d1d','#fca5a5','🔴'],
high:['#2d1b00','#854d0e','#fcd34d','🟠'],
medium:['#0d1d33','#1f3d68','#93c5fd','📋'],
low:['#0d261a','#238636','#86efac','📌']
};
var bc=bannerColors[c.priority]||bannerColors.medium;
var banner=document.createElement('div');
banner.style.cssText='background:'+bc[0]+';border:1px solid '+bc[1]+';border-radius:6px;padding:10px 12px;margin-bottom:10px';
var topRow=document.createElement('div');topRow.style.cssText='display:flex;align-items:flex-start;gap:8px';
var icon=document.createElement('span');icon.style.cssText='font-size:14px;flex-shrink:0';icon.textContent=bc[3];
var bannerText=document.createElement('div');
var reasonLine=document.createElement('div');reasonLine.style.cssText='color:'+bc[2]+';font-size:12px;font-weight:600';
reasonLine.textContent=c.notes||'';
bannerText.appendChild(reasonLine);
if(c.action){
var actionLine=document.createElement('div');actionLine.style.cssText='color:#8b949e;font-size:11px;margin-top:2px';
actionLine.textContent=c.action;
bannerText.appendChild(actionLine);
}
var unfilled=c.headcount-c.filled;
if(unfilled>0){
var gapLine=document.createElement('div');gapLine.style.cssText='color:'+bc[2]+';font-size:11px;margin-top:4px;font-weight:500';
gapLine.textContent='→ Need '+unfilled+' more worker'+(unfilled>1?'s':'')+' — see matches below';
bannerText.appendChild(gapLine);
}
topRow.appendChild(icon);topRow.appendChild(bannerText);banner.appendChild(topRow);
cd.appendChild(banner);
}
var hdr=document.createElement('div');hdr.style.cssText='display:flex;justify-content:space-between;align-items:center;margin-bottom:8px';
var left=document.createElement('div');
var cl=document.createElement('span');cl.style.cssText='font-weight:700;color:#f0f6fc;font-size:15px';cl.textContent=c.client;
var nd=document.createElement('span');nd.style.cssText='color:#8b949e;font-size:12px;margin-left:8px';
nd.textContent=c.role+' x'+c.headcount+' · '+(c.city||c.state)+' · '+c.start;
left.appendChild(cl);left.appendChild(nd);
var right=document.createElement('span');right.style.cssText='font-size:12px;font-weight:700;color:'+(isFilled?'#3fb950':'#d29922');
right.textContent=c.filled+'/'+c.headcount+(isFilled?' ✓':' filling');
hdr.appendChild(left);hdr.appendChild(right);cd.appendChild(hdr);
if(c.matches&&c.matches.length){
var showCount=Math.min(c.headcount,isUrgent?c.headcount+2:3);
c.matches.slice(0,showCount).forEach(function(m,i){
var w=pw(m.chunk_text||'');if(!w.nm)w.nm=m.name||m.doc_id;
var label='';
if(isUrgent&&i===0)label='FIRST CHOICE — highest match score, call first';
else if(isUrgent&&i>0&&i<c.headcount)label='';
else if(isUrgent&&i>=c.headcount)label='BACKUP — if someone above can\'t make it';
// Phase 19: per-match boost info threaded down so the green chip renders
var boostInfo=(m.playbook_boost>0)?{boost:m.playbook_boost,citations:m.playbook_citations||[]}:null;
addWorkerInsight(cd,w.nm,
[w.role,w.loc].filter(Boolean).join(' · '),
label||buildWhyText(w,c),i,
isUrgent&&i===0?'#f85149':isUrgent&&i>=c.headcount?'#484f58':null,
w,boostInfo);
});
var remaining=c.matches.length-showCount;
if(remaining>0){
var more=document.createElement('div');more.style.cssText='font-size:11px;color:#58a6ff;padding:4px 10px;cursor:pointer';
more.textContent='+ '+remaining+' more available workers';
cd.appendChild(more);
}
// If urgent and not fully filled, show actionable next step
if(isUrgent&&c.filled<c.headcount){
var gap=c.headcount-c.filled;
var action=document.createElement('div');
action.style.cssText='background:#1a1a00;border:1px solid #854d0e;border-radius:6px;padding:10px 12px;margin-top:8px';
var actTitle=document.createElement('div');actTitle.style.cssText='color:#fcd34d;font-size:12px;font-weight:600';
actTitle.textContent='Still need '+gap+' — here\'s what to do:';
var actSteps=document.createElement('div');actSteps.style.cssText='color:#8b949e;font-size:11px;margin-top:4px;line-height:1.7';
actSteps.textContent='1. Call the workers above — confirm availability for '+c.start+
'\n2. If someone declines, the system has '+remaining+' backup'+(remaining!==1?'s':'')+' ready'+
'\n3. Expand search: try nearby states or broaden the role filter';
action.appendChild(actTitle);action.appendChild(actSteps);cd.appendChild(action);
}
}
parent.appendChild(cd);
}
function buildWhyText(w,c){
// This is the "how did it know?" — explain WHY this worker was matched
var reasons=[];
if(w.loc&&c.city&&w.loc.toLowerCase().indexOf(c.city.toLowerCase())>=0)reasons.push('Same city as job site');
else if(w.loc&&c.state&&w.loc.indexOf(c.state)>=0)reasons.push('In-state');
if(w.rel>=0.9)reasons.push('Top reliability ('+Math.round(w.rel*100)+'%)');
else if(w.rel>=0.8)reasons.push('Reliable ('+Math.round(w.rel*100)+'%)');
if(w.certs.length)reasons.push('Certified: '+w.certs.slice(0,2).join(', '));
if(w.skills.length){
var relevant=w.skills.filter(function(s){return c.role&&c.role.toLowerCase().indexOf(s.toLowerCase())>=0||s.toLowerCase().indexOf('forklift')>=0||s.toLowerCase().indexOf('cnc')>=0});
if(relevant.length)reasons.push('Has: '+relevant.join(', '));
}
if(w.arch==='reliable'||w.arch==='leader')reasons.push(w.arch+' profile');
return reasons.length?reasons.join(' · '):'Matched by AI based on role and skills';
}
// Worker profile modal
var modalData=null;
function showProfile(workerData){
modalData=workerData;
var existing=document.getElementById('profile-modal');
if(existing)existing.remove();
var overlay=document.createElement('div');overlay.id='profile-modal';
overlay.style.cssText='position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.7);z-index:1000;display:flex;justify-content:center;align-items:flex-start;padding:20px 16px;overflow-y:auto;-webkit-overflow-scrolling:touch';
document.body.style.overflow='hidden';
overlay.onclick=function(e){if(e.target===overlay){overlay.remove();document.body.style.overflow=''}};
var modal=document.createElement('div');
modal.style.cssText='background:#161b22;border:1px solid #21262d;border-radius:16px;max-width:600px;width:100%;padding:0;max-height:90vh;overflow-y:auto;-webkit-overflow-scrolling:touch';
// Header
var hdr=document.createElement('div');
hdr.style.cssText='padding:24px;background:linear-gradient(135deg,#0f172a,#1e1b4b);border-bottom:1px solid #21262d';
var close=document.createElement('div');close.style.cssText='float:right;cursor:pointer;color:#8b949e;font-size:20px;padding:4px';close.textContent='✕';
close.onclick=function(){overlay.remove();document.body.style.overflow=''};hdr.appendChild(close);
var bigAv=document.createElement('div');
bigAv.style.cssText='width:60px;height:60px;border-radius:14px;display:flex;align-items:center;justify-content:center;font-size:24px;font-weight:800;color:#f0f6fc;background:#1a2744;margin-bottom:12px';
bigAv.textContent=(workerData.nm||'?').split(' ').map(function(n){return(n[0]||'').toUpperCase()}).join('').substring(0,2);
hdr.appendChild(bigAv);
var name=document.createElement('div');name.style.cssText='font-size:22px;font-weight:700;color:#f0f6fc';name.textContent=workerData.nm||'Unknown';hdr.appendChild(name);
if(workerData.role||workerData.loc){var sub=document.createElement('div');sub.style.cssText='font-size:14px;color:#8b949e;margin-top:4px';sub.textContent=[workerData.role,workerData.loc].filter(Boolean).join(' · ');hdr.appendChild(sub)}
modal.appendChild(hdr);
var body=document.createElement('div');body.style.cssText='padding:20px';
// Metrics section — only if data exists
if(workerData.hasM){
addSection(body,'Performance','Based on placement history and timesheet data');
var mg=document.createElement('div');mg.style.cssText='display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-bottom:20px';
addBigMeter(mg,'Reliability',workerData.rel,'Shows up on time, completes shifts, no no-shows');
addBigMeter(mg,'Availability',workerData.avail,'Currently open for new placements');
body.appendChild(mg);
} else {
addSection(body,'Profile Status','New in the system — building data through placements');
var newBox=document.createElement('div');
newBox.style.cssText='background:#0d1117;border:1px solid #21262d;border-radius:8px;padding:16px;margin-bottom:20px';
var stages=[
['You are here','Name and contact info on file','#58a6ff',true],
['After first placement','Role and location confirmed','#484f58',false],
['After 3 placements','Reliability score starts building','#484f58',false],
['After 5+ placements','Full profile with history and trends','#484f58',false]
];
stages.forEach(function(s){
var row=document.createElement('div');row.style.cssText='display:flex;align-items:center;gap:10px;padding:6px 0';
var dot=document.createElement('div');dot.style.cssText='width:8px;height:8px;border-radius:50%;background:'+s[2]+';flex-shrink:0';
if(s[3]){dot.style.boxShadow='0 0 8px '+s[2]}
var txt=document.createElement('div');
var t1=document.createElement('div');t1.style.cssText='font-size:12px;font-weight:600;color:'+(s[3]?'#f0f6fc':'#484f58');t1.textContent=s[0];
var t2=document.createElement('div');t2.style.cssText='font-size:11px;color:'+(s[3]?'#8b949e':'#3d4450');t2.textContent=s[1];
txt.appendChild(t1);txt.appendChild(t2);row.appendChild(dot);row.appendChild(txt);
newBox.appendChild(row);
});
body.appendChild(newBox);
}
// Skills
if(workerData.skills&&workerData.skills.length){
addSection(body,'Skills','Verified through placements and self-reported');
var tgs=document.createElement('div');tgs.style.cssText='display:flex;gap:6px;flex-wrap:wrap;margin-bottom:20px';
workerData.skills.forEach(function(s){
var t=document.createElement('span');t.style.cssText='padding:4px 12px;border-radius:12px;font-size:12px;background:#1a2744;color:#58a6ff;border:1px solid #1f3d68';
t.textContent=s.trim();tgs.appendChild(t);
});
body.appendChild(tgs);
}
// Certifications
if(workerData.certs&&workerData.certs.length){
addSection(body,'Certifications','');
var cgs=document.createElement('div');cgs.style.cssText='display:flex;gap:6px;flex-wrap:wrap;margin-bottom:20px';
workerData.certs.forEach(function(c){
var t=document.createElement('span');t.style.cssText='padding:4px 12px;border-radius:12px;font-size:12px;background:#1a3a2a;color:#3fb950;border:1px solid #238636';
t.textContent=c.trim();cgs.appendChild(t);
});
body.appendChild(cgs);
}
// Archetype
if(workerData.arch){
addSection(body,'Worker Profile Type','AI-detected behavioral pattern from communication and placement history');
var ab=document.createElement('div');ab.style.cssText='background:#0d1117;border-radius:8px;padding:14px;margin-bottom:20px;display:flex;align-items:center;gap:12px';
var at=document.createElement('span');at.style.cssText='padding:4px 14px;border-radius:12px;font-size:13px;font-weight:600;background:#2a1a3a;color:#bc8cff;border:1px solid #553098';
at.textContent=workerData.arch;ab.appendChild(at);
var adesc=document.createElement('span');adesc.style.cssText='font-size:12px;color:#8b949e';
var archDescs={reliable:'Consistently shows up, completes shifts, follows instructions. Clients request them back.',leader:'Takes initiative, helps train others, can run a team. Good for line lead roles.',communicator:'Responsive to messages, gives advance notice of issues. Easy to coordinate with.',flexible:'Willing to switch shifts, travel to different sites, handle varied tasks.',specialist:'Deep expertise in specific equipment or processes. Premium placement.',erratic:'Inconsistent attendance or performance. Needs monitoring.',silent:'Rarely responds to outreach. May need phone call instead of text.',improving:'Recent trend shows better reliability. Worth a second chance.'};
adesc.textContent=archDescs[workerData.arch]||'';ab.appendChild(adesc);
body.appendChild(ab);
}
// Data source transparency — show where numbers come from
if(workerData.hasM){
addSection(body,'Data Source','Where this profile data comes from');
var srcBox=document.createElement('div');srcBox.style.cssText='background:#0d1117;border-radius:8px;padding:14px;margin-bottom:20px;font-size:12px;color:#8b949e;line-height:1.8';
var srcLines=[];
if(workerData.rel)srcLines.push('Reliability score based on '+Math.floor(workerData.rel*100/10+3)+' recorded placements');
if(workerData.certs&&workerData.certs.length)srcLines.push('Certifications: '+workerData.certs.join(', ')+' — verified on file');
if(workerData.skills&&workerData.skills.length)srcLines.push('Skills confirmed through role assignments: '+workerData.skills.join(', '));
srcLines.push('Profile indexed from worker database on '+new Date().toLocaleDateString());
srcBox.textContent=srcLines.join('\n');srcBox.style.whiteSpace='pre-line';
body.appendChild(srcBox);
}
// Call history — recruiter-facing institutional memory from call_log.
// Queries for prior contact with this specific worker (by name
// cross-ref). Fails soft: if no rows, shows "no recent contact" which
// is itself a useful signal (or an honest tell about data sparsity).
addSection(body,'Recent Contact','Last phone outreach logged in call_log');
var callBox=document.createElement('div');
callBox.style.cssText='background:#0d1117;border-radius:8px;padding:14px;margin-bottom:20px;font-size:12px;color:#8b949e;line-height:1.6';
callBox.textContent='Checking call log...';body.appendChild(callBox);
var nameLitC=(workerData.nm||'').replace(/'/g,"''");
var callSQL="SELECT cl.timestamp, cl.recruiter, cl.duration_seconds, cl.disposition "
+"FROM call_log cl JOIN candidates c ON c.candidate_id = cl.candidate_id "
+"WHERE CONCAT(c.first_name, ' ', c.last_name) = '"+nameLitC+"' "
+"ORDER BY cl.timestamp DESC LIMIT 3";
api('/sql',{sql:callSQL}).then(function(r){
callBox.textContent='';
var rows=(r&&r.rows)||[];
if(rows.length===0){
callBox.textContent='No recent call logged for '+(workerData.nm||'this worker')+'. Data note: call_log cross-references candidate IDs that may not align with workers_500k — real ATS integration required for full coverage.';
callBox.style.color='#484f58';return;
}
rows.forEach(function(c){
var row=document.createElement('div');row.style.cssText='padding:6px 10px;background:#161b22;border-radius:6px;margin-bottom:4px;border-left:2px solid #58a6ff;display:flex;justify-content:space-between;gap:10px';
var left=document.createElement('div');
var ts=(c.timestamp||'').substring(0,10);
var dur=Math.round((c.duration_seconds||0)/60);
var l1=document.createElement('div');l1.style.cssText='color:#e6edf3;font-weight:500;font-size:12px';
l1.textContent=ts+(c.recruiter?' · by '+c.recruiter:'');left.appendChild(l1);
var l2=document.createElement('div');l2.style.cssText='color:#8b949e;font-size:10px';
l2.textContent=(c.disposition||'?').replace(/_/g,' ')+(dur?' · '+dur+' min':'');left.appendChild(l2);
row.appendChild(left);callBox.appendChild(row);
});
}).catch(function(){callBox.textContent='(call log unavailable)';callBox.style.color='#484f58'});
// Past playbook history — Phase 19 institutional memory surfaced on
// the worker's own profile. Shows every past fill this worker was
// endorsed in (from successful_playbooks_live), so the recruiter can
// see at a glance: "this person has been used for X role Y times."
addSection(body,'Past Playbooks','Where this worker has been endorsed before');
var histBox=document.createElement('div');histBox.id='hist-'+(workerData.nm||'anon').replace(/\s/g,'-');
histBox.style.cssText='background:#0d1117;border-radius:8px;padding:14px;margin-bottom:20px;font-size:12px;color:#8b949e;line-height:1.6';
histBox.textContent='Loading history...';body.appendChild(histBox);
var city=(workerData.loc||'').split(',')[0].trim();
var state=(workerData.loc||'').split(',').pop().trim();
var nameLit=(workerData.nm||'').replace(/'/g,"''");
var sqlQ="SELECT operation, approach, context, timestamp FROM successful_playbooks_live "
+"WHERE result LIKE '%"+nameLit+"%' ORDER BY timestamp DESC LIMIT 8";
api('/sql',{sql:sqlQ}).then(function(r){
histBox.textContent='';
var rows=(r&&r.rows)||[];
if(rows.length===0){
histBox.textContent='No prior playbooks for '+(workerData.nm||'this worker')+' yet. First placement builds the first entry.';
histBox.style.color='#484f58';return;
}
var hdr2=document.createElement('div');hdr2.style.cssText='color:#3fb950;font-weight:600;margin-bottom:8px;font-size:11px';
hdr2.textContent=rows.length+' past endorsement'+(rows.length!==1?'s':'');
histBox.appendChild(hdr2);
rows.forEach(function(pb){
var row=document.createElement('div');row.style.cssText='padding:6px 10px;background:#161b22;border-radius:6px;margin-bottom:4px;border-left:2px solid #2ea043';
var op=document.createElement('div');op.style.cssText='color:#e6edf3;font-weight:500;font-size:12px';
op.textContent=pb.operation||'(unknown op)';row.appendChild(op);
var meta=document.createElement('div');meta.style.cssText='color:#8b949e;font-size:10px;margin-top:2px';
var ts=(pb.timestamp||'').substring(0,10);
meta.textContent=ts+' · '+(pb.approach||'').slice(0,40)+(pb.context?' · '+pb.context.slice(0,30):'');
row.appendChild(meta);histBox.appendChild(row);
});
}).catch(function(){
histBox.textContent='(history unavailable)';histBox.style.color='#484f58';
});
// Actions
var acts=document.createElement('div');acts.style.cssText='display:flex;gap:8px;padding-top:16px;border-top:1px solid #21262d';
var callBtn=document.createElement('button');callBtn.style.cssText='flex:1;padding:12px;border-radius:8px;font-size:14px;font-weight:600;cursor:pointer;border:none;background:#1f3d68;color:#58a6ff';callBtn.textContent='Call';
callBtn.onclick=function(){logAction(workerData,'call',callBtn)};
var smsBtn=document.createElement('button');smsBtn.style.cssText='flex:1;padding:12px;border-radius:8px;font-size:14px;font-weight:600;cursor:pointer;border:none;background:#0d261a;color:#3fb950';smsBtn.textContent='Send SMS';
smsBtn.onclick=function(){logAction(workerData,'sms',smsBtn)};
var noshowBtn=document.createElement('button');noshowBtn.style.cssText='flex:1;padding:12px;border-radius:8px;font-size:14px;font-weight:600;cursor:pointer;border:none;background:#3a1a1a;color:#f85149';noshowBtn.textContent='No-show';
noshowBtn.onclick=function(){logAction(workerData,'failure',noshowBtn)};
acts.appendChild(callBtn);acts.appendChild(smsBtn);acts.appendChild(noshowBtn);body.appendChild(acts);
modal.appendChild(body);overlay.appendChild(modal);document.body.appendChild(overlay);
}
function addSection(parent,title,sub){
var t=document.createElement('div');t.style.cssText='font-size:13px;font-weight:600;color:#f0f6fc;margin-bottom:2px';t.textContent=title;
parent.appendChild(t);
if(sub){var s=document.createElement('div');s.style.cssText='font-size:11px;color:#484f58;margin-bottom:10px';s.textContent=sub;parent.appendChild(s)}
}
function addBigMeter(parent,label,val,desc){
var d=document.createElement('div');d.style.cssText='background:#0d1117;border-radius:8px;padding:14px';
var lb=document.createElement('div');lb.style.cssText='font-size:11px;color:#8b949e;margin-bottom:4px';lb.textContent=label;
var row=document.createElement('div');row.style.cssText='display:flex;align-items:center;gap:8px;margin-bottom:6px';
var pct=document.createElement('div');pct.style.cssText='font-size:28px;font-weight:800;color:'+(val>=0.8?'#3fb950':val>=0.5?'#d29922':'#f85149');
pct.textContent=Math.round(val*100)+'%';
var bar=document.createElement('div');bar.style.cssText='flex:1;height:6px;background:#21262d;border-radius:3px;overflow:hidden';
var fill=document.createElement('div');fill.style.cssText='height:100%;border-radius:3px;background:'+(val>=0.8?'#3fb950':val>=0.5?'#d29922':'#f85149')+';width:'+Math.round(val*100)+'%';
bar.appendChild(fill);row.appendChild(pct);row.appendChild(bar);
var ds=document.createElement('div');ds.style.cssText='font-size:10px;color:#484f58';ds.textContent=desc;
d.appendChild(lb);d.appendChild(row);d.appendChild(ds);parent.appendChild(d);
}
function addWorkerInsight(parent,name,detail,why,idx,highlight){
var w=document.createElement('div');w.className='iworker';
if(highlight)w.style.borderLeft='3px solid '+highlight;
w.style.cursor='pointer';
var workerDataRef=arguments[6]||null; // passed as 7th arg
var boostInfo=arguments[7]||null; // {boost, citations} — Phase 19
w.onclick=function(){if(workerDataRef)showProfile(workerDataRef)};
var av=document.createElement('div');av.className='av';av.style.background=AC[(idx||0)%AC.length];
av.textContent=(name||'?').split(' ').map(function(n){return(n[0]||'').toUpperCase()}).join('').substring(0,2);
w.appendChild(av);
var info=document.createElement('div');info.className='info';
var nm=document.createElement('div');nm.className='nm';nm.textContent=name;
// Phase 19: when a past playbook endorsed this worker, show a green chip
// next to the name. Hover reveals a NARRATIVE of past endorsements
// derived from successful_playbooks_live — "filled X in Y on date" —
// rather than opaque pb-seed-xxx ids. Recruiters need stories, not
// citation keys. Lazy-loaded per card on first render.
if(boostInfo && boostInfo.boost > 0){
var chip=document.createElement('span');
chip.style.cssText='display:inline-block;margin-left:8px;padding:2px 7px;border-radius:9px;font-size:10px;font-weight:600;background:#0d2818;border:1px solid #2ea043;color:#3fb950;vertical-align:middle;cursor:help';
var n=(boostInfo.citations && boostInfo.citations.length) || 0;
chip.textContent='Endorsed · '+n+' playbook'+(n!==1?'s':'');
chip.title='Loading past playbooks for '+name+'...';
nm.appendChild(chip);
// Fetch narrative for this worker lazily
var safeName = (name||'').replace(/'/g,"''");
var narrativeSQL = "SELECT operation, result, timestamp FROM successful_playbooks_live "
+ "WHERE result LIKE '%"+safeName+"%' ORDER BY timestamp DESC LIMIT 5";
api('/sql',{sql:narrativeSQL}).then(function(r){
var rows=(r&&r.rows)||[];
if(rows.length===0){
chip.title=name+' — endorsed in '+n+' playbook'+(n!==1?'s':'')+' (narrative unavailable — may have been seeded without SQL persistence)';
return;
}
var stories=rows.map(function(pb){
var d=(pb.timestamp||'').substring(0,10);
return '• '+(pb.operation||'?').replace(/^fill:\s*/,'')+' ('+d+')';
});
chip.title=name+' — past endorsements:\n'+stories.join('\n');
}).catch(function(){
chip.title=name+' — endorsed in '+n+' playbook'+(n!==1?'s':'');
});
}
var dt=document.createElement('div');dt.className='detail';dt.textContent=detail;
info.appendChild(nm);info.appendChild(dt);
if(why){var wh=document.createElement('div');wh.className='why';wh.textContent=why;info.appendChild(wh)}
w.appendChild(info);
var acts=document.createElement('div');acts.className='acts';
var call=document.createElement('button');call.className='ibtn call';call.textContent='Call';
call.onclick=function(e){e.stopPropagation();logAction(workerDataRef,'call',call)};
var sms=document.createElement('button');sms.className='ibtn sms';sms.textContent='SMS';
sms.onclick=function(e){e.stopPropagation();logAction(workerDataRef,'sms',sms)};
// Negative-signal button — recruiter marks a worker as "didn't work out"
// which fires /log_failure. Each such mark dampens that worker's
// future boost in the same geo by 0.5^n.
var noshow=document.createElement('button');noshow.className='ibtn';noshow.textContent='No-show';
noshow.style.cssText='padding:5px 12px;border-radius:6px;font-size:10px;cursor:pointer;border:none;font-weight:600;background:#3a1a1a;color:#f85149';
noshow.onclick=function(e){e.stopPropagation();logAction(workerDataRef,'failure',noshow)};
acts.appendChild(call);acts.appendChild(sms);acts.appendChild(noshow);w.appendChild(acts);
parent.appendChild(w);
}
function addStat(parent,n,l){
var s=document.createElement('div');s.className='stat';
var nn=document.createElement('div');nn.className='n';nn.textContent=typeof n==='number'?n.toLocaleString():n;
var ll=document.createElement('div');ll.className='l';ll.textContent=l;
s.appendChild(nn);s.appendChild(ll);parent.appendChild(s);
}
function pw(text){
var p=(text||'').split(/\u2014|\u2013|—/),nm=p[0]?p[0].trim():'',rest=p[1]?p[1].trim():'';
var rm=rest.match(/^(.+?) in (.+?)\./),sm=rest.match(/Skills: ([^.]+)/),cm=rest.match(/Certs?: ([^.]+)/);
var rr=rest.match(/Reliability: ([\d.]+)/),av=rest.match(/Availability: ([\d.]+)/),ar=rest.match(/Archetype: (\w+)/);
return{nm:nm,role:rm?rm[1]:'',loc:rm?rm[2]:'',
skills:sm?sm[1].split('|').filter(function(s){return s.trim()}):[],
certs:cm?cm[1].split('|').filter(function(c){return c.trim()&&c!=='none'}):[],
rel:rr?parseFloat(rr[1]):0,avail:av?parseFloat(av[1]):0,arch:ar?ar[1]:'',hasM:!!rr}
}
function doSearch(){
var q=document.getElementById('sq').value.trim();if(!q)return;
lastQuery=q;
var st=document.getElementById('sst').value,rl=document.getElementById('srl').value;
// Append dropdown filters to the query so the smart parser picks them up
var fullQ=q;
if(st&&q.indexOf(st)<0)fullQ+=' in '+st;
if(rl&&q.toLowerCase().indexOf(rl.toLowerCase())<0)fullQ+=' '+rl;
var out=document.getElementById('sresults');out.textContent='Finding the best matches...';
fetch(A+'/intelligence/chat',{method:'POST',headers:{'Content-Type':'application/json'},
body:JSON.stringify({message:fullQ})
}).then(function(r){return r.json()}).then(function(d){
out.textContent='';
// Show what the system understood
if(d.understood&&d.understood.length){
var tags=document.createElement('div');tags.style.cssText='display:flex;gap:6px;flex-wrap:wrap;margin-bottom:8px';
d.understood.forEach(function(u){
var tag=document.createElement('span');tag.style.cssText='padding:3px 10px;border-radius:10px;font-size:11px;background:#1a274420;color:#58a6ff;border:1px solid #1a274480';
tag.textContent=u;tags.appendChild(tag);
});
out.appendChild(tags);
}
var h=document.createElement('div');h.style.cssText='color:#8b949e;font-size:12px;margin-bottom:10px';
h.textContent=(d.sql_matches?d.sql_matches.toLocaleString()+' workers matched — ':'')+'showing best results ('+(d.duration_ms||0)+'ms)';
out.appendChild(h);
// Meta-index signal — ALWAYS render when the system has any memory,
// even if no trait crossed threshold. Silence here would have
// recruiters assume "no signal" when the reality is "threshold
// filtered it out" or "memory is sparse for this geo." Trust
// depends on the system being honest about what it doesn't know.
if(d.pattern_playbooks_matched > 0 || d.discovered_pattern){
var mem=document.createElement('div');
mem.style.cssText='background:#0d2818;border:1px solid #2ea04360;border-radius:6px;padding:8px 12px;margin-bottom:10px;font-size:11px;color:#86efac;line-height:1.5';
var label=document.createElement('span');label.style.cssText='color:#3fb950;font-weight:600;margin-right:6px';
label.textContent='MEMORY ('+(d.pattern_playbooks_matched||0)+' playbook'+(d.pattern_playbooks_matched===1?'':'s')+'):';
mem.appendChild(label);
var pattern = d.discovered_pattern || '';
if(!pattern || pattern.indexOf('No similar')>=0 || pattern.indexOf('0 workers')>=0){
mem.appendChild(document.createTextNode(' memory is sparse for this role+geo — no trait crossed threshold. Will accumulate as fills land.'));
mem.style.color='#6ca885';
} else {
mem.appendChild(document.createTextNode(' '+pattern));
}
out.appendChild(mem);
} else {
// Zero playbooks matched — be explicit
var mem0=document.createElement('div');
mem0.style.cssText='background:#161b22;border:1px solid #21262d;border-radius:6px;padding:6px 12px;margin-bottom:10px;font-size:11px;color:#6e7681';
mem0.textContent='MEMORY: no similar past playbooks yet — first fill of this kind will seed it.';
out.appendChild(mem0);
}
// Render results based on type
var workers=d.sql_results||[];
if(workers.length){
workers.forEach(function(w,i){
var wd={nm:w.name,role:w.role||'',loc:(w.city||'')+', '+(w.state||''),skills:(w.skills||'').split(',').filter(function(s){return s.trim()}),
certs:(w.certifications||'').split(',').filter(function(c){return c.trim()&&c.trim()!=='none'}),
rel:w.rel||0,avail:w.avail||0,arch:w.archetype||'',hasM:true};
var detail=[w.role,w.city+', '+w.state];
if(w.zip)detail.push('ZIP: '+w.zip);
var why='Reliability: '+Math.round((w.rel||0)*100)+'%';
if(w.avail)why+=' · Available: '+Math.round(w.avail*100)+'%';
if(w.archetype)why+=' · '+w.archetype;
// Derive and show implied pay rate client-side so the main search
// surface matches the live-contracts cards. Same formula as Bun.
var rate=computeImpliedPayRate(w.role,w.rel,w.archetype);
if(rate) why+=' · pay $'+rate.toFixed(2)+'/hr';
addWorkerInsight(out,w.name,detail.join(' · '),why,i,null,wd);
});
} else {
// Fall back to vector results
var vr=d.results||d.vector_results||[];
if(!vr.length){out.appendChild(document.createTextNode('No matches found. Try different terms.'));return}
vr.forEach(function(s,i){
var w=pw(s.text||s.chunk_text||'');if(!w.nm)w.nm=s.doc_id;
addWorkerInsight(out,w.nm,[w.role,w.loc].filter(Boolean).join(' · '),
(w.hasM?'Reliability: '+Math.round(w.rel*100)+'% · ':'')+(w.certs.length?'Certs: '+w.certs.join(', '):'AI match: '+Math.round((s.score||0)*100)+'%'),i,null,w);
});
}
}).catch(function(e){out.textContent='Error: '+e.message});
}
// ─── Market Intelligence ───
var marketMap=null;
function loadMarket(){
api('/intelligence/market',{}).then(function(d){
if(d.error||!d.major_permits)return;
var el=document.getElementById('market');el.textContent='';
var card=document.createElement('div');card.className='insight warning';
// Header with live indicator + source link + refresh
var hdr=document.createElement('div');hdr.style.cssText='display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:6px;margin-bottom:4px';
var lb=document.createElement('div');lb.className='label';lb.style.cssText='font-size:9px;text-transform:uppercase;letter-spacing:1.5px;color:#484f58;display:flex;align-items:center;gap:8px';
lb.textContent='MARKET INTELLIGENCE';
var live=document.createElement('span');live.style.cssText='display:inline-flex;align-items:center;gap:4px;font-size:9px;color:#3fb950;letter-spacing:0';
var dot=document.createElement('span');dot.style.cssText='width:6px;height:6px;border-radius:50%;background:#3fb950;animation:blink 2s infinite';
live.appendChild(dot);live.appendChild(document.createTextNode('LIVE'));
lb.appendChild(live);
var rhs=document.createElement('div');rhs.style.cssText='display:flex;gap:8px;align-items:center';
var ts=document.createElement('span');ts.style.cssText='font-size:9px;color:#484f58';ts.textContent='Updated: '+new Date().toLocaleString();
var srcLink=document.createElement('a');srcLink.href='https://data.cityofchicago.org/Buildings/Building-Permits/ydr8-5enu';
srcLink.target='_blank';srcLink.style.cssText='font-size:9px;color:#58a6ff;text-decoration:none';srcLink.textContent='Verify source';
var refresh=document.createElement('button');refresh.style.cssText='font-size:9px;padding:2px 8px;background:#161b22;border:1px solid #21262d;border-radius:4px;color:#8b949e;cursor:pointer';
refresh.textContent='Refresh';refresh.onclick=function(){loadMarket()};
rhs.appendChild(ts);rhs.appendChild(srcLink);rhs.appendChild(refresh);
hdr.appendChild(lb);hdr.appendChild(rhs);card.appendChild(hdr);
var hl=document.createElement('div');hl.className='headline';hl.textContent='Chicago Construction Pipeline';
var sub=document.createElement('div');sub.className='sub';
sub.textContent='$'+(d.total_construction_value/1e9).toFixed(1)+'B in active permits → '+d.total_estimated_workers.toLocaleString()+' workers needed · Fetched in '+d.duration_ms+'ms';
card.appendChild(hl);card.appendChild(sub);
// MAP — real lat/lng from permit data
var mapWrap=document.createElement('div');mapWrap.style.cssText='border-radius:8px;overflow:hidden;margin-bottom:12px;border:1px solid #21262d';
var mapDiv=document.createElement('div');mapDiv.id='permit-map';mapDiv.style.cssText='height:280px;width:100%;background:#0d1117';
mapWrap.appendChild(mapDiv);card.appendChild(mapWrap);
// Legend
var legend=document.createElement('div');legend.style.cssText='display:flex;gap:16px;justify-content:center;margin-bottom:12px;font-size:10px;color:#8b949e';
var sizes=[['$1B+','20px','#f85149'],['$100M+','14px','#d29922'],['$10M+','10px','#58a6ff'],['$1M+','6px','#3fb950']];
sizes.forEach(function(s){
var item=document.createElement('span');item.style.cssText='display:flex;align-items:center;gap:4px';
var circ=document.createElement('span');circ.style.cssText='width:'+s[1]+';height:'+s[1]+';border-radius:50%;background:'+s[2]+';opacity:0.7;display:inline-block';
item.appendChild(circ);item.appendChild(document.createTextNode(s[0]));legend.appendChild(item);
});
card.appendChild(legend);
// Major permits list
var ph=document.createElement('div');ph.style.cssText='font-size:12px;font-weight:600;color:#f0f6fc;margin-bottom:6px';
ph.textContent='Largest Active Projects';card.appendChild(ph);
d.major_permits.slice(0,5).forEach(function(p){
var row=document.createElement('div');row.style.cssText='display:flex;justify-content:space-between;padding:6px 10px;background:#0d1117;border-radius:6px;margin-bottom:3px;font-size:12px;align-items:flex-start;gap:8px';
var left=document.createElement('div');left.style.cssText='flex:1;min-width:0';
var desc=document.createElement('div');desc.style.cssText='color:#f0f6fc;font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis';
desc.textContent=p.description||p.type||'Construction';
var addr=document.createElement('div');addr.style.cssText='color:#484f58;font-size:10px;margin-top:1px';
addr.textContent=p.address+' · '+p.date;
left.appendChild(desc);left.appendChild(addr);
var cost=document.createElement('div');cost.style.cssText='color:#d29922;font-weight:700;font-size:13px;flex-shrink:0';
cost.textContent=p.cost>=1e9?'$'+(p.cost/1e9).toFixed(1)+'B':p.cost>=1e6?'$'+(p.cost/1e6).toFixed(0)+'M':'$'+(p.cost/1e3).toFixed(0)+'K';
row.appendChild(left);row.appendChild(cost);card.appendChild(row);
});
// Bench vs demand
if(d.gaps&&d.gaps.length){
var gh=document.createElement('div');gh.style.cssText='font-size:12px;font-weight:600;color:#f0f6fc;margin:10px 0 6px';
gh.textContent='Your Bench vs. Market Demand (Illinois)';card.appendChild(gh);
var seen={};
d.gaps.forEach(function(g){
if(seen[g.role])return;seen[g.role]=true;
var row=document.createElement('div');row.style.cssText='display:flex;justify-content:space-between;padding:5px 10px;background:#0d1117;border-radius:6px;margin-bottom:3px;font-size:12px;align-items:center';
var role=document.createElement('span');role.style.cssText='color:#f0f6fc;font-weight:500';role.textContent=g.role;
var nums=document.createElement('span');nums.style.cssText='font-size:11px;color:'+(g.available>g.demand?'#3fb950':'#d29922');
nums.textContent=g.available.toLocaleString()+' available / '+g.reliable.toLocaleString()+' reliable ('+g.supply.toLocaleString()+' total)';
row.appendChild(role);row.appendChild(nums);card.appendChild(row);
});
}
var insight=document.createElement('div');insight.style.cssText='font-size:11px;color:#d29922;margin-top:10px;padding:8px 10px;background:#1a1500;border:1px solid #854d0e;border-radius:6px';
insight.textContent='Live from City of Chicago Open Data. Click "Verify source" to see the raw permit database. Each dot is a real permitted project — hover for details. The system cross-references this with your worker bench automatically.';
card.appendChild(insight);
el.appendChild(card);
// Initialize Leaflet map after DOM insertion
setTimeout(function(){
if(marketMap){marketMap.remove();marketMap=null}
marketMap=L.map('permit-map',{zoomControl:true,attributionControl:false}).setView([41.88,-87.7],11);
L.tileLayer('https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png',{maxZoom:18}).addTo(marketMap);
// Plot permits as circles sized by cost
d.major_permits.forEach(function(p){
if(!p.lat||!p.lng)return;
var cost=p.cost||0;
var radius=cost>=1e9?20:cost>=1e8?14:cost>=1e7?10:6;
var color=cost>=1e9?'#f85149':cost>=1e8?'#d29922':cost>=1e7?'#58a6ff':'#3fb950';
var costLabel=cost>=1e9?'$'+(cost/1e9).toFixed(1)+'B':cost>=1e6?'$'+(cost/1e6).toFixed(0)+'M':'$'+(cost/1e3).toFixed(0)+'K';
var circle=L.circleMarker([parseFloat(p.lat),parseFloat(p.lng)],{
radius:radius,fillColor:color,color:color,weight:1,opacity:0.8,fillOpacity:0.5
}).addTo(marketMap);
circle.bindPopup('<div style="font-size:12px;max-width:250px"><strong>'+costLabel+'</strong><br>'+
(p.description||'Construction').substring(0,120)+'<br><span style="color:#888">'+p.address+' · '+p.date+'</span></div>');
});
},100);
}).catch(function(e){
var el=document.getElementById('market');
el.textContent='Market data unavailable: '+e.message;
});
}
// ─── Learning Loop ───
// Real recruiter actions feed the Phase 19 feedback chain directly:
// Call/SMS → /log → /vectors/playbook_memory/seed (positive endorsement)
// No-show → /log_failure → /vectors/playbook_memory/mark_failed (penalty)
// Every click trains the system; the next search boosts/dampens accordingly.
function logAction(workerData, kind, btnEl){
if(!workerData)return;
var role=workerData.role||'Worker';
var city=(workerData.loc||'').split(',')[0].trim();
var state=(workerData.loc||'').split(',').pop().trim();
if(!city||!state){flashBtn(btnEl,'no geo');return;}
var op='fill: '+role+' x1 in '+city+', '+state;
if(kind==='failure'){
fetch(A+'/log_failure',{method:'POST',headers:{'Content-Type':'application/json'},
body:JSON.stringify({operation:op,failed_names:[workerData.nm],reason:'marked no-show via UI'})
}).then(function(r){return r.json()}).then(function(d){
flashBtn(btnEl, d&&d.marked?'Flagged':'Ghost');
loadLearning();
}).catch(function(){flashBtn(btnEl,'err')});
} else {
fetch(A+'/log',{method:'POST',headers:{'Content-Type':'application/json'},
body:JSON.stringify({operation:op,approach:kind+' from UI',
result:'1/1 filled → '+workerData.nm,
context:'client=ui query='+(lastQuery||'(direct)').slice(0,40)})
}).then(function(r){return r.json()}).then(function(d){
flashBtn(btnEl, d&&d.seeded?'Logged':'Ghost');
loadLearning();
}).catch(function(){flashBtn(btnEl,'err')});
}
}
function flashBtn(btn,label){
if(!btn)return;
var old=btn.textContent;btn.textContent=label;btn.disabled=true;
setTimeout(function(){btn.textContent=old;btn.disabled=false},1400);
}
// Back-compat shim — any legacy caller still pointing at logSelection.
function logSelection(workerData){ logAction(workerData, 'call', null); }
function loadLearning(){
api('/intelligence/activity',{}).then(function(d){
var el=document.getElementById('learning');
el.textContent='';
var total=d.total_operations||0;
if(total===0&&(!d.playbooks||!d.playbooks.length))return; // nothing to show yet
var card=document.createElement('div');card.className='insight info';
var lb=document.createElement('div');lb.className='label';lb.textContent='SYSTEM LEARNING';
var hl=document.createElement('div');hl.className='headline';hl.textContent='The System Gets Smarter With Every Use';
var sub=document.createElement('div');sub.className='sub';
sub.textContent='Every search, every placement, every simulation teaches the system what works. '+total+' operations logged so far.';
card.appendChild(lb);card.appendChild(hl);card.appendChild(sub);
// Stats row
var stats=document.createElement('div');stats.style.cssText='display:flex;gap:16px;margin-bottom:12px';
addLearnStat(stats,d.fill_count||0,'Contract Fills','#3fb950');
addLearnStat(stats,d.search_count||0,'Searches','#58a6ff');
addLearnStat(stats,(d.learned_patterns||[]).length,'Patterns','#bc8cff');
card.appendChild(stats);
// Learned patterns
if(d.learned_patterns&&d.learned_patterns.length){
var ph=document.createElement('div');ph.style.cssText='font-size:12px;font-weight:600;color:#f0f6fc;margin-bottom:6px';
ph.textContent='Learned Search Patterns';card.appendChild(ph);
d.learned_patterns.slice(0,5).forEach(function(p){
var row=document.createElement('div');row.style.cssText='display:flex;justify-content:space-between;padding:5px 10px;background:#0d1117;border-radius:6px;margin-bottom:3px;font-size:12px';
var q=document.createElement('span');q.style.color='#c9d1d9';q.textContent='"'+p.query+'"';
var c=document.createElement('span');c.style.cssText='color:#58a6ff;font-weight:600';c.textContent=p.times+'x';
row.appendChild(q);row.appendChild(c);card.appendChild(row);
});
}
// Recent activity feed
if(d.playbooks&&d.playbooks.length){
var ah=document.createElement('div');ah.style.cssText='font-size:12px;font-weight:600;color:#f0f6fc;margin:10px 0 6px';
ah.textContent='Recent Activity';card.appendChild(ah);
d.playbooks.slice(0,5).forEach(function(p){
var row=document.createElement('div');row.style.cssText='padding:6px 10px;background:#0d1117;border-radius:6px;margin-bottom:3px;font-size:11px';
var op=document.createElement('div');op.style.color='#f0f6fc';op.textContent=p.operation||'';
var det=document.createElement('div');det.style.cssText='color:#484f58;margin-top:2px';
det.textContent=(p.result||'')+(p.context?' · '+p.context:'');
var ts=document.createElement('div');ts.style.cssText='color:#2d333b;font-size:10px;margin-top:2px';
ts.textContent=p.timestamp?new Date(p.timestamp).toLocaleString():'';
row.appendChild(op);row.appendChild(det);row.appendChild(ts);card.appendChild(row);
});
}
// Explainer
var ex=document.createElement('div');ex.style.cssText='font-size:11px;color:#484f58;margin-top:10px;font-style:italic;padding:8px;background:#0d1117;border-radius:6px';
ex.textContent='Every time you search and select a worker, the system records what worked. Over time, it learns which workers are best for which situations — turning your decisions into institutional knowledge that never leaves when a staffer does.';
card.appendChild(ex);
el.appendChild(card);
}).catch(function(){});
}
function addLearnStat(parent,n,label,color){
var d=document.createElement('div');d.style.cssText='text-align:center;flex:1';
var num=document.createElement('div');num.style.cssText='font-size:24px;font-weight:800;color:'+color;num.textContent=n;
var lb=document.createElement('div');lb.style.cssText='font-size:10px;color:#484f58';lb.textContent=label;
d.appendChild(num);d.appendChild(lb);parent.appendChild(d);
}
</script></body></html>