[object Object]

← back to Commercialrealestate

feat(condos): full Amazon left-rail drill-down — warrantability (incl. Unwarrantable) + city(20) + firm(14) + beds/baths mins + price/HOA/sqft/$-per-sqft/year ranges + 17 per-field show/hide toggles (computed $/sqft, $/bed, age); list/grid both field-driven; ?status deep-link

a33bd4694afaf6980ddab808bdf947f6787ad586 · 2026-07-02 10:01:01 -0700 · Steve Abrams

Files touched

Diff

commit a33bd4694afaf6980ddab808bdf947f6787ad586
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Thu Jul 2 10:01:01 2026 -0700

    feat(condos): full Amazon left-rail drill-down — warrantability (incl. Unwarrantable) + city(20) + firm(14) + beds/baths mins + price/HOA/sqft/$-per-sqft/year ranges + 17 per-field show/hide toggles (computed $/sqft, $/bed, age); list/grid both field-driven; ?status deep-link
---
 public/condos.html | 308 +++++++++++++++++++++++++++++++++--------------------
 1 file changed, 195 insertions(+), 113 deletions(-)

diff --git a/public/condos.html b/public/condos.html
index 2ab57af..8b40078 100644
--- a/public/condos.html
+++ b/public/condos.html
@@ -8,21 +8,42 @@
   :root{ --bg:#0e1116; --card:#161b22; --line:#2a313c; --ink:#e6edf3; --mut:#8b949e; --acc:#3fb950; --blue:#58a6ff; --gold:#d29922; --red:#f85149; --purp:#bc8cff; }
   *{box-sizing:border-box;} html,body{margin:0;}
   body{background:var(--bg);color:var(--ink);font:14px/1.5 -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;}
-  header{display:flex;align-items:center;gap:16px;padding:14px 22px;border-bottom:1px solid var(--line);position:sticky;top:0;background:var(--bg);z-index:5;}
+  header{display:flex;align-items:center;gap:16px;padding:14px 22px;border-bottom:1px solid var(--line);position:sticky;top:0;background:rgba(14,17,22,.98);backdrop-filter:blur(8px);z-index:30;}
   header h1{font-size:18px;margin:0;} header a{color:var(--blue);text-decoration:none;font-size:13px;}
   header .nav{margin-left:auto;display:flex;gap:18px;}
   .banner{font-size:12px;color:var(--gold);padding:9px 22px;background:rgba(210,153,34,.08);border-bottom:1px solid rgba(210,153,34,.25);}
-  .controls{display:flex;gap:12px;align-items:center;padding:14px 22px;flex-wrap:wrap;border-bottom:1px solid var(--line);}
-  input[type=text],select{background:var(--card);color:var(--ink);border:1px solid var(--line);border-radius:8px;padding:8px 11px;font-size:13px;}
-  input[type=text]{min-width:280px;} input[type=range]{accent-color:var(--blue);}
-  .controls label{font-size:12px;color:var(--mut);display:flex;align-items:center;gap:7px;}
-  .count{margin-left:auto;font-size:12px;color:var(--mut);} .count b{color:var(--ink);}
-  main{padding:30px 32px;}
+  input[type=text],input[type=number]{background:var(--card);color:var(--ink);border:1px solid var(--line);border-radius:8px;padding:8px 11px;font-size:13px;}
+  input[type=range]{accent-color:var(--blue);}
+  .count{font-size:12px;color:var(--mut);margin-left:auto;} .count b{color:var(--ink);}
+  /* ---- Amazon-style left rail ---- */
+  .shell{display:flex;align-items:flex-start;}
+  .rail{flex:0 0 262px;background:#0b0e13;border-right:1px solid var(--line);align-self:stretch;position:sticky;top:53px;height:calc(100vh - 53px);overflow-y:auto;padding:0 0 40px;}
+  .rail .rsec{padding:13px 16px;border-bottom:1px solid var(--line);}
+  .rail h4{margin:0 0 9px;font-size:11px;text-transform:uppercase;letter-spacing:.6px;color:var(--mut);}
+  .chips{display:flex;flex-wrap:wrap;gap:6px;}
+  .chip{background:var(--card);color:var(--mut);border:1px solid var(--line);border-radius:16px;padding:4px 10px;font-size:12px;cursor:pointer;user-select:none;}
+  .chip:hover{color:var(--ink);} .chip.active{border-color:var(--blue);color:var(--blue);background:rgba(88,166,255,.08);}
+  .chip.warr-unwarrantable.active{border-color:var(--red);color:var(--red);background:rgba(248,81,73,.10);}
+  .chip .ct{color:var(--mut);font-size:10px;margin-left:4px;}
+  .row2{display:flex;gap:8px;} .fld{flex:1;} .fld label{display:block;font-size:10px;color:var(--mut);margin-bottom:3px;}
+  .rail input[type=number]{width:100%;}
+  .fieldtoggles{display:flex;flex-direction:column;gap:1px;max-height:320px;overflow-y:auto;}
+  .ftog{display:flex;align-items:center;gap:8px;font-size:12.5px;color:var(--ink);cursor:pointer;padding:3px 4px;border-radius:6px;} .ftog:hover{background:rgba(88,166,255,.06);} .ftog input{accent-color:var(--blue);}
+  .minibtn{background:var(--card);color:var(--mut);border:1px solid var(--line);border-radius:7px;padding:4px 9px;font-size:11px;cursor:pointer;margin-right:5px;} .minibtn:hover{border-color:var(--blue);color:var(--ink);}
+  main{flex:1 1 auto;min-width:0;padding:0 0 40px;}
+  .topbar{display:flex;align-items:center;gap:12px;flex-wrap:wrap;padding:14px 24px;border-bottom:1px solid var(--line);position:sticky;top:53px;background:rgba(14,17,22,.95);backdrop-filter:blur(6px);z-index:20;}
+  .topbar input[type=text]{min-width:260px;}
+  .topbar .dens{font-size:12px;color:var(--mut);display:flex;align-items:center;gap:7px;}
+  .viewseg{display:inline-flex;border:1px solid var(--line);border-radius:8px;overflow:hidden;}
+  .viewseg button{background:var(--card);color:var(--mut);border:0;border-right:1px solid var(--line);padding:7px 12px;font-size:12px;cursor:pointer;}
+  .viewseg button:last-child{border-right:0;} .viewseg button.active{background:#1f6feb;color:#fff;}
+  .gridwrap{padding:24px;}
   .grid{display:grid;gap:22px;grid-template-columns:repeat(var(--cols,3),minmax(0,1fr));}
+  .grid.listhost{display:block;}
   .condo{background:var(--card);border:2px solid #fff;border-radius:12px;padding:20px;display:flex;flex-direction:column;gap:6px;}
   .condo .name{font-weight:600;font-size:14px;}
   .condo .addr{font-size:12px;color:var(--mut);}
-  .row{display:flex;justify-content:space-between;font-size:12px;}
+  .row{display:flex;justify-content:space-between;font-size:12px;gap:10px;}
   .row .k{color:var(--mut);}
   .badge{display:inline-block;padding:2px 9px;border-radius:11px;font-size:11px;font-weight:700;width:fit-content;}
   .badge.fha_approved{background:rgba(63,185,80,.16);color:var(--acc);}
@@ -32,10 +53,6 @@
   .src{font-size:11px;color:var(--mut);border-top:1px solid var(--line);padding-top:7px;margin-top:2px;}
   .broker{font-size:11px;color:var(--purp);}
   .empty{padding:50px;text-align:center;color:var(--mut);}
-  .viewseg{display:inline-flex;border:1px solid var(--line);border-radius:8px;overflow:hidden;}
-  .viewseg button{background:var(--card);color:var(--mut);border:0;border-right:1px solid var(--line);padding:7px 12px;font-size:12px;cursor:pointer;}
-  .viewseg button:last-child{border-right:0;} .viewseg button.active{background:#1f6feb;color:#fff;}
-  .grid.listhost{display:block;}
   .listwrap{overflow-x:auto;border:2px solid #fff;border-radius:12px;}
   .listtbl{width:100%;border-collapse:collapse;font-size:12.5px;white-space:nowrap;}
   .listtbl th,.listtbl td{text-align:right;padding:9px 13px;border-top:1px solid var(--line);}
@@ -44,139 +61,204 @@
   .listtbl th.stick{background:#0b0e13;}
   .listtbl tr:hover td{background:rgba(88,166,255,.06);}
   .listtbl .la{font-weight:600;color:var(--ink);} .listtbl .lc{color:var(--mut);font-size:11px;}
+  @media(max-width:820px){ .rail{display:none;} }
 </style>
 </head>
 <body>
 <header>
   <h1>🏢 LA Condos — Warrantability</h1>
-  <div class="nav"><a href="/">← properties</a><a href="/graphics.html">graphics</a><a href="/brokers.html">mind-map</a><a href="/fha-loans.html">FHA loans</a></div>
+  <div class="nav"><a href="/">← properties</a><a href="/mls.html">MLS</a><a href="/graphics.html">graphics</a><a href="/fha-loans.html">FHA loans</a></div>
 </header>
 <div class="banner" id="banner">⚖️ Loading warrantability label…</div>
 
-<div class="controls">
-  <input type="text" id="q" placeholder="search project / city / zip / address…">
-  <label>Status
-    <select id="status">
-      <option value="fha_approved">FHA-approved (warrantable proxy)</option>
-      <option value="fha_expired">FHA-expired (re-cert needed)</option>
-      <option value="unwarrantable">⚑ Unwarrantable · non-FHA (Arcstone target)</option>
-      <option value="all">All</option>
-    </select>
-  </label>
-  <span class="viewseg" id="viewseg">
-    <button data-view="grid" class="active">▦ Grid</button>
-    <button data-view="list">☰ List</button>
-  </span>
-  <label>Density <input type="range" id="cols" min="1" max="20" step="1" value="3"></label>
-  <div class="count" id="count"></div>
-</div>
-
+<div class="shell">
+<aside class="rail">
+  <div class="rsec"><h4>Warrantability</h4><div class="chips" id="fWarr"></div></div>
+  <div class="rsec"><h4>City</h4><div class="chips" id="fCity"></div></div>
+  <div class="rsec" id="firmSec"><h4>Listing firm</h4><div class="chips" id="fFirm"></div></div>
+  <div class="rsec" id="bedSec"><h4>Beds (min)</h4><div class="chips" id="fBeds"></div></div>
+  <div class="rsec" id="bathSec"><h4>Baths (min)</h4><div class="chips" id="fBaths"></div></div>
+  <div class="rsec"><h4>Price</h4><div class="row2"><div class="fld"><label>Min $</label><input type="number" id="pMin" step="50000" placeholder="any"></div><div class="fld"><label>Max $</label><input type="number" id="pMax" step="50000" placeholder="any"></div></div></div>
+  <div class="rsec"><h4>HOA / mo</h4><div class="row2"><div class="fld"><label>Min $</label><input type="number" id="hoaMin" step="50" placeholder="any"></div><div class="fld"><label>Max $</label><input type="number" id="hoaMax" step="50" placeholder="any"></div></div></div>
+  <div class="rsec"><h4>Sq Ft</h4><div class="row2"><div class="fld"><label>Min</label><input type="number" id="sqMin" step="100" placeholder="any"></div><div class="fld"><label>Max</label><input type="number" id="sqMax" step="100" placeholder="any"></div></div></div>
+  <div class="rsec"><h4>$ / Sq Ft</h4><div class="row2"><div class="fld"><label>Min</label><input type="number" id="psfMin" step="25" placeholder="any"></div><div class="fld"><label>Max</label><input type="number" id="psfMax" step="25" placeholder="any"></div></div></div>
+  <div class="rsec"><h4>Year built</h4><div class="row2"><div class="fld"><label>After</label><input type="number" id="yrMin" step="1" placeholder="any"></div><div class="fld"><label>Before</label><input type="number" id="yrMax" step="1" placeholder="any"></div></div></div>
+  <div class="rsec"><h4>Fields — show / hide</h4>
+    <div class="fieldtoggles" id="fFields"></div>
+    <div style="margin-top:8px"><button class="minibtn" id="fAll">All</button><button class="minibtn" id="fNone">None</button><button class="minibtn" id="fDef">Default</button></div>
+  </div>
+</aside>
 <main>
-  <div class="grid" id="grid"><div class="empty">loading…</div></div>
+  <div class="topbar">
+    <input type="text" id="q" placeholder="search project / city / zip / address / broker / firm…">
+    <span class="viewseg" id="viewseg"><button data-view="grid" class="active">▦ Grid</button><button data-view="list">☰ List</button></span>
+    <label class="dens">Density <input type="range" id="cols" min="1" max="20" step="1" value="3"></label>
+    <span class="count" id="count">…</span>
+  </div>
+  <div class="gridwrap"><div class="grid" id="grid"><div class="empty">loading…</div></div></div>
 </main>
+</div>
 
 <script>
 const $=s=>document.querySelector(s);
+const $$=s=>Array.from(document.querySelectorAll(s));
+const esc=s=>String(s==null?'':s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/"/g,'&quot;');
 const PROXY='FHA/VA-approval-based proxy, NOT lender-verified Fannie/Freddie warrantability.';
-let DB_CONDOS=[]; // classified listings from cre DB (if any sample loaded)
+const NOW_YEAR=new Date().getFullYear();
+let DATA=[], MODE='db', VIEW=localStorage.getItem('condoView')||'grid', bannerDefault='';
 
 function statusBadge(s){
   const txt={ fha_approved:'FHA-APPROVED · proxy warrantable', fha_expired:'FHA-EXPIRED · re-cert', heuristic_flag:'FLAGGED · review', not_listed:'NOT LISTED · verify' }[s]||s;
   return `<span class="badge ${s}">${txt}</span>`;
 }
 
-const esc=s=>String(s==null?'':s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/"/g,'&quot;');
-const money=n=>n?'$'+(+n).toLocaleString():'—';
-let VIEW=localStorage.getItem('condoView')||'grid';
-let LAST={rows:[],mode:'db'};
-function paint(){ const g=$('#grid'); if(!LAST.rows.length){ g.className='grid'; g.innerHTML='<div class="empty">no projects match.</div>'; return; }
-  if(VIEW==='list'){ g.className='grid listhost'; g.innerHTML=(LAST.mode==='db'?dbList:fhaList)(LAST.rows); }
-  else { g.className='grid'; g.innerHTML=(LAST.mode==='db'?dbCards:fhaCards)(LAST.rows); } }
-function dbList(rows){
-  const head=['Project','Status','Price','HOA','City','ZIP','Broker','Firm'];
-  return `<div class="listwrap"><table class="listtbl"><thead><tr><th class="stick">${head[0]}</th>${head.slice(1).map(h=>`<th>${h}</th>`).join('')}</tr></thead><tbody>`
-    +rows.map(c=>`<tr><td class="stick"><div class="la">${esc(c.project_name||c.address||'Condo')}</div><div class="lc">${esc(c.address||'')}</div></td>`
-      +`<td>${esc(c.warrantable_status||'—')}</td><td>${money(c.price)}</td><td>${c.hoa?'$'+c.hoa+'/mo':'—'}</td>`
-      +`<td>${esc(c.city||'—')}</td><td>${esc(c.zip||'—')}</td><td>${esc(c.broker_name||'—')}</td><td>${esc(c.firm_name||'—')}</td></tr>`).join('')
-    +'</tbody></table></div>';
+// ---- every data point (incl. computed) — each toggleable, drives cards + list columns ----
+const FIELDS=[
+  {k:'warrantable_status', l:'Warrantability', badge:1, col:1, def:1},
+  {k:'price',       l:'Price',        money:1, col:1, def:1},
+  {k:'hoa',         l:'HOA/mo',       money:1, col:1, def:1, suf:'/mo'},
+  {k:'beds',        l:'Beds',                  col:1, def:1},
+  {k:'baths',       l:'Baths',                 col:1, def:1},
+  {k:'sqft',        l:'Sq Ft',        num:1,   col:1, def:1},
+  {k:'ppsf',        l:'$/Sq Ft',      money:1, col:1, def:1, calc:c=>c.sqft?Math.round(c.price/c.sqft):null},
+  {k:'ppbed',       l:'$/Bed',        money:1, col:1, def:0, calc:c=>c.beds?Math.round(c.price/c.beds):null},
+  {k:'year_built',  l:'Year built',            col:1, def:1},
+  {k:'age',         l:'Age (yrs)',             col:1, def:0, calc:c=>c.year_built?(NOW_YEAR-c.year_built):null},
+  {k:'city',        l:'City',                  col:1, def:1},
+  {k:'zip',         l:'ZIP',                   col:1, def:0},
+  {k:'project_name',l:'Project',               col:1, def:0},
+  {k:'broker_name', l:'Broker',                col:1, def:1},
+  {k:'firm_name',   l:'Firm',                  col:1, def:0},
+  {k:'warrant_source',l:'Warrant note',        col:0, def:0},
+  {k:'created_at',  l:'Added',        date:1,  col:1, def:0},
+];
+const VIS=(function(){ let s={}; try{s=JSON.parse(localStorage.getItem('condoFields')||'{}');}catch(e){} const o={}; FIELDS.forEach(f=>o[f.k]=(f.k in s)?!!s[f.k]:!!f.def); return o; })();
+function fvis(k){ return !!VIS[k]; }
+function saveVis(){ try{localStorage.setItem('condoFields',JSON.stringify(VIS));}catch(e){} }
+function fval(c,f){ return f.calc?f.calc(c):c[f.k]; }
+function fcell(c,f){ const v=fval(c,f);
+  if(f.badge) return statusBadge(c.warrantable_status||c.warrant_signal||'—');
+  if(v==null||v==='') return '—';
+  if(f.money) return '$'+(+v).toLocaleString()+(f.suf||'');
+  if(f.num) return (+v).toLocaleString();
+  if(f.date){ try{ return new Date(v).toLocaleDateString(undefined,{year:'numeric',month:'short',day:'numeric'}); }catch(e){ return '—'; } }
+  return esc(v)+(f.suf||'');
 }
-function fhaList(rows){
-  const head=['Project','Signal','FHA concentration','Expires','City','ZIP','Method','HUD ID'];
-  return `<div class="listwrap"><table class="listtbl"><thead><tr><th class="stick">${head[0]}</th>${head.slice(1).map(h=>`<th>${h}</th>`).join('')}</tr></thead><tbody>`
-    +rows.map(c=>`<tr><td class="stick"><div class="la">${esc(c.project_name||'')}</div><div class="lc">${esc(c.address||'')}</div></td>`
-      +`<td>${esc(c.warrant_signal||'—')}</td><td>${esc(c.fha_concentration||'—')}</td><td>${esc(c.expiration_date||'—')}</td>`
-      +`<td>${esc(c.city||'—')}</td><td>${esc(c.zip||'—')}</td><td>${esc(c.approval_method||'—')}</td><td>${esc(c.condo_id||'—')}</td></tr>`).join('')
-    +'</tbody></table></div>';
+
+// ---- facet state ----
+const F={ warr:'all', cities:new Set(), firms:new Set(), bedsMin:0, bathsMin:0,
+  pmin:null,pmax:null, hoamin:null,hoamax:null, sqftMin:null,sqftMax:null, yearMin:null,yearMax:null, ppsfMin:null,ppsfMax:null };
+function rng(v,mn,mx){ if(mn!=null){ if(v==null||!(+v>=mn)) return false; } if(mx!=null){ if(v==null||!(+v<=mx)) return false; } return true; }
+function passFacets(c){
+  const ws=c.warrantable_status||c.warrant_signal;
+  if(!(F.warr==='all'?true:F.warr==='unwarrantable'?ws!=='fha_approved':ws===F.warr)) return false;
+  if(F.cities.size&&!F.cities.has(c.city)) return false;
+  if(F.firms.size&&!F.firms.has(c.firm_name)) return false;
+  if(F.bedsMin&&!(+c.beds>=F.bedsMin)) return false;
+  if(F.bathsMin&&!(+c.baths>=F.bathsMin)) return false;
+  if(!rng(c.price,F.pmin,F.pmax)) return false;
+  if(!rng(c.hoa,F.hoamin,F.hoamax)) return false;
+  if(!rng(c.sqft,F.sqftMin,F.sqftMax)) return false;
+  if(!rng(c.year_built,F.yearMin,F.yearMax)) return false;
+  if(!rng(c.sqft?c.price/c.sqft:null,F.ppsfMin,F.ppsfMax)) return false;
+  return true;
 }
-// Render either real DB condo listings (preferred) OR the FHA-approved project directory.
-function renderDbCondos(rows){ LAST={rows,mode:'db'}; paint(); }
-function dbCards(rows){
-  return rows.map(c=>{
-    const ws=c.warrant_signals; const sigArr=Array.isArray(ws)?ws:((ws&&ws.signals)||[]);
-    const sig=sigArr.map(x=>`• ${(x&&x.note)||x}`).join('<br>');
-    return `<div class="condo">
-      <div class="name">${c.project_name||c.address||'Condo'}</div>
-      <div class="addr">${c.address||''}${c.city?', '+c.city:''} ${c.zip||''}</div>
-      ${statusBadge(c.warrantable_status)}
-      <div class="row"><span class="k">Price</span><span>${c.price?'$'+(+c.price).toLocaleString():'—'}</span></div>
-      <div class="row"><span class="k">HOA</span><span>${c.hoa?'$'+c.hoa+'/mo':'—'}</span></div>
-      ${c.broker_name?`<div class="broker">🧑‍💼 ${c.broker_name}${c.firm_name?' · '+c.firm_name:''}</div>`:c.firm_name?`<div class="broker">🏢 ${c.firm_name}</div>`:''}
-      <div class="src">${c.warrant_source||''}${sig?'<br>'+sig:''}</div>
-    </div>`;
+
+// ---- rail builder ----
+function buildRail(){
+  const wc={},cc={},fc={};
+  DATA.forEach(c=>{ const s=c.warrantable_status||c.warrant_signal||'?'; wc[s]=(wc[s]||0)+1; if(c.city)cc[c.city]=(cc[c.city]||0)+1; if(c.firm_name)fc[c.firm_name]=(fc[c.firm_name]||0)+1; });
+  const order=[['all','All'],['fha_approved','FHA-approved'],['fha_expired','FHA-expired'],['not_listed','Not-listed'],['heuristic_flag','Flagged'],['unwarrantable','⚑ Unwarrantable · non-FHA']];
+  $('#fWarr').innerHTML=order.filter(([v])=>v==='all'||v==='unwarrantable'||wc[v]).map(([v,l])=>{
+    const n=v==='all'?DATA.length:v==='unwarrantable'?(DATA.length-(wc.fha_approved||0)):wc[v];
+    return `<span class="chip warr-${v} ${F.warr===v?'active':''}" data-warr="${v}">${l}<span class="ct">${n}</span></span>`;
   }).join('');
+  $('#fCity').innerHTML=Object.entries(cc).sort((a,b)=>b[1]-a[1]).slice(0,20).map(([k,c])=>`<span class="chip ${F.cities.has(k)?'active':''}" data-cy="${esc(k)}">${esc(k)}<span class="ct">${c}</span></span>`).join('');
+  const firms=Object.entries(fc).sort((a,b)=>b[1]-a[1]).slice(0,14);
+  $('#firmSec').style.display=firms.length?'':'none';
+  $('#fFirm').innerHTML=firms.map(([k,c])=>`<span class="chip ${F.firms.has(k)?'active':''}" data-fm="${esc(k)}">${esc(k)}<span class="ct">${c}</span></span>`).join('');
+  const hasBeds=DATA.some(c=>c.beds!=null), hasBaths=DATA.some(c=>c.baths!=null);
+  $('#bedSec').style.display=hasBeds?'':'none'; $('#bathSec').style.display=hasBaths?'':'none';
+  $('#fBeds').innerHTML=[0,1,2,3,4].map(n=>`<span class="chip ${F.bedsMin===n?'active':''}" data-beds="${n}">${n===0?'Any':n+'+'}</span>`).join('');
+  $('#fBaths').innerHTML=[0,1,2,3].map(n=>`<span class="chip ${F.bathsMin===n?'active':''}" data-baths="${n}">${n===0?'Any':n+'+'}</span>`).join('');
+  buildFieldToggles();
+}
+function buildFieldToggles(){ $('#fFields').innerHTML=FIELDS.map(f=>`<label class="ftog"><input type="checkbox" data-fk="${f.k}" ${fvis(f.k)?'checked':''}><span>${esc(f.l)}</span>${f.col?'<span class="ct" title="Also a List column">▤</span>':''}</label>`).join(''); }
+
+// ---- renderers ----
+const CARD_SKIP=new Set(['warrantable_status','city','zip','project_name','warrant_source']);
+function dbCards(rows){
+  const vis=FIELDS.filter(f=>fvis(f.k)&&!CARD_SKIP.has(f.k));
+  return rows.map(c=>`<div class="condo">
+    <div class="name">${esc(c.project_name||c.address||'Condo')}</div>
+    <div class="addr">${esc(c.address||'')}${c.city?', '+esc(c.city):''} ${esc(c.zip||'')}</div>
+    ${fvis('warrantable_status')?statusBadge(c.warrantable_status||c.warrant_signal):''}
+    ${vis.map(f=>`<div class="row"><span class="k">${esc(f.l)}</span><span>${fcell(c,f)}</span></div>`).join('')}
+    ${(fvis('firm_name')||fvis('broker_name'))&&(c.broker_name||c.firm_name)?`<div class="broker">🧑‍💼 ${esc(c.broker_name||'')}${c.firm_name?(c.broker_name?' · ':'')+esc(c.firm_name):''}</div>`:''}
+    ${fvis('warrant_source')&&c.warrant_source?`<div class="src">${esc(c.warrant_source)}</div>`:''}
+  </div>`).join('');
+}
+function dbList(rows){
+  const cols=FIELDS.filter(f=>f.col&&fvis(f.k));
+  const head='<th class="stick">Project</th>'+cols.map(f=>`<th>${esc(f.l)}</th>`).join('');
+  const body=rows.map(c=>`<tr><td class="stick"><div class="la">${esc(c.project_name||c.address||'Condo')}</div><div class="lc">${esc(c.address||'')}${c.city?', '+esc(c.city):''}</div></td>`
+    +cols.map(f=>`<td>${fcell(c,f)}</td>`).join('')+'</tr>').join('');
+  return `<div class="listwrap"><table class="listtbl"><thead><tr>${head}</tr></thead><tbody>${body}</tbody></table></div>`;
+}
+function paint(rows){ const g=$('#grid');
+  if(!rows.length){ g.className='grid'; g.innerHTML='<div class="empty">no condos match these filters.</div>'; return; }
+  if(VIEW==='list'){ g.className='grid listhost'; g.innerHTML=dbList(rows); }
+  else { g.className='grid'; g.innerHTML=dbCards(rows); }
 }
 
-function renderFhaDirectory(rows){ LAST={rows:rows||[],mode:'fha'}; paint(); }
-function fhaCards(rows){
-  return rows.map(c=>`
-    <div class="condo">
-      <div class="name">${c.project_name}</div>
-      <div class="addr">${c.address||''}${c.city?', '+c.city:''} ${c.zip||''}</div>
-      ${statusBadge(c.warrant_signal)}
-      <div class="row"><span class="k">FHA concentration</span><span>${c.fha_concentration||'—'}</span></div>
-      <div class="row"><span class="k">Composition</span><span style="text-align:right;max-width:60%">${(c.composition||'').slice(0,60)}</span></div>
-      <div class="row"><span class="k">Expires</span><span>${c.expiration_date||'—'}</span></div>
-      <div class="src">HUD ${c.condo_id||''} ${c.submission||''} · method ${c.approval_method||'—'} · status ${c.status||c.hud_status||''}</div>
-    </div>`).join('');
+// ---- apply filters + render ----
+function apply(){
+  let rows=DATA.filter(passFacets);
+  const ql=($('#q').value||'').trim().toLowerCase();
+  if(ql) rows=rows.filter(c=>((c.project_name||'')+' '+(c.city||'')+' '+(c.zip||'')+' '+(c.address||'')+' '+(c.broker_name||'')+' '+(c.firm_name||'')).toLowerCase().includes(ql));
+  const unw=F.warr==='unwarrantable';
+  $('#count').innerHTML=`<b>${rows.length}</b> of ${DATA.length} ${unw?'unwarrantable (non-FHA) condos':(MODE==='fha'?'FHA-list projects':'condos')}`;
+  $('#banner').textContent = unw
+    ? '⚑ Non-warrantable condos — NOT on the FHA-approved list (expired or never listed). Conventional/FHA lenders decline these — Arcstone non-QM / portfolio financing territory.'
+    : bannerDefault;
+  paint(rows);
 }
 
-async function load(){
-  const status=$('#status').value, q=$('#q').value.trim();
-  // 1) Prefer real classified condo listings from the cre DB.
-  if(!DB_CONDOS.length){
-    try{ const r=await (await fetch('/api/condos')).json(); DB_CONDOS=r.condos||[]; if(r.label) $('#banner').textContent='⚖️ '+r.label; }catch(_){}
-  }
-  // "unwarrantable" = every condo NOT on the FHA-approved list (expired + not-listed) — Arcstone's non-QM target.
-  const matchStatus=(v)=> status==='all' ? true : status==='unwarrantable' ? v!=='fha_approved' : v===status;
-  if(DB_CONDOS.length){
-    let rows=DB_CONDOS.filter(c=>matchStatus(c.warrantable_status));
-    if(q){ const ql=q.toLowerCase(); rows=rows.filter(c=>((c.project_name||'')+' '+(c.city||'')+' '+(c.zip||'')+' '+(c.address||'')).toLowerCase().includes(ql)); }
-    $('#count').innerHTML=`<b>${rows.length}</b> ${status==='unwarrantable'?'unwarrantable (non-FHA) condos':'classified condo listings'}`;
-    if(status==='unwarrantable') $('#banner').textContent='⚑ Non-warrantable condos — NOT on the FHA-approved list (expired or never listed). These are the deals conventional/FHA lenders decline — Arcstone non-QM / portfolio financing territory.';
-    renderDbCondos(rows);
-    return;
+// ---- boot ----
+async function boot(){
+  try{ const r=await (await fetch('/api/condos')).json(); DATA=r.condos||[]; if(r.label) bannerDefault='⚖️ '+r.label; }catch(_){}
+  if(!DATA.length){
+    MODE='fha';
+    try{ const r=await (await fetch('/api/fha-condos?status=all')).json(); DATA=(r.condos||[]).map(c=>({...c,warrantable_status:c.warrant_signal})); bannerDefault='⚖️ '+(( DATA[0]&&DATA[0].label)||PROXY)+' — HUD FHA-approved project directory (no live condo listings loaded; full residential scrape is gated).'; }catch(_){}
   }
-  // 2) Fallback: the authoritative FHA-approved project directory (real warrantable inventory).
-  const sQ = status==='unwarrantable' ? 'all' : status;
-  const r=await (await fetch(`/api/fha-condos?status=${sQ}&q=${encodeURIComponent(q)}`)).json();
-  let fcondos=r.condos||[]; if(status==='unwarrantable') fcondos=fcondos.filter(c=>c.warrant_signal!=='fha_approved');
-  $('#banner').textContent = status==='unwarrantable'
-    ? '⚑ Non-warrantable condos (non-FHA-approved) — Arcstone non-QM / portfolio financing target.'
-    : '⚖️ '+(r.label||PROXY)+'  —  showing the HUD FHA-approved project directory (no live condo listings loaded yet; full residential scrape is gated).';
-  $('#count').innerHTML=`<b>${fcondos.length}</b> ${status==='unwarrantable'?'non-warrantable projects':'FHA-list projects'}`;
-  renderFhaDirectory(fcondos);
+  if(!bannerDefault) bannerDefault='⚖️ '+PROXY;
+  // deep-link: /condos?status=unwarrantable (or any warrantability value)
+  const qs=new URLSearchParams(location.search).get('status');
+  if(qs && ['all','fha_approved','fha_expired','not_listed','heuristic_flag','unwarrantable'].includes(qs)) F.warr=qs;
+  buildRail(); apply();
 }
 
-$('#q').oninput=()=>{ clearTimeout(window._t); window._t=setTimeout(load,200); };
-$('#status').onchange=load;
+// ---- wiring ----
+function tog(set,v){ set.has(v)?set.delete(v):set.add(v); }
+$('#fWarr').addEventListener('click',e=>{const c=e.target.closest('.chip');if(!c)return;F.warr=c.dataset.warr;$$('#fWarr .chip').forEach(x=>x.classList.toggle('active',x.dataset.warr===F.warr));apply();});
+$('#fCity').addEventListener('click',e=>{const c=e.target.closest('.chip');if(!c)return;tog(F.cities,c.dataset.cy);c.classList.toggle('active');apply();});
+$('#fFirm').addEventListener('click',e=>{const c=e.target.closest('.chip');if(!c)return;tog(F.firms,c.dataset.fm);c.classList.toggle('active');apply();});
+$('#fBeds').addEventListener('click',e=>{const c=e.target.closest('.chip');if(!c)return;F.bedsMin=+c.dataset.beds;$$('#fBeds .chip').forEach(x=>x.classList.toggle('active',+x.dataset.beds===F.bedsMin));apply();});
+$('#fBaths').addEventListener('click',e=>{const c=e.target.closest('.chip');if(!c)return;F.bathsMin=+c.dataset.baths;$$('#fBaths .chip').forEach(x=>x.classList.toggle('active',+x.dataset.baths===F.bathsMin));apply();});
+const rangeWire=(id,key)=>$('#'+id).addEventListener('input',()=>{F[key]=$('#'+id).value?+$('#'+id).value:null;apply();});
+rangeWire('pMin','pmin');rangeWire('pMax','pmax');rangeWire('hoaMin','hoamin');rangeWire('hoaMax','hoamax');
+rangeWire('sqMin','sqftMin');rangeWire('sqMax','sqftMax');rangeWire('psfMin','ppsfMin');rangeWire('psfMax','ppsfMax');
+rangeWire('yrMin','yearMin');rangeWire('yrMax','yearMax');
+$('#fFields').addEventListener('change',e=>{const cb=e.target.closest('input[data-fk]');if(!cb)return;VIS[cb.dataset.fk]=cb.checked;saveVis();apply();});
+$('#fAll').addEventListener('click',()=>{FIELDS.forEach(f=>VIS[f.k]=true);saveVis();buildFieldToggles();apply();});
+$('#fNone').addEventListener('click',()=>{FIELDS.forEach(f=>VIS[f.k]=false);saveVis();buildFieldToggles();apply();});
+$('#fDef').addEventListener('click',()=>{FIELDS.forEach(f=>VIS[f.k]=!!f.def);saveVis();buildFieldToggles();apply();});
+$('#q').oninput=()=>{ clearTimeout(window._t); window._t=setTimeout(apply,180); };
 $('#cols').oninput=e=>{ document.documentElement.style.setProperty('--cols',e.target.value); localStorage.setItem('condoCols',e.target.value); };
 const savedCols=localStorage.getItem('condoCols'); if(savedCols){ $('#cols').value=savedCols; document.documentElement.style.setProperty('--cols',savedCols); }
-document.querySelectorAll('#viewseg button').forEach(b=>b.classList.toggle('active',b.dataset.view===VIEW));
-$('#viewseg').onclick=e=>{ const b=e.target.closest('button[data-view]'); if(!b) return; VIEW=b.dataset.view; localStorage.setItem('condoView',VIEW); document.querySelectorAll('#viewseg button').forEach(x=>x.classList.toggle('active',x.dataset.view===VIEW)); paint(); };
-// deep-link support: /condos?status=unwarrantable preselects the filter
-const _qs=new URLSearchParams(location.search).get('status');
-if(_qs && [...$('#status').options].some(o=>o.value===_qs)) $('#status').value=_qs;
-load();
+$$('#viewseg button').forEach(b=>b.classList.toggle('active',b.dataset.view===VIEW));
+$('#viewseg').onclick=e=>{ const b=e.target.closest('button[data-view]'); if(!b) return; VIEW=b.dataset.view; localStorage.setItem('condoView',VIEW); $$('#viewseg button').forEach(x=>x.classList.toggle('active',x.dataset.view===VIEW)); apply(); };
+boot();
 </script>
 </body>
 </html>

← a2c781e feat(condos): add Unwarrantable (non-FHA-approved) filter —  ·  back to Commercialrealestate  ·  fix(index): surface Condos + Unwarrantable as the FIRST chip 58cbced →