lakehouse/mcp-server/contractor.html
root 31d8ef918c demo: contractor links — respect the /lakehouse path prefix
J reported https://devop.live/contractor?name=3115%20W%20POLK%20ST.%20LLC
returned 404. Cause: the anchor href was a bare /contractor, which on
devop.live routes to the LLM Team UI (port 5000) at the main site root,
not the lakehouse mcp-server (which lives under /lakehouse/*).

Every page that renders a contractor link now uses the same prefix
detector the dashboard already had:

  var P = location.pathname.indexOf('/lakehouse') >= 0 ? '/lakehouse' : '';

Files updated:
- search.html: entity-brief anchor + preview anchor → P+/contractor
- console.html: permit-card contractor list → P+/contractor
- contractor.html: history.replaceState + back-link + the
  /intelligence/contractor_profile fetch all use P prefix. The page
  is reachable at /lakehouse/contractor on the public URL and bare
  /contractor on localhost; both work without further config.

Verified:
  https://devop.live/lakehouse/contractor?name=3115%20W%20POLK%20ST.%20LLC
    → 200, 29.9 KB, full profile renders. Contractor has 1 permit on
    file (a small LLC), 1 geocoded so the heat map plots one marker.
2026-04-28 06:01:04 -05:00

607 lines
29 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

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

<!DOCTYPE html>
<html><head>
<meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
<title>Contractor Profile · 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}
body{font-family:'Inter',-apple-system,system-ui,sans-serif;background:#090c10;color:#b0b8c4;font-size:14px;line-height:1.6}
.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}
.bar a{color:#545d68;text-decoration:none;font-size:12px;padding:6px 14px;border-radius:6px}
.bar a:hover{color:#e6edf3;background:#161b22}
.content{max-width:1100px;margin:0 auto;padding:24px 20px 40px}
.search-box{background:#0d1117;border:1px solid #21262d;border-radius:10px;padding:16px;margin-bottom:24px;display:flex;gap:10px}
.search-box input{flex:1;padding:12px 16px;background:#161b22;border:1px solid #21262d;border-radius:8px;color:#e6edf3;font-size:14px;outline:none}
.search-box input:focus{border-color:#388bfd}
.search-box button{padding:12px 24px;background:#1f6feb;border:none;border-radius:8px;color:#fff;font-weight:600;cursor:pointer}
.hero{background:#0d1117;border:1px solid #171d27;border-radius:12px;padding:24px;margin-bottom:16px}
.hero h2{color:#e6edf3;font-size:22px;font-weight:700;letter-spacing:-0.5px;margin-bottom:6px}
.hero .ticker-row{display:flex;align-items:center;gap:10px;margin-top:10px;flex-wrap:wrap}
.hero .ticker{font-family:ui-monospace,SFMono-Regular,monospace;background:#161b22;padding:4px 10px;border-radius:6px;color:#3fb950;border:1px solid #3fb95066;font-weight:600;font-size:12px}
.hero .meta{font-size:12px;color:#8b949e}
.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(320px,1fr));gap:14px}
.card{background:#0d1117;border:1px solid #171d27;border-radius:10px;padding:16px}
.card h3{font-size:11px;color:#545d68;text-transform:uppercase;letter-spacing:1.2px;margin-bottom:10px;font-weight:600}
.card .big{font-size:24px;font-weight:700;color:#e6edf3;letter-spacing:-0.5px;margin-bottom:4px}
.card .sub{font-size:11px;color:#8b949e;line-height:1.5}
.card a{color:#58a6ff;text-decoration:none;font-size:11px}
.row{display:flex;justify-content:space-between;align-items:baseline;padding:6px 0;border-bottom:1px dashed #1f2631;font-size:11px}
.row:last-child{border:none}
.row .l{color:#8b949e}
.row .v{color:#e6edf3;font-family:ui-monospace,monospace;font-variant-numeric:tabular-nums}
.chip{display:inline-block;padding:3px 8px;border-radius:9px;font-size:10px;font-weight:600;margin-right:6px;margin-bottom:4px}
.ld{color:#3d444d;text-align:center;padding:60px;font-size:13px}
.empty{color:#545d68;font-size:11px;font-style:italic;line-height:1.5}
.wide{grid-column:1/-1}
.heatmap{height:380px;border-radius:8px;border:1px solid #1f2631;overflow:hidden;margin-top:10px}
.heatmap .leaflet-container{background:#0a0d12}
.timeline{margin-top:10px;display:flex;align-items:flex-end;gap:2px;height:80px;padding:6px 0;border-bottom:1px solid #1f2631}
.timeline .tbar{flex:1;background:#1f6feb;min-height:2px;border-radius:2px 2px 0 0;position:relative;cursor:help}
.timeline .tbar:hover{background:#58a6ff}
.timeline-axis{display:flex;justify-content:space-between;font-size:10px;color:#545d68;padding-top:4px;font-family:ui-monospace,monospace}
.placeholder-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:10px;margin-top:14px}
.ph-card{background:#0a0d12;border:1px dashed #21262d;border-radius:8px;padding:12px 14px;position:relative}
.ph-card h4{font-size:11px;color:#8b949e;font-weight:600;margin-bottom:4px;display:flex;align-items:center;gap:6px}
.ph-card h4 .badge{font-size:9px;padding:2px 6px;border-radius:8px;background:#161b22;color:#d29922;border:1px solid #d2992244;font-weight:600;letter-spacing:0.5px;text-transform:uppercase}
.ph-card .why{font-size:11px;color:#e6edf3;line-height:1.5;margin-bottom:6px}
.ph-card .would{font-size:10px;color:#545d68;font-family:ui-monospace,monospace;line-height:1.5;border-top:1px dashed #1f2631;padding-top:6px;margin-top:6px}
.section-label{font-size:10px;color:#545d68;text-transform:uppercase;letter-spacing:1.4px;font-weight:600;margin:24px 0 8px}
@media(max-width:640px){.bar{padding:0 14px}.content{padding:14px}.hero{padding:16px}.hero h2{font-size:18px}.card{padding:12px}}
</style>
</head><body>
<div class="bar">
<h1>Staffing Co-Pilot · Contractor Profile</h1>
<a href="/">← Dashboard</a>
</div>
<div class="content">
<div class="search-box">
<input id="q" type="text" placeholder="Type a contractor name (e.g., Turner Construction Company)" onkeydown="if(event.key==='Enter')lookup()">
<button onclick="lookup()">Look up</button>
</div>
<div id="out"><div class="ld">Type a name above to load the full portfolio across every wired data source.</div></div>
</div>
<script>
function $(id){return document.getElementById(id)}
// Path prefix detection — devop.live serves this page under /lakehouse,
// localhost:3700 serves it at root. URL rewrites must preserve whatever
// prefix the user reached the page through, otherwise the back-link and
// browser refresh break.
var P=location.pathname.indexOf('/lakehouse')>=0?'/lakehouse':'';
// Bootstrap from URL: /contractor?name=Turner+Construction
window.addEventListener('load', function(){
var name = new URLSearchParams(location.search).get('name');
if(name){
$('q').value = name;
lookup();
}
// Back link respects the prefix too
var back=document.querySelector('.bar a');
if(back) back.href=P+'/';
});
function lookup(){
var name = $('q').value.trim();
if(!name){ $('out').textContent = ''; return; }
history.replaceState({}, '', P+'/contractor?name='+encodeURIComponent(name));
var out = $('out');
out.textContent = '';
var ld = document.createElement('div');
ld.className = 'ld';
ld.textContent = 'Pulling OSHA, SEC, Stooq, Chicago history, USASpending… (~5-10s on cold cache)';
out.appendChild(ld);
fetch(P+'/intelligence/contractor_profile',{
method:'POST',
headers:{'Content-Type':'application/json'},
body:JSON.stringify({name:name})
}).then(function(r){return r.json()}).then(function(d){
render(d);
}).catch(function(e){
out.textContent = '';
var err = document.createElement('div');
err.className = 'ld';
err.style.color = '#f85149';
err.textContent = 'profile failed: '+e.message;
out.appendChild(err);
});
}
function render(d){
var out = $('out');
out.textContent = '';
// ─── Hero — name, ticker, parent ─────────────
var hero = document.createElement('div');
hero.className = 'hero';
var h2 = document.createElement('h2');
h2.textContent = d.display_name;
hero.appendChild(h2);
var sub = document.createElement('div');
sub.className = 'meta';
sub.textContent = 'Internal ticker: '+(d.ticker||'?')+' · profile generated '+new Date(d.generated_at).toLocaleTimeString();
hero.appendChild(sub);
var trow = document.createElement('div');
trow.className = 'ticker-row';
// Direct ticker
var s = d.stock;
if(s && s.status==='ok'){
var tk = document.createElement('span');
tk.className = 'ticker';
tk.textContent = s.ticker;
trow.appendChild(tk);
var px = document.createElement('span');
px.className = 'meta';
px.textContent = (s.company_name||'')+(s.exchange?' · '+s.exchange:'')+(s.price?' · $'+s.price.toFixed(2):'');
if(s.day_change_pct!=null && !isNaN(s.day_change_pct)){
var ch = (s.day_change_pct>=0?'+':'')+s.day_change_pct.toFixed(2)+'%';
var chSpan = document.createElement('span');
chSpan.style.color = s.day_change_pct>=0?'#3fb950':'#f85149';
chSpan.style.marginLeft = '6px';
chSpan.textContent = ch;
px.appendChild(chSpan);
}
trow.appendChild(px);
} else {
var noTk = document.createElement('span');
noTk.className = 'meta';
noTk.textContent = 'Private — no direct US ticker';
trow.appendChild(noTk);
}
// Parent link
var pl = d.parent_link;
if(pl && pl.status==='ok'){
var arrow = document.createElement('span');
arrow.className = 'meta';
arrow.style.color = '#545d68';
arrow.textContent = ' → parent ';
trow.appendChild(arrow);
var pTk = document.createElement('span');
pTk.className = 'ticker';
pTk.style.color = '#d29922';
pTk.style.borderColor = '#d2992266';
pTk.textContent = pl.parent_ticker || '?';
pTk.title = pl.link_source || '';
trow.appendChild(pTk);
var pName = document.createElement('span');
pName.className = 'meta';
pName.textContent = pl.parent_name+(pl.parent_exchange?' · '+pl.parent_exchange:'')+(pl.parent_country?' · '+pl.parent_country:'');
trow.appendChild(pName);
} else if(pl && pl.status==='no_link'){
var pp = document.createElement('span');
pp.className = 'meta';
pp.style.fontStyle = 'italic';
pp.textContent = ' · '+(pl.reason||'no public parent identified');
trow.appendChild(pp);
}
hero.appendChild(trow);
out.appendChild(hero);
// ─── Grid of cards ─────────────────────────────
var grid = document.createElement('div');
grid.className = 'grid';
// OSHA
var oCard = card('OSHA SAFETY HISTORY (NATIONAL)');
var osha = d.osha || {};
if(osha.status==='ok'){
big(oCard, osha.inspection_count + ' inspections', 'most recent '+(osha.most_recent_date||'?'));
rowEl(oCard, 'States seen', (osha.states_seen||[]).join(', ') || '?');
rowEl(oCard, 'Most recent', osha.most_recent_date||'?');
if(osha.recent_inspections && osha.recent_inspections.length){
var rep = document.createElement('div');
rep.style.marginTop = '8px';
rep.style.fontSize = '10px';
rep.style.color = '#545d68';
rep.textContent = 'Recent inspections:';
oCard.appendChild(rep);
osha.recent_inspections.slice(0,5).forEach(function(i){
var r = document.createElement('div');
r.style.fontSize = '10px';
r.style.color = '#8b949e';
r.style.fontFamily = 'ui-monospace,monospace';
r.style.padding = '2px 0';
var a = document.createElement('a');
a.href = i.detail_url;
a.target = '_blank';
a.textContent = i.id;
r.appendChild(a);
r.appendChild(document.createTextNode(' · '+i.date+' · '+i.state+' · '+i.type+' · '+i.scope));
oCard.appendChild(r);
});
}
} else if(osha.status==='no_match'){
big(oCard, 'No inspections', 'clean record');
} else {
empty(oCard, 'OSHA fetch error: '+(osha.error||'unknown'));
}
grid.appendChild(oCard);
// Chicago history
var hCard = card('CHICAGO PERMIT HISTORY (24mo + LIFETIME)');
var hist = d.history || {};
if(hist.status==='ok'){
big(hCard, hist.permits_historical_total+' permits all-time',
hist.permits_last_180d+' in last 180d · '+hist.permits_last_24mo+' in 24mo · trend: '+hist.trend);
rowEl(hCard, 'Cost (24mo)', hist.total_cost_last_24mo>=1e6 ? '$'+(hist.total_cost_last_24mo/1e6).toFixed(1)+'M' : '$'+Math.round(hist.total_cost_last_24mo/1e3)+'K');
if(hist.recent_permits && hist.recent_permits.length){
var rh = document.createElement('div');
rh.style.marginTop = '8px';
rh.style.fontSize = '10px';
rh.style.color = '#545d68';
rh.textContent = 'Recent Chicago permits:';
hCard.appendChild(rh);
hist.recent_permits.slice(0,5).forEach(function(p){
var r = document.createElement('div');
r.style.fontSize = '10px';
r.style.color = '#8b949e';
r.style.padding = '2px 0';
r.textContent = '· '+(p.date||'?')+' · '+p.work_type+' · $'+(p.cost||0).toLocaleString()+' · '+p.address;
hCard.appendChild(r);
});
}
} else {
empty(hCard, 'Chicago history error');
}
grid.appendChild(hCard);
// Federal contracts
var fCard = card('FEDERAL CONTRACTS (USASpending.gov)');
var fed = d.federal || {};
if(fed.status==='ok' && fed.total_awards_count>0){
var dollars = fed.total_awards_value>=1e9 ? '$'+(fed.total_awards_value/1e9).toFixed(2)+'B'
: fed.total_awards_value>=1e6 ? '$'+(fed.total_awards_value/1e6).toFixed(1)+'M'
: '$'+Math.round(fed.total_awards_value/1e3)+'K';
big(fCard, dollars, fed.total_awards_count+' awards · most recent '+(fed.most_recent_award_date||'?'));
if(fed.top_agencies && fed.top_agencies.length){
var ta = document.createElement('div');
ta.style.marginTop = '6px';
ta.style.fontSize = '10px';
ta.style.color = '#545d68';
ta.textContent = 'Top awarding agencies:';
fCard.appendChild(ta);
fed.top_agencies.forEach(function(a){
var r = document.createElement('div');
r.style.fontSize = '11px';
r.style.color = '#8b949e';
r.style.padding = '3px 0';
var dollars2 = a.value>=1e6 ? '$'+(a.value/1e6).toFixed(1)+'M' : '$'+Math.round(a.value/1e3)+'K';
r.textContent = '· '+a.agency+' — '+dollars2;
fCard.appendChild(r);
});
}
if(fed.source_url){
var lnk = document.createElement('a');
lnk.href = fed.source_url;
lnk.target = '_blank';
lnk.style.display = 'inline-block';
lnk.style.marginTop = '8px';
lnk.textContent = 'View on usaspending.gov ↗';
fCard.appendChild(lnk);
}
} else if(fed.status==='no_match'){
big(fCard, 'No federal contracts', 'on file under this name');
} else {
empty(fCard, 'usaspending error');
}
grid.appendChild(fCard);
// Debarment + NLRB combined
var rCard = card('DEBARMENT + LABOR ACTIONS');
var deb = d.debarment || {};
var nlrb = d.nlrb || {};
rowEl(rCard, 'SAM.gov excluded', deb.status==='needs_setup' ? 'awaiting API key' : (deb.sam_excluded?'YES':'no'));
rowEl(rCard, 'IDOL debarred', deb.status==='needs_setup' ? 'awaiting scrape' : (deb.idol_debarred?'YES':'no'));
rowEl(rCard, 'NLRB cases', nlrb.status==='needs_setup' ? 'awaiting scrape' : (nlrb.total_cases||0));
if(deb.status==='needs_setup' || nlrb.status==='needs_setup'){
var dn = document.createElement('div');
dn.className = 'empty';
dn.style.marginTop = '8px';
dn.textContent = 'Both sources pending wire-up: '+(deb.reason||nlrb.reason||'');
rCard.appendChild(dn);
}
grid.appendChild(rCard);
// ILSOS
var iCard = card('CORPORATE REGISTRY (Illinois SoS)');
var ilsos = d.ilsos || {};
if(ilsos.status==='source_unreachable'){
rowEl(iCard, 'Status', 'source blocked at our ASN');
var en = document.createElement('div');
en.className = 'empty';
en.style.marginTop = '8px';
en.textContent = ilsos.reason||'';
iCard.appendChild(en);
} else if(ilsos.status==='ok'){
rowEl(iCard, 'Entity name', ilsos.entity_name||'?');
rowEl(iCard, 'File #', ilsos.file_number||'?');
rowEl(iCard, 'Status', ilsos.status_text||'?');
rowEl(iCard, 'Formed', ilsos.formation_date||'?');
rowEl(iCard, 'Registered agent', ilsos.registered_agent||'?');
} else {
empty(iCard, 'no ILSOS data');
}
grid.appendChild(iCard);
out.appendChild(grid);
// ─── Project Index summary — the staffer-facing build-signal score ──
var pixHeader = document.createElement('div');
pixHeader.className = 'section-label';
pixHeader.textContent = '◆ Project Index — build-signal score';
out.appendChild(pixHeader);
var pixCard = document.createElement('div');
pixCard.className = 'card wide';
// Score is a simple weighted blend of the wired signals — designed to
// be replaced with a real model once enough placeholders are wired.
var hist2 = d.history || {};
var pixScore = 0;
var pixDrivers = [];
if(hist2.permits_last_180d){ pixScore += Math.min(hist2.permits_last_180d * 5, 30); pixDrivers.push(hist2.permits_last_180d+' Chicago permits in 180d (+'+Math.min(hist2.permits_last_180d*5,30)+')'); }
if(hist2.trend === 'rising'){ pixScore += 10; pixDrivers.push('permit trend rising (+10)'); }
if(d.osha && d.osha.status==='ok' && d.osha.inspection_count>0){ pixScore -= Math.min(d.osha.inspection_count*5, 25); pixDrivers.push(d.osha.inspection_count+' OSHA inspections (-'+Math.min(d.osha.inspection_count*5,25)+')'); }
if(d.federal && d.federal.status==='ok' && d.federal.total_awards_count>0){ pixScore += 15; pixDrivers.push('federally-vetted contractor (+15)'); }
if(d.debarment && d.debarment.sam_excluded){ pixScore -= 50; pixDrivers.push('SAM.gov excluded (-50)'); }
if(d.stock && d.stock.status==='ok'){ pixScore += 5; pixDrivers.push('public ticker (+5)'); }
pixScore = Math.max(0, Math.min(100, 50 + pixScore));
var pixColor = pixScore >= 70 ? '#3fb950' : pixScore >= 40 ? '#d29922' : '#f85149';
var pixHero = document.createElement('div');
pixHero.style.cssText = 'display:flex;align-items:baseline;gap:14px;margin-bottom:8px';
var pixBig = document.createElement('span');
pixBig.style.cssText = 'font-size:42px;font-weight:700;color:'+pixColor+';letter-spacing:-1px';
pixBig.textContent = pixScore;
pixHero.appendChild(pixBig);
var pixLabel = document.createElement('span');
pixLabel.style.cssText = 'font-size:12px;color:#8b949e';
pixLabel.textContent = pixScore >= 70 ? 'Strong staffing partner — wired signals positive' : pixScore >= 40 ? 'Mixed signals — review drivers below' : 'Caution — wired signals negative';
pixHero.appendChild(pixLabel);
pixCard.appendChild(pixHero);
if(pixDrivers.length){
var pixDrv = document.createElement('div');
pixDrv.style.cssText = 'font-size:11px;color:#8b949e;line-height:1.7;font-family:ui-monospace,monospace';
pixDrv.textContent = pixDrivers.join(' · ');
pixCard.appendChild(pixDrv);
}
var pixFoot = document.createElement('div');
pixFoot.style.cssText = 'font-size:10px;color:#545d68;margin-top:8px;font-style:italic;line-height:1.5';
pixFoot.textContent = 'Score is a placeholder weighted blend of the 6 wired signals above. Real ML model lands once 12 awaiting sources below ship — that gives the index 18 features instead of 6.';
pixCard.appendChild(pixFoot);
out.appendChild(pixCard);
// ─── Heat map — every Chicago permit they're contact_1 or contact_2 on ─
var mapHeader = document.createElement('div');
mapHeader.className = 'section-label';
mapHeader.textContent = '◆ Where they\'ve worked — Chicago permits, last 24 months';
out.appendChild(mapHeader);
var mapCard = document.createElement('div');
mapCard.className = 'card wide';
var mapDiv = document.createElement('div');
mapDiv.className = 'heatmap';
mapDiv.id = 'cmap';
mapCard.appendChild(mapDiv);
var mapHint = document.createElement('div');
mapHint.style.cssText = 'font-size:11px;color:#545d68;margin-top:8px';
mapHint.textContent = 'Loading geo from chicago_permits…';
mapCard.appendChild(mapHint);
out.appendChild(mapCard);
// Plot the recent_permits embedded in the contractor profile (now
// includes lat/lng/permit_id/description per the entity.ts change).
// Color by cost: green <$100K, amber $100K-$1M, red ≥$1M.
var permits = (hist2.recent_permits||[]).filter(function(p){return p.lat&&p.lng});
if(!permits.length){
mapHint.textContent = 'No geocoded permits in the contractor history (Socrata may not have lat/lng for these records).';
} else {
// Construct map only after the div is in the DOM; defer one tick.
setTimeout(function(){
var map = L.map('cmap', {zoomControl:true, attributionControl:false}).setView([41.88,-87.63], 11);
L.tileLayer('https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png',{maxZoom:19}).addTo(map);
var bounds = [];
var costs = permits.map(function(p){return Number(p.cost)||0});
var maxCost = Math.max.apply(null, costs.concat([1]));
permits.forEach(function(p){
var c = Number(p.cost)||0;
var radius = 4 + (c/maxCost)*16;
var color = c >= 1000000 ? '#f85149' : c >= 100000 ? '#d29922' : '#3fb950';
var marker = L.circleMarker([p.lat,p.lng],{radius:radius, color:color, weight:1, fillOpacity:0.55});
// Build popup via DOM (no innerHTML — keeps the XSS hook happy)
var pop = document.createElement('div');
pop.style.cssText = 'font-family:ui-monospace,monospace;font-size:11px;color:#0a0d12;min-width:160px';
var costRow = document.createElement('div');
costRow.style.cssText = 'font-weight:700;margin-bottom:4px';
costRow.textContent = '$'+c.toLocaleString()+' · '+(p.date||'?');
pop.appendChild(costRow);
var wt = document.createElement('div');
wt.textContent = p.work_type||'?';
pop.appendChild(wt);
var addr = document.createElement('div');
addr.style.color = '#545d68';
addr.textContent = p.address||'?';
pop.appendChild(addr);
if(p.permit_id){
var pid = document.createElement('div');
pid.style.cssText = 'color:#545d68;margin-top:4px;font-size:10px';
pid.textContent = 'permit '+p.permit_id;
pop.appendChild(pid);
}
marker.bindPopup(pop);
marker.addTo(map);
bounds.push([p.lat, p.lng]);
});
if(bounds.length>1) map.fitBounds(bounds, {padding:[24,24]});
mapHint.textContent = permits.length+' permits plotted · green <$100K, amber $100K-$1M, red $1M · radius: relative cost';
}, 50);
}
// ─── History timeline — monthly permit volume + cost trend ─────────
if(hist2.recent_permits && hist2.recent_permits.length){
var tlHeader = document.createElement('div');
tlHeader.className = 'section-label';
tlHeader.textContent = ' Activity timeline Chicago permits by month';
out.appendChild(tlHeader);
var tlCard = document.createElement('div');
tlCard.className = 'card wide';
// Bucket by year-month
var buckets = {};
hist2.recent_permits.forEach(function(p){
var d = (p.date||'').substring(0,7); // YYYY-MM
if(!d) return;
buckets[d] = buckets[d] || {count:0, cost:0};
buckets[d].count++;
buckets[d].cost += Number(p.cost)||0;
});
var months = Object.keys(buckets).sort();
if(months.length){
var maxC = Math.max.apply(null, months.map(function(m){return buckets[m].count}));
var tl = document.createElement('div'); tl.className='timeline';
months.forEach(function(m){
var b = buckets[m];
var bar = document.createElement('div'); bar.className='tbar';
bar.style.height = Math.max(2, Math.round(b.count/maxC*72)) + 'px';
bar.title = m+' · '+b.count+' permit'+(b.count===1?'':'s')+' · $'+Math.round(b.cost).toLocaleString();
tl.appendChild(bar);
});
tlCard.appendChild(tl);
var ax = document.createElement('div'); ax.className='timeline-axis';
var first = document.createElement('span'); first.textContent = months[0];
var last = document.createElement('span'); last.textContent = months[months.length-1];
ax.appendChild(first); ax.appendChild(last);
tlCard.appendChild(ax);
}
out.appendChild(tlCard);
}
// ─── 12 awaiting-source placeholders ──────────────────────────────
// Each one names a real public data source that would feed the
// build-signal index, with a one-line "why a staffer cares" framing
// and a sample shape of what the panel would show once wired.
var phHeader = document.createElement('div');
phHeader.className = 'section-label';
phHeader.textContent = ' 12 awaiting sources what plugs in next';
out.appendChild(phHeader);
var phGrid = document.createElement('div');
phGrid.className = 'placeholder-grid';
PLACEHOLDERS.forEach(function(p){
var c = document.createElement('div'); c.className='ph-card';
var h = document.createElement('h4');
var name = document.createElement('span'); name.textContent = p.name;
var badge = document.createElement('span'); badge.className='badge'; badge.textContent='AWAITING';
h.appendChild(name); h.appendChild(badge);
c.appendChild(h);
var why = document.createElement('div'); why.className='why'; why.textContent = p.why;
c.appendChild(why);
var would = document.createElement('div'); would.className='would';
would.textContent = 'Would show: ' + p.would;
c.appendChild(would);
phGrid.appendChild(c);
});
out.appendChild(phGrid);
// Roadmap footer
var foot = document.createElement('div');
foot.style.marginTop = '20px';
foot.style.fontSize = '10px';
foot.style.color = '#484f58';
foot.style.lineHeight = '1.6';
foot.textContent = 'Wired: OSHA Enforcement · SEC EDGAR + Stooq · Chicago Socrata permits (lat/lng) · USASpending.gov · curated parent-ticker map · ILSOS (datacenter ASN blocked). 12 awaiting sources above are real public datasets that would 3× the feature count of the build-signal index each one labeled with the one-liner the staffer would ask before placing a worker.';
out.appendChild(foot);
}
// Twelve real public data sources, framed in coordinator language.
// Each is a placeholder; the panel renders them as "AWAITING" with a
// description of what they'd add once wired. Order is roughly: highest
// staffing-decision relevance first.
var PLACEHOLDERS = [
{
name: 'DOL Wage & Hour (WHD)',
why: 'Has this contractor stiffed workers before? WHD posts every back-wage settlement and unpaid-overtime case.',
would: 'cases last 24mo · total back wages owed · status by state · most recent settlement date · whether the workers got paid',
},
{
name: 'State Licensure Boards',
why: 'Is the contractor legally allowed to do this work today, in this state?',
would: 'license # · status (active / expired / suspended) · trade scope · expiration date · disciplinary history',
},
{
name: 'Surety Bond Capacity',
why: 'How big a job can this contractor actually take? Bond ceiling = upper bound on what they\'re bonded for.',
would: 'bonding company · single-contract ceiling · aggregate cap · current utilization · recent bond denials',
},
{
name: 'EPA ECHO Compliance',
why: 'If a worker shows up to a site with hazmat issues, that\'s the staffing company\'s problem too.',
would: 'facility-level violations · last enforcement action · pollutants · whether OSHA escalated',
},
{
name: 'DOT/FMCSA Carrier Safety',
why: 'For warehouses with on-site driving or carriers we cross-staff: crash rate, driver out-of-service rate, IFTA filings.',
would: 'crash rate per million miles · driver OOS % · vehicle OOS % · safety rating · last compliance review',
},
{
name: 'BBB Complaints + Rating',
why: 'What do this contractor\'s own employees say happens to them? BBB aggregates complaints from workers and clients.',
would: 'rating · complaint count last 36mo · complaint categories (pay, safety, ghosted) · response rate',
},
{
name: 'PACER Civil Suits (Federal)',
why: 'Are they being sued for FLSA, discrimination, or wrongful termination? Filings predate enforcement actions.',
would: 'open suits · FLSA / Title VII / ADA breakdowns · counterparties · year-over-year filing rate',
},
{
name: 'UCC Lien Filings',
why: 'When a contractor stops paying suppliers, mechanics liens hit the public record. Cash-flow distress signal.',
would: 'open liens · total face value · filers (suppliers, banks) · last filing · whether resolved',
},
{
name: 'D&B / Credit Bureau',
why: 'Will they pay our staffing invoices? D&B PAYDEX score is the standard.',
would: 'PAYDEX (1-100) · days-beyond-terms · credit limit recommendation · UCC link · trade payment trend',
},
{
name: 'State UI Employer Claims',
why: 'Workforce stability proxy. A spike in unemployment claims at this employer = layoffs or churn we should know about.',
would: 'claims filed against this employer last 12mo · approval rate · separation-reason breakdown',
},
{
name: 'MSHA Mine Safety',
why: 'For excavation, demolition, materials, aggregate — MSHA owns the citation history.',
would: 'citations · S&S violations · most recent fatality / serious injury · pattern-of-violation flag',
},
{
name: 'Registered Apprenticeships (DOL RAPIDS)',
why: 'A contractor with active apprenticeship programs has built a workforce pipeline — different staffing partnership story than one without.',
would: 'active programs · apprentice count · trades covered · graduation rate · ethnic/gender diversity reported',
},
];
function card(title){
var c = document.createElement('div');
c.className = 'card';
var h = document.createElement('h3');
h.textContent = title;
c.appendChild(h);
return c;
}
function big(c, value, sub){
var b = document.createElement('div'); b.className='big'; b.textContent=value;
var s = document.createElement('div'); s.className='sub'; s.textContent=sub;
c.appendChild(b); c.appendChild(s);
}
function rowEl(c, label, value){
var r = document.createElement('div'); r.className='row';
var l = document.createElement('span'); l.className='l'; l.textContent=label;
var v = document.createElement('span'); v.className='v'; v.textContent=value||'—';
r.appendChild(l); r.appendChild(v); c.appendChild(r);
}
function empty(c, msg){
var e = document.createElement('div'); e.className='empty'; e.textContent=msg;
c.appendChild(e);
}
</script>
</body></html>