[object Object]

← back to Nationalrealestate

usreal: dedupe residential+commercial broker desks into shared broker-desk.js (mode param) + add SORT + DENSITY + real Prev/Next pagination (standing grid rule + Cody gate). commercial-brokers now truly commercial-only (was mislabeled all) (TK-10590 cycle 2)

e61c69f30e68028c648a115d866a0711964673b0 · 2026-08-15 08:26:40 -0700 · Steve Abrams

Files touched

Diff

commit e61c69f30e68028c648a115d866a0711964673b0
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Sat Aug 15 08:26:40 2026 -0700

    usreal: dedupe residential+commercial broker desks into shared broker-desk.js (mode param) + add SORT + DENSITY + real Prev/Next pagination (standing grid rule + Cody gate). commercial-brokers now truly commercial-only (was mislabeled all) (TK-10590 cycle 2)
---
 public/broker-desk.js           | 121 ++++++++++++++++++++++++++++++++++++++++
 public/commercial-brokers.html  |  68 +++++-----------------
 public/residential-brokers.html |  68 ++++------------------
 3 files changed, 144 insertions(+), 113 deletions(-)

diff --git a/public/broker-desk.js b/public/broker-desk.js
new file mode 100644
index 0000000..3695707
--- /dev/null
+++ b/public/broker-desk.js
@@ -0,0 +1,121 @@
+/* broker-desk.js — shared residential/commercial broker & firm desk for usreal (TK-10590).
+ * A page sets `window.DESK_MODE = 'residential' | 'commercial' | ''` (asset_class) BEFORE loading
+ * this script, plus an optional `window.DESK_CITY` default. One module, two surfaces — kills the
+ * drift between residential-brokers.html and commercial-brokers.html (Cody gate: they were 97% dup).
+ * Adds the standing grid requirements missing from the old clones: SORT + DENSITY + real PAGINATION,
+ * and an honest count (never show the full total next to a truncated page). */
+(function () {
+  const MODE = window.DESK_MODE || '';                 // '' = all, else asset_class
+  const AGENTS = MODE === 'residential';
+  const NOUN = AGENTS ? 'agents' : 'brokers';
+  const PAGE = 200;
+  const $ = (s) => document.querySelector(s);
+  const LS = { get(k){try{return localStorage.getItem(k)}catch{return null}}, set(k,v){try{localStorage.setItem(k,v)}catch{}} };
+  const esc = (s) => String(s == null ? '' : s).replace(/[&<>"]/g, c => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;'}[c]));
+
+  const S = {
+    tab: 'firms',
+    city: (window.DESK_CITY !== undefined ? window.DESK_CITY : 'LOS ANGELES'),
+    q: '',
+    sort: LS.get('desk.sort') || '',
+    dens: +(LS.get('desk.dens') || 280),
+    offset: 0, total: 0, shown: 0
+  };
+
+  const CITIES = [['LOS ANGELES','Los Angeles'],['BEVERLY HILLS','Beverly Hills'],['SANTA MONICA','Santa Monica'],
+    ['LONG BEACH','Long Beach'],['BURBANK','Burbank'],['GLENDALE','Glendale'],['PASADENA','Pasadena'],['','All CA']];
+
+  const root = document.getElementById('desk-root');
+  root.innerHTML = `
+    <div id="controls">
+      <div id="tabs"><button data-t="firms" class="active">Firms</button><button data-t="brokers">${AGENTS?'Agents':'Brokers'}</button></div>
+      <select id="city">${CITIES.map(([v,l])=>`<option value="${v}"${v===S.city?' selected':''}>${l}</option>`).join('')}</select>
+      <input id="q" placeholder="Search name / firm / license…" style="width:200px">
+      <select id="sort" title="Sort this page">
+        <option value="">Sort: default</option>
+        <option value="name">Name A→Z</option>
+        <option value="firm">Firm A→Z</option>
+        <option value="agents">Agents ↓</option>
+        <option value="city">City A→Z</option>
+      </select>
+      <label id="densWrap" title="Card density">▦ <input type="range" id="dens" min="200" max="380" step="20" value="${S.dens}"></label>
+      <span id="count"></span>
+    </div>
+    <div id="grid"></div>
+    <div id="pager"><button id="pgPrev">‹ Prev</button><span id="pgInfo"></span><button id="pgNext">Next ›</button></div>
+    <div id="loading">Loading…</div>`;
+
+  // density → grid column min-width (persisted)
+  function applyDens(){ $('#grid').style.gridTemplateColumns = `repeat(auto-fill,minmax(${S.dens}px,1fr))`; }
+  applyDens();
+
+  async function j(u){ const r = await fetch(u); return r.json(); }
+  function params(extra){
+    const p = new URLSearchParams({ state:'CA', limit:PAGE, offset:S.offset });
+    if (MODE) p.set('asset_class', MODE);
+    if (S.city) p.set('city', S.city);
+    if (S.q) p.set('q', S.q);
+    if (extra) for (const k in extra) p.set(k, extra[k]);
+    return p;
+  }
+  function setCount(shown,total){
+    S.shown = shown; S.total = total;
+    $('#count').textContent = (shown < total)
+      ? `showing ${shown.toLocaleString()} of ${total.toLocaleString()} ${S.tab==='firms'?'firms':NOUN} — page ${Math.floor(S.offset/PAGE)+1} of ${Math.max(Math.ceil(total/PAGE),1).toLocaleString()}`
+      : `${total.toLocaleString()} ${S.tab==='firms'?'firms':NOUN}`;
+    const pgs = Math.max(Math.ceil(total/PAGE),1), pg = Math.floor(S.offset/PAGE)+1;
+    $('#pgInfo').textContent = `${pg} / ${pgs.toLocaleString()}`;
+    $('#pgPrev').disabled = S.offset <= 0;
+    $('#pgNext').disabled = pg >= pgs;
+  }
+  // client-side sort of the current page (the API has no sort param; honest — sorts what's shown)
+  function sortRows(rows){
+    const k = S.sort; if (!k) return rows;
+    const cmp = {
+      name:  (a,b)=> String(a.name||'').localeCompare(String(b.name||'')),
+      firm:  (a,b)=> String(a.firm_name||a.name||'').localeCompare(String(b.firm_name||b.name||'')),
+      agents:(a,b)=> (b.agent_count||0)-(a.agent_count||0),
+      city:  (a,b)=> String(a.hq_city||a.city||'').localeCompare(String(b.hq_city||b.city||'')),
+    }[k];
+    return cmp ? rows.slice().sort(cmp) : rows;
+  }
+
+  async function loadFirms(){
+    const d = await j('/api/firms?'+params());
+    const rows = sortRows(d.rows||[]);
+    setCount(rows.length, d.total||0);
+    $('#grid').innerHTML = rows.map(f=>`<div class="card firm" data-name="${esc(f.name)}">
+      <div class="n">${esc(f.name)||'—'}</div>
+      <div class="m"><span class="agents">${f.agent_count||0} agents</span><span>${esc(f.hq_city)}</span>${f.license_no?`<span>DRE ${esc(f.license_no)}</span>`:''}</div>
+      <div class="roster"></div></div>`).join('') || '<div style="color:var(--muted);padding:20px">no firms match</div>';
+    document.querySelectorAll('.card.firm').forEach(c=>c.onclick=()=>toggleRoster(c));
+  }
+  async function toggleRoster(c){
+    const r = c.querySelector('.roster');
+    if (r.style.display==='block'){ r.style.display='none'; return; }
+    r.style.display='block';
+    if (r.dataset.loaded) return;
+    r.innerHTML='loading roster…';
+    const d = await j('/api/brokers?'+params({ limit:40, offset:0, firm:c.dataset.name }));
+    r.dataset.loaded='1';
+    r.innerHTML = (d.rows||[]).slice(0,30).map(b=>`<div>${esc(b.name)} <span style="color:var(--muted)">· ${esc(b.license_type)} · DRE ${esc(b.license_no)}</span></div>`).join('') || '<div style="color:var(--muted)">no roster rows</div>';
+  }
+  async function loadBrokers(){
+    const d = await j('/api/brokers?'+params());
+    const rows = sortRows(d.rows||[]);
+    setCount(rows.length, d.total||0);
+    $('#grid').innerHTML = rows.map(b=>`<div class="card">
+      <div class="n">${esc(b.name)||'—'}</div>
+      <div class="m"><span class="lt">${esc(b.license_type)}</span><span>${esc(b.firm_name)||'—'}</span><span>${esc(b.city)}</span>${b.license_no?`<span>DRE ${esc(b.license_no)}</span>`:''}</div></div>`).join('') || '<div style="color:var(--muted);padding:20px">no '+NOUN+' match</div>';
+  }
+  async function load(){ $('#loading').style.display='block'; S.tab==='firms'?await loadFirms():await loadBrokers(); $('#loading').style.display='none'; }
+
+  $('#tabs').querySelectorAll('button').forEach(b=>b.onclick=()=>{ $('#tabs').querySelectorAll('button').forEach(x=>x.classList.remove('active')); b.classList.add('active'); S.tab=b.dataset.t; S.offset=0; load(); });
+  $('#city').onchange=()=>{ S.city=$('#city').value; S.offset=0; load(); };
+  $('#q').oninput=()=>{ S.q=$('#q').value; S.offset=0; clearTimeout(window._t); window._t=setTimeout(load,250); };
+  $('#sort').value=S.sort; $('#sort').onchange=()=>{ S.sort=$('#sort').value; LS.set('desk.sort',S.sort); load(); };
+  $('#dens').oninput=()=>{ S.dens=+$('#dens').value; LS.set('desk.dens',S.dens); applyDens(); };
+  $('#pgPrev').onclick=()=>{ if(S.offset>0){ S.offset=Math.max(0,S.offset-PAGE); load(); } };
+  $('#pgNext').onclick=()=>{ if((Math.floor(S.offset/PAGE)+1) < Math.ceil(S.total/PAGE)){ S.offset+=PAGE; load(); } };
+  load();
+})();
diff --git a/public/commercial-brokers.html b/public/commercial-brokers.html
index 578b879..f9015ec 100644
--- a/public/commercial-brokers.html
+++ b/public/commercial-brokers.html
@@ -4,7 +4,7 @@
 <meta charset="utf-8">
 <meta name="viewport" content="width=device-width, initial-scale=1">
 <link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Crect width='16' height='16' rx='3' fill='%230a0d13'/%3E%3Ctext x='8' y='12' font-size='10' text-anchor='middle' fill='%23c8a24b'%3ECR%3C/text%3E%3C/svg%3E">
-<title>USRealEstate — LA Brokers & Firms</title>
+<title>USRealEstate — Commercial Brokers &amp; Firms</title>
 <style>
   :root{--bg:#0a0d13;--panel:#11151d;--line:#222936;--fg:#e8ecf2;--muted:#8a93a3;--gold:#c8a24b;}
   *{box-sizing:border-box;}
@@ -19,6 +19,8 @@
   #tabs button{background:var(--panel);color:var(--muted);border:0;border-right:1px solid var(--line);padding:7px 14px;font-size:13px;cursor:pointer;}
   #tabs button:last-child{border-right:0;} #tabs button.active{background:var(--gold);color:var(--bg);font-weight:700;}
   input,select{background:var(--panel);color:var(--fg);border:1px solid var(--line);border-radius:8px;padding:7px 10px;font-size:13px;}
+  #densWrap{display:inline-flex;align-items:center;gap:6px;color:var(--muted);font-size:13px;}
+  #dens{accent-color:var(--gold);}
   #count{font-size:12px;color:var(--muted);margin-left:auto;}
   #grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:10px;}
   .card{background:var(--panel);border:1px solid var(--line);border-radius:10px;padding:13px;}
@@ -29,6 +31,9 @@
   .lt{font-size:10px;letter-spacing:.05em;text-transform:uppercase;color:var(--gold);border:1px solid var(--line);border-radius:5px;padding:1px 6px;}
   .roster{margin-top:9px;padding-top:9px;border-top:1px solid var(--line);font-size:12px;color:#c3ccd8;display:none;}
   .roster div{padding:2px 0;}
+  #pager{display:flex;align-items:center;gap:10px;justify-content:center;margin-top:16px;font-size:13px;color:var(--muted);}
+  #pager button{background:var(--panel);color:var(--fg);border:1px solid var(--line);border-radius:8px;padding:6px 12px;cursor:pointer;}
+  #pager button:disabled{opacity:.4;cursor:default;}
   #loading{padding:40px;text-align:center;color:var(--muted);}
 </style>
 <link rel="stylesheet" href="/nav-drawer.css">
@@ -37,7 +42,7 @@
 </head>
 <body>
 <div id="topbar">
-  <div class="brand"><b>USRealEstate</b> — LA Brokers &amp; Firms</div>
+  <div class="brand"><b>USRealEstate</b> — Commercial Brokers &amp; Firms</div>
   <nav>
     <a href="/">Map</a><a href="/markets.html">Markets</a>
     <a href="/commercial.html">Commercial</a>
@@ -47,60 +52,13 @@
   </nav>
 </div>
 <main>
-  <p class="lede">Real estate firms &amp; licensed brokers operating in the LA region, from the California DRE
-    current-licensee registry. Firms rank by licensed-agent count — the largest are the major commercial houses.</p>
-  <div id="controls">
-    <div id="tabs"><button data-t="firms" class="active">Firms</button><button data-t="brokers">Brokers</button></div>
-    <select id="city">
-      <option value="LOS ANGELES">Los Angeles</option><option value="BEVERLY HILLS">Beverly Hills</option>
-      <option value="SANTA MONICA">Santa Monica</option><option value="LONG BEACH">Long Beach</option>
-      <option value="BURBANK">Burbank</option><option value="GLENDALE">Glendale</option>
-      <option value="PASADENA">Pasadena</option><option value="">All CA</option>
-    </select>
-    <input id="q" placeholder="Search name / firm / license…" style="width:220px">
-    <span id="count"></span>
-  </div>
-  <div id="grid"></div>
-  <div id="loading">Loading…</div>
+  <p class="lede">Commercial real estate firms &amp; licensed brokers operating in the LA region, from the California DRE
+    current-licensee registry (commercial-classified). Firms rank by licensed-agent count — the largest are the major
+    commercial houses. Sort &amp; density controls above; page through with Prev/Next.</p>
+  <div id="desk-root"></div>
 </main>
-<script>
-const $=s=>document.querySelector(s);
-const state={tab:'firms',city:'LOS ANGELES',q:''};
-async function j(u){const r=await fetch(u);return r.json();}
-function setCount(rows,total,noun){$('#count').textContent = (rows<total) ? ('showing first '+rows.toLocaleString()+' of '+total.toLocaleString()+' '+noun+' — refine by city/search') : (total.toLocaleString()+' '+noun);}
-function params(){const p=new URLSearchParams({state:'CA',limit:200});if(state.city)p.set('city',state.city);if(state.q)p.set('q',state.q);return p;}
-async function loadFirms(){
-  const d=await j('/api/firms?'+params());
-  setCount((d.rows||[]).length, d.total||0, 'firms');
-  $('#grid').innerHTML=d.rows.map(f=>`<div class="card firm" data-id="${f.id}" data-name="${(f.name||'').replace(/"/g,'&quot;')}">
-    <div class="n">${f.name||'—'}</div>
-    <div class="m"><span class="agents">${f.agent_count||0} agents</span><span>${f.hq_city||''}</span>${f.license_no?`<span>DRE ${f.license_no}</span>`:''}</div>
-    <div class="roster"></div></div>`).join('');
-  document.querySelectorAll('.card.firm').forEach(c=>c.onclick=()=>toggleRoster(c));
-}
-async function toggleRoster(c){
-  const r=c.querySelector('.roster');
-  if(r.style.display==='block'){r.style.display='none';return;}
-  r.style.display='block';
-  if(r.dataset.loaded){return;}
-  r.innerHTML='loading roster…';
-  const d=await j('/api/brokers?state=CA&limit=40&firm='+encodeURIComponent(c.dataset.name));
-  r.dataset.loaded='1';
-  r.innerHTML=d.rows.slice(0,30).map(b=>`<div>${b.name} <span style="color:var(--muted)">· ${b.license_type||''} · DRE ${b.license_no||''}</span></div>`).join('')||'<div style="color:var(--muted)">no roster rows</div>';
-}
-async function loadBrokers(){
-  const d=await j('/api/brokers?'+params());
-  setCount((d.rows||[]).length, d.total||0, 'brokers');
-  $('#grid').innerHTML=d.rows.map(b=>`<div class="card">
-    <div class="n">${b.name||'—'}</div>
-    <div class="m"><span class="lt">${b.license_type||''}</span><span>${b.firm_name||'—'}</span><span>${b.city||''}</span>${b.license_no?`<span>DRE ${b.license_no}</span>`:''}</div></div>`).join('');
-}
-async function load(){$('#loading').style.display='block';state.tab==='firms'?await loadFirms():await loadBrokers();$('#loading').style.display='none';}
-$('#tabs').querySelectorAll('button').forEach(b=>b.onclick=()=>{$('#tabs').querySelectorAll('button').forEach(x=>x.classList.remove('active'));b.classList.add('active');state.tab=b.dataset.t;load();});
-$('#city').onchange=()=>{state.city=$('#city').value;load();};
-$('#q').oninput=()=>{state.q=$('#q').value;clearTimeout(window._t);window._t=setTimeout(load,250);};
-load();
-</script>
+<script>window.DESK_MODE='commercial';</script>
+<script src="/broker-desk.js"></script>
 <script src="/nav-drawer.js"></script>
 <script src="/geo-search.js"></script>
   <script src="/nav-agent/nav-agent.js" defer></script><!-- nav-agent -->
diff --git a/public/residential-brokers.html b/public/residential-brokers.html
index 40525c5..58dc976 100644
--- a/public/residential-brokers.html
+++ b/public/residential-brokers.html
@@ -19,6 +19,8 @@
   #tabs button{background:var(--panel);color:var(--muted);border:0;border-right:1px solid var(--line);padding:7px 14px;font-size:13px;cursor:pointer;}
   #tabs button:last-child{border-right:0;} #tabs button.active{background:var(--gold);color:var(--bg);font-weight:700;}
   input,select{background:var(--panel);color:var(--fg);border:1px solid var(--line);border-radius:8px;padding:7px 10px;font-size:13px;}
+  #densWrap{display:inline-flex;align-items:center;gap:6px;color:var(--muted);font-size:13px;}
+  #dens{accent-color:var(--gold);}
   #count{font-size:12px;color:var(--muted);margin-left:auto;}
   #grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:10px;}
   .card{background:var(--panel);border:1px solid var(--line);border-radius:10px;padding:13px;}
@@ -29,6 +31,9 @@
   .lt{font-size:10px;letter-spacing:.05em;text-transform:uppercase;color:var(--gold);border:1px solid var(--line);border-radius:5px;padding:1px 6px;}
   .roster{margin-top:9px;padding-top:9px;border-top:1px solid var(--line);font-size:12px;color:#c3ccd8;display:none;}
   .roster div{padding:2px 0;}
+  #pager{display:flex;align-items:center;gap:10px;justify-content:center;margin-top:16px;font-size:13px;color:var(--muted);}
+  #pager button{background:var(--panel);color:var(--fg);border:1px solid var(--line);border-radius:8px;padding:6px 12px;cursor:pointer;}
+  #pager button:disabled{opacity:.4;cursor:default;}
   #loading{padding:40px;text-align:center;color:var(--muted);}
 </style>
 <link rel="stylesheet" href="/nav-drawer.css">
@@ -47,65 +52,12 @@
 </div>
 <main>
   <p class="lede">Residential real estate firms &amp; licensed agents across the region, from the California DRE
-    current-licensee registry. This directory is the residential counterpart to the commercial desk — the
-    ~204k residential-classified agents that don't appear on the commercial (RENTV) surface. Firms rank by
-    licensed-agent count.</p>
-  <div id="controls">
-    <div id="tabs"><button data-t="firms" class="active">Firms</button><button data-t="brokers">Agents</button></div>
-    <select id="city">
-      <option value="LOS ANGELES">Los Angeles</option><option value="BEVERLY HILLS">Beverly Hills</option>
-      <option value="SANTA MONICA">Santa Monica</option><option value="LONG BEACH">Long Beach</option>
-      <option value="BURBANK">Burbank</option><option value="GLENDALE">Glendale</option>
-      <option value="PASADENA">Pasadena</option><option value="">All CA</option>
-    </select>
-    <input id="q" placeholder="Search name / firm / license…" style="width:220px">
-    <span id="count"></span>
-  </div>
-  <div id="grid"></div>
-  <div id="loading">Loading…</div>
+    current-licensee registry — the residential counterpart to the commercial desk (the ~204k residential-classified
+    agents that don't appear on the commercial/RENTV surface). Sort &amp; density controls above; page through with Prev/Next.</p>
+  <div id="desk-root"></div>
 </main>
-<script>
-const $=s=>document.querySelector(s);
-const state={tab:'firms',city:'LOS ANGELES',q:''};
-async function j(u){const r=await fetch(u);return r.json();}
-// Honest count: this desk pulls a capped page (limit 200). Never show the full total next to a
-// truncated card list — say "first N of TOTAL" so the number matches what's on screen (Cody gate).
-function setCount(rows,total,noun){$('#count').textContent = (rows<total) ? ('showing first '+rows.toLocaleString()+' of '+total.toLocaleString()+' '+noun+' — refine by city/search') : (total.toLocaleString()+' '+noun);}
-// Residential desk — force asset_class=residential on every registry call so this surface only ever
-// shows residential firms/agents (the mirror of RENTV forcing commercial). TK-10590.
-function params(){const p=new URLSearchParams({state:'CA',limit:200,asset_class:'residential'});if(state.city)p.set('city',state.city);if(state.q)p.set('q',state.q);return p;}
-async function loadFirms(){
-  const d=await j('/api/firms?'+params());
-  setCount((d.rows||[]).length, d.total||0, 'firms');
-  $('#grid').innerHTML=d.rows.map(f=>`<div class="card firm" data-id="${f.id}" data-name="${(f.name||'').replace(/"/g,'&quot;')}">
-    <div class="n">${f.name||'—'}</div>
-    <div class="m"><span class="agents">${f.agent_count||0} agents</span><span>${f.hq_city||''}</span>${f.license_no?`<span>DRE ${f.license_no}</span>`:''}</div>
-    <div class="roster"></div></div>`).join('');
-  document.querySelectorAll('.card.firm').forEach(c=>c.onclick=()=>toggleRoster(c));
-}
-async function toggleRoster(c){
-  const r=c.querySelector('.roster');
-  if(r.style.display==='block'){r.style.display='none';return;}
-  r.style.display='block';
-  if(r.dataset.loaded){return;}
-  r.innerHTML='loading roster…';
-  const d=await j('/api/brokers?state=CA&limit=40&asset_class=residential&firm='+encodeURIComponent(c.dataset.name));
-  r.dataset.loaded='1';
-  r.innerHTML=d.rows.slice(0,30).map(b=>`<div>${b.name} <span style="color:var(--muted)">· ${b.license_type||''} · DRE ${b.license_no||''}</span></div>`).join('')||'<div style="color:var(--muted)">no roster rows</div>';
-}
-async function loadBrokers(){
-  const d=await j('/api/brokers?'+params());
-  setCount((d.rows||[]).length, d.total||0, 'agents');
-  $('#grid').innerHTML=d.rows.map(b=>`<div class="card">
-    <div class="n">${b.name||'—'}</div>
-    <div class="m"><span class="lt">${b.license_type||''}</span><span>${b.firm_name||'—'}</span><span>${b.city||''}</span>${b.license_no?`<span>DRE ${b.license_no}</span>`:''}</div></div>`).join('');
-}
-async function load(){$('#loading').style.display='block';state.tab==='firms'?await loadFirms():await loadBrokers();$('#loading').style.display='none';}
-$('#tabs').querySelectorAll('button').forEach(b=>b.onclick=()=>{$('#tabs').querySelectorAll('button').forEach(x=>x.classList.remove('active'));b.classList.add('active');state.tab=b.dataset.t;load();});
-$('#city').onchange=()=>{state.city=$('#city').value;load();};
-$('#q').oninput=()=>{state.q=$('#q').value;clearTimeout(window._t);window._t=setTimeout(load,250);};
-load();
-</script>
+<script>window.DESK_MODE='residential';</script>
+<script src="/broker-desk.js"></script>
 <script src="/nav-drawer.js"></script>
 <script src="/geo-search.js"></script>
   <script src="/nav-agent/nav-agent.js" defer></script><!-- nav-agent -->

← 5a16e24 usreal brokers: honest count (show 'first 200 of N' not full  ·  back to Nationalrealestate  ·  usreal broker desk: honest page-scoped sort labels + roster 53f52a8 →