[object Object]

← back to Rentv

Add 10 unique back-office admin layouts (A01-A10) + refined flagship front page (bottom breaking bar, CRE data strip)

874354372738f00bd0655bb202c22fd4ea1108fd · 2026-07-22 14:28:50 -0700 · Steve

Files touched

Diff

commit 874354372738f00bd0655bb202c22fd4ea1108fd
Author: Steve <steve@designerwallcoverings.com>
Date:   Wed Jul 22 14:28:50 2026 -0700

    Add 10 unique back-office admin layouts (A01-A10) + refined flagship front page (bottom breaking bar, CRE data strip)
---
 mockups/admin/A01.html      |  54 +++++++++++
 mockups/admin/A02.html      |  39 ++++++++
 mockups/admin/A03.html      |  65 ++++++++++++++
 mockups/admin/A04.html      |  41 +++++++++
 mockups/admin/A05.html      |  37 ++++++++
 mockups/admin/A06.html      |  50 +++++++++++
 mockups/admin/A07.html      |  47 ++++++++++
 mockups/admin/A08.html      |  41 +++++++++
 mockups/admin/A09.html      |  51 +++++++++++
 mockups/admin/A10.html      |  55 ++++++++++++
 mockups/admin/admin-core.js |  41 +++++++++
 mockups/admin/admin.css     |  15 ++++
 mockups/admin/index.html    | 212 +++++++++-----------------------------------
 13 files changed, 580 insertions(+), 168 deletions(-)

diff --git a/mockups/admin/A01.html b/mockups/admin/A01.html
new file mode 100644
index 00000000..01ab9b03
--- /dev/null
+++ b/mockups/admin/A01.html
@@ -0,0 +1,54 @@
+<!doctype html><html lang="en"><head>
+<meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
+<title>A01 · DataGrid — RENTV Admin</title>
+<link rel="stylesheet" href="../assets/base.css"><link rel="stylesheet" href="admin.css">
+<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
+<style>
+  body{background:#0d1014;color:#e7ecf1;font-family:'Inter',system-ui,sans-serif;font-size:13.5px}
+  .wrap{max-width:1300px;margin:0 auto;padding:14px 20px}
+  .bar{display:flex;gap:10px;flex-wrap:wrap;align-items:center;margin:8px 0 12px}
+  .bar input{flex:1;min-width:240px;background:#11161c;border:1px solid #232a32;border-radius:9px;color:#e7ecf1;padding:10px 13px;font:inherit}
+  .bar select{background:#11161c;border:1px solid #232a32;border-radius:9px;color:#e7ecf1;padding:10px;font:inherit}
+  table{width:100%;border-collapse:collapse;background:#11161c;border:1px solid #232a32;border-radius:10px;overflow:hidden}
+  th{position:sticky;top:52px;background:#0e141a;text-align:left;font-size:11px;text-transform:uppercase;letter-spacing:.06em;color:#8b939b;padding:11px 14px;border-bottom:1px solid #232a32;cursor:pointer}
+  td{padding:11px 14px;border-bottom:1px solid #171d24;vertical-align:top}
+  tr.r{cursor:pointer}tr.r:hover td{background:#141b22}
+  .nm{font-weight:700}.mono{font-family:ui-monospace,Menlo,monospace;color:#9aa4ad;font-size:12px}
+  tr.exp td{background:#0e141a;border-bottom:1px solid #232a32}
+  .expwrap{display:none}tr.open+tr.exp .expwrap{display:block}
+  .expwrap .kv{margin-top:10px}
+</style></head>
+<body>
+<div class="admhead" data-eln><h1>RENTV Admin · <span>DataGrid</span></h1><div class="fresh" id="fresh"></div></div>
+<div class="admtabs" data-eln id="tabs"><button data-tab="brokers" class="on">Brokers <span class="c" id="cB">0</span></button><button data-tab="firms">Firms <span class="c" id="cF">0</span></button><button data-tab="owners">Owners <span class="c" id="cO">0</span></button></div>
+<div class="wrap">
+  <div class="bar" data-eln><input id="q" placeholder="Filter name, firm, city, license…"><select id="sort"><option value="name">Name A→Z</option><option value="firm">Firm</option><option value="city">City</option><option value="agents">Agents ↓</option></select><span style="color:#8b939b;font-size:12px" id="count"></span></div>
+  <table data-eln><thead id="thead"></thead><tbody id="tbody"></tbody></table>
+</div>
+<script src="../assets/chrome.js"></script><script src="admin-core.js"></script>
+<script>
+let S={tab:'brokers'};
+ADM.mount('A01', d=>{
+  S.d=d; document.getElementById('cB').textContent=d.totalB.toLocaleString();document.getElementById('cF').textContent=d.totalF.toLocaleString();document.getElementById('cO').textContent=d.owners.length;
+  document.getElementById('fresh').textContent='CA DRE registry · '+d.brokers.length+' brokers loaded of '+d.totalB.toLocaleString()+' statewide';
+  const esc=ADM.esc;
+  const cols={brokers:['Broker','Firm','City','License','Status'],firms:['Firm','HQ','Agents','License'],owners:['Address','Type','Assessed','Recorded']};
+  function rows(){let r=(S.tab==='brokers'?d.brokers:S.tab==='firms'?d.firms:d.owners).slice();const q=(document.getElementById('q').value||'').toLowerCase();if(q)r=r.filter(x=>JSON.stringify(x).toLowerCase().includes(q));const s=document.getElementById('sort').value;const k={name:'name',firm:S.tab==='brokers'?'firm_name':'name',city:S.tab==='firms'?'hq_city':'city',agents:'agent_count'}[s];r.sort((a,b)=>s==='agents'?(b.agent_count||0)-(a.agent_count||0):String(a[k]||a.name||a.address||'').localeCompare(String(b[k]||b.name||b.address||'')));return r;}
+  function render(){
+    document.getElementById('thead').innerHTML='<tr>'+cols[S.tab].map(c=>`<th>${c}</th>`).join('')+'</tr>';
+    const r=rows();
+    document.getElementById('tbody').innerHTML=r.map((x,i)=>{
+      let cells;
+      if(S.tab==='brokers')cells=`<td class="nm">${esc(x.name)}</td><td><a class="field-link" href="${ADM.LI_C(x.firm_name||'')}" target="_blank" onclick="event.stopPropagation()">${esc(x.firm_name||'—')}</a></td><td>${esc(x.city||'—')}</td><td class="mono"><a class="field-link" href="${ADM.DRE(x.license_no||'')}" target="_blank" onclick="event.stopPropagation()">${esc(x.license_no||'—')}</a></td><td>${esc(x.license_status||'—')}</td>`;
+      else if(S.tab==='firms')cells=`<td class="nm">${esc(x.name)}</td><td>${esc(ADM.loc(x)||'—')}</td><td><b>${(x.agent_count||0).toLocaleString()}</b></td><td class="mono">${esc(x.license_no||'—')}</td>`;
+      else cells=`<td class="nm">${esc(x.address||x.ain)}</td><td>${esc(x.type_label||'')}</td><td><b>${RENTV.shortUsd(x.assessed_total)}</b></td><td>${RENTV.dstr(x.recording_date)}</td>`;
+      return `<tr class="r" data-i="${i}">${cells}</tr><tr class="exp"><td colspan="${cols[S.tab].length}"><div class="expwrap"><div class="acts">${ADM.actions(S.tab,x)}</div></div></td></tr>`;
+    }).join('');
+    document.getElementById('count').textContent=r.length+' shown';
+    document.querySelectorAll('tr.r').forEach(tr=>tr.onclick=()=>tr.classList.toggle('open'));
+  }
+  document.getElementById('tabs').onclick=e=>{const b=e.target.closest('button');if(!b)return;document.querySelectorAll('#tabs button').forEach(x=>x.classList.remove('on'));b.classList.add('on');S.tab=b.dataset.tab;render();};
+  document.getElementById('q').oninput=render;document.getElementById('sort').onchange=render;
+  render();
+});
+</script></body></html>
diff --git a/mockups/admin/A02.html b/mockups/admin/A02.html
new file mode 100644
index 00000000..170a1542
--- /dev/null
+++ b/mockups/admin/A02.html
@@ -0,0 +1,39 @@
+<!doctype html><html lang="en"><head>
+<meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
+<title>A02 · Kanban — RENTV Admin</title>
+<link rel="stylesheet" href="../assets/base.css"><link rel="stylesheet" href="admin.css">
+<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
+<style>
+  body{background:#0d1014;color:#e7ecf1;font-family:'Inter',system-ui,sans-serif}
+  .wrap{max-width:1360px;margin:0 auto;padding:14px 18px}
+  .board{display:grid;grid-auto-flow:column;grid-auto-columns:300px;gap:14px;overflow-x:auto;padding-bottom:10px}
+  .col{background:#0f141a;border:1px solid #232a32;border-radius:12px;display:flex;flex-direction:column;max-height:calc(100vh - 130px)}
+  .col h3{margin:0;padding:13px 14px;font-size:12px;letter-spacing:.05em;text-transform:uppercase;color:#cfd6dd;border-bottom:1px solid #232a32;display:flex;justify-content:space-between}
+  .col h3 .n{color:#8b939b;background:#161d25;border-radius:20px;padding:1px 8px;font-size:11px}
+  .col .body{overflow-y:auto;padding:10px;display:flex;flex-direction:column;gap:9px}
+  .card{background:#141b22;border:1px solid #232a32;border-radius:10px;padding:11px 12px;cursor:pointer}
+  .card:hover{border-color:#3a4652}
+  .card .nm{font-weight:700;font-size:14px}
+  .card .f{font-size:12px;color:#9aa4ad;margin-top:2px}
+  .card .lic{font-size:11px;color:#37d67a;font-family:ui-monospace,monospace;margin-top:5px}
+  .card .exp{display:none;margin-top:9px;padding-top:9px;border-top:1px solid #232a32}
+  .card.open .exp{display:block}
+</style></head>
+<body>
+<div class="admhead" data-eln><h1>RENTV Admin · <span>Pipeline Board</span></h1><div class="fresh" id="fresh"></div></div>
+<div class="wrap"><div class="board" data-eln id="board"></div></div>
+<script src="../assets/chrome.js"></script><script src="admin-core.js"></script>
+<script>
+ADM.mount('A02', d=>{
+  const esc=ADM.esc;
+  document.getElementById('fresh').textContent='Brokers grouped by license type · '+d.brokers.length+' of '+d.totalB.toLocaleString()+' CA licensees';
+  // columns = license type (a natural "pipeline" grouping)
+  const groups={};
+  d.brokers.forEach(b=>{const g=b.license_type||'Other';(groups[g]=groups[g]||[]).push(b);});
+  const order=Object.keys(groups).sort((a,b)=>groups[b].length-groups[a].length);
+  document.getElementById('board').innerHTML=order.map(g=>`<div class="col"><h3>${esc(g)}<span class="n">${groups[g].length}</span></h3><div class="body">${
+    groups[g].slice(0,40).map(b=>`<div class="card"><div class="nm">${esc(b.name)}</div><div class="f">${esc(b.firm_name||'—')} · ${esc(b.city||'')}</div><div class="lic">DRE ${esc(b.license_no||'')}</div><div class="exp"><div class="acts">${ADM.actions('brokers',b)}</div></div></div>`).join('')
+  }</div></div>`).join('');
+  document.querySelectorAll('.card').forEach(c=>c.onclick=e=>{if(e.target.closest('a'))return;c.classList.toggle('open');});
+});
+</script></body></html>
diff --git a/mockups/admin/A03.html b/mockups/admin/A03.html
new file mode 100644
index 00000000..0cd197eb
--- /dev/null
+++ b/mockups/admin/A03.html
@@ -0,0 +1,65 @@
+<!doctype html><html lang="en"><head>
+<meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
+<title>A03 · Master-Detail — RENTV Admin</title>
+<link rel="stylesheet" href="../assets/base.css"><link rel="stylesheet" href="admin.css">
+<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
+<style>
+  body{background:#0d1014;color:#e7ecf1;font-family:'Inter',system-ui,sans-serif}
+  .split{display:grid;grid-template-columns:380px 1fr;height:calc(100vh - 52px)}
+  .master{border-right:1px solid #232a32;display:flex;flex-direction:column;min-height:0}
+  .msearch{padding:12px;border-bottom:1px solid #232a32}
+  .msearch input{width:100%;background:#11161c;border:1px solid #232a32;border-radius:9px;color:#e7ecf1;padding:10px 12px;font:inherit}
+  .mtabs{display:flex;gap:4px;padding:8px 12px 0}
+  .mtabs button{background:none;border:0;color:#8b939b;font:800 12px system-ui;padding:7px 10px;cursor:pointer;border-radius:7px}.mtabs button.on{color:#fff;background:#11161c}
+  .mlist{overflow-y:auto;flex:1}
+  .mrow{padding:12px 14px;border-bottom:1px solid #171d24;cursor:pointer}
+  .mrow:hover{background:#141b22}.mrow.sel{background:#11161c;border-left:3px solid #c8102e}
+  .mrow .nm{font-weight:700;font-size:14px}.mrow .f{font-size:12px;color:#9aa4ad;margin-top:2px}
+  .detail{overflow-y:auto;padding:34px 40px}
+  .detail .k{font-size:11px;letter-spacing:.1em;text-transform:uppercase;color:#c8102e;font-weight:800}
+  .detail h2{font-size:34px;font-weight:800;margin:6px 0 4px;letter-spacing:-.5px}
+  .detail .meta{color:#9aa4ad;font-size:15px}
+  .detail .acts{margin:22px 0}
+  .detail .grid{display:grid;grid-template-columns:1fr 1fr;gap:14px;margin-top:20px}
+  .detail .cell{background:#11161c;border:1px solid #232a32;border-radius:10px;padding:14px}
+  .detail .cell .kk{font-size:11px;color:#8b939b;text-transform:uppercase;letter-spacing:.05em}.detail .cell .vv{font-size:18px;font-weight:700;margin-top:3px}
+  @media(max-width:820px){.split{grid-template-columns:1fr;height:auto}.master{height:50vh}}
+</style></head>
+<body>
+<div class="split">
+  <div class="master">
+    <div class="msearch" data-eln><input id="q" placeholder="Search the registry…"></div>
+    <div class="mtabs" data-eln id="tabs"><button data-tab="brokers" class="on">Brokers</button><button data-tab="firms">Firms</button><button data-tab="owners">Owners</button></div>
+    <div class="mlist" data-eln id="mlist"></div>
+  </div>
+  <div class="detail" data-eln id="detail"></div>
+</div>
+<script src="../assets/chrome.js"></script><script src="admin-core.js"></script>
+<script>
+let S={tab:'brokers',sel:0};
+ADM.mount('A03', d=>{
+  S.d=d;const esc=ADM.esc;
+  function items(){let r=(S.tab==='brokers'?d.brokers:S.tab==='firms'?d.firms:d.owners).slice();const q=(document.getElementById('q').value||'').toLowerCase();if(q)r=r.filter(x=>JSON.stringify(x).toLowerCase().includes(q));return r;}
+  function detail(x){
+    if(!x){document.getElementById('detail').innerHTML='<p style="color:#6b747d">Select a record.</p>';return;}
+    const grid=S.tab==='owners'
+      ? [['Assessed',RENTV.shortUsd(x.assessed_total)],['SqFt',(x.sqft||0).toLocaleString()],['Built',x.year_built||'—'],['AIN',x.ain]]
+      : S.tab==='firms'
+      ? [['Agents',(x.agent_count||0).toLocaleString()],['HQ',ADM.loc(x)||'—'],['License',x.license_no||'—'],['Source',x.source||'—']]
+      : [['License',x.license_no||'—'],['Type',x.license_type||'—'],['Firm',x.firm_name||'—'],['City',x.city||'—']];
+    document.getElementById('detail').innerHTML=`<div class="k">${esc(S.tab==='owners'?(x.type_label||'Asset'):S.tab==='firms'?'Brokerage':(x.license_type||'Licensee'))}</div>
+      <h2>${esc(ADM.nm(x))}</h2><div class="meta">${esc(S.tab==='owners'?[x.address,x.city].filter(Boolean).join(', '):(ADM.sub(S.tab,x)+' · '+(ADM.loc(x)||'')))}</div>
+      <div class="acts">${ADM.actions(S.tab,x)}</div>
+      <div class="grid">${grid.map(([k,v])=>`<div class="cell"><div class="kk">${k}</div><div class="vv">${esc(v)}</div></div>`).join('')}</div>`;
+  }
+  function render(){
+    const r=items();
+    document.getElementById('mlist').innerHTML=r.map((x,i)=>`<div class="mrow${i===S.sel?' sel':''}" data-i="${i}"><div class="nm">${esc(ADM.nm(x))}</div><div class="f">${esc(ADM.sub(S.tab,x))}</div></div>`).join('');
+    document.querySelectorAll('.mrow').forEach(row=>row.onclick=()=>{S.sel=+row.dataset.i;document.querySelectorAll('.mrow').forEach(x=>x.classList.remove('sel'));row.classList.add('sel');detail(r[S.sel]);});
+    detail(r[S.sel]||r[0]);
+  }
+  document.getElementById('tabs').onclick=e=>{const b=e.target.closest('button');if(!b)return;document.querySelectorAll('#tabs button').forEach(x=>x.classList.remove('on'));b.classList.add('on');S.tab=b.dataset.tab;S.sel=0;render();};
+  document.getElementById('q').oninput=()=>{S.sel=0;render();};
+  render();
+});
+</script></body></html>
diff --git a/mockups/admin/A04.html b/mockups/admin/A04.html
new file mode 100644
index 00000000..20f2a956
--- /dev/null
+++ b/mockups/admin/A04.html
@@ -0,0 +1,41 @@
+<!doctype html><html lang="en"><head>
+<meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
+<title>A04 · Rolodex — RENTV Admin</title>
+<link rel="stylesheet" href="../assets/base.css"><link rel="stylesheet" href="admin.css">
+<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
+<style>
+  body{background:#0d1014;color:#e7ecf1;font-family:'Inter',system-ui,sans-serif}
+  .wrap{max-width:1300px;margin:0 auto;padding:14px 20px}
+  .bar{display:flex;gap:10px;align-items:center;margin:8px 0 14px}
+  .bar input{flex:1;background:#11161c;border:1px solid #232a32;border-radius:9px;color:#e7ecf1;padding:11px 14px;font:inherit}
+  .grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:14px}
+  .card{background:#11161c;border:1px solid #232a32;border-radius:14px;padding:16px;display:flex;flex-direction:column;gap:9px}
+  .card .top{display:flex;gap:12px;align-items:center}
+  .av{width:46px;height:46px;border-radius:50%;flex:none;display:flex;align-items:center;justify-content:center;font-weight:800;font-size:17px;color:#fff}
+  .card .nm{font-weight:800;font-size:16px;line-height:1.15}.card .f{font-size:12.5px;color:#9aa4ad}
+  .card .row{display:flex;gap:8px;flex-wrap:wrap;font-size:12px}
+  .card .row a{color:#7cc0ff;text-decoration:none;border-bottom:1px dotted #3a5064}
+  .card .acts{border-top:1px solid #232a32;padding-top:11px}
+</style></head>
+<body>
+<div class="admhead" data-eln><h1>RENTV Admin · <span>Broker Rolodex</span></h1><div class="fresh" id="fresh"></div></div>
+<div class="wrap">
+  <div class="bar" data-eln><input id="q" placeholder="Search brokers by name, firm, city…"><span style="color:#8b939b;font-size:12px" id="count"></span></div>
+  <div class="grid" data-eln id="grid"></div>
+</div>
+<script src="../assets/chrome.js"></script><script src="admin-core.js"></script>
+<script>
+ADM.mount('A04', d=>{
+  const esc=ADM.esc;const hue=s=>((String(s).length*47)%360);
+  document.getElementById('fresh').textContent=d.brokers.length+' CA brokers · click any action to reach them';
+  function render(){
+    let r=d.brokers.slice();const q=(document.getElementById('q').value||'').toLowerCase();if(q)r=r.filter(x=>JSON.stringify(x).toLowerCase().includes(q));
+    document.getElementById('grid').innerHTML=r.map(b=>{const ini=(b.name||'?').split(/[\s,]+/).filter(Boolean).slice(0,2).map(w=>w[0]).join('').toUpperCase();
+      return `<div class="card"><div class="top"><div class="av" style="background:hsl(${hue(b.name)} 45% 38%)">${esc(ini)}</div><div><div class="nm">${esc(b.name)}</div><div class="f">${esc(b.license_type||'Licensee')}</div></div></div>
+      <div class="row"><a href="${ADM.LI_C(b.firm_name||'')}" target="_blank" onclick="event.stopPropagation()">${esc(b.firm_name||'—')}</a> · <a href="${ADM.MAPS((b.firm_name||'')+' '+ADM.loc(b))}" target="_blank" onclick="event.stopPropagation()">${esc(b.city||'')}</a> · <a class="field-link" href="${ADM.DRE(b.license_no||'')}" target="_blank" onclick="event.stopPropagation()">DRE ${esc(b.license_no||'')}</a></div>
+      <div class="acts"><div class="acts">${ADM.actions('brokers',b)}</div></div></div>`;}).join('');
+    document.getElementById('count').textContent=r.length+' shown';
+  }
+  document.getElementById('q').oninput=render;render();
+});
+</script></body></html>
diff --git a/mockups/admin/A05.html b/mockups/admin/A05.html
new file mode 100644
index 00000000..0d4b57cb
--- /dev/null
+++ b/mockups/admin/A05.html
@@ -0,0 +1,37 @@
+<!doctype html><html lang="en"><head>
+<meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
+<title>A05 · Map Desk — RENTV Admin</title>
+<link rel="stylesheet" href="../assets/base.css"><link rel="stylesheet" href="admin.css">
+<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
+<style>
+  body{background:#0d1014;color:#e7ecf1;font-family:'Inter',system-ui,sans-serif}
+  .layout{display:grid;grid-template-columns:1fr 360px;height:calc(100vh - 52px)}
+  .map{position:relative;background:radial-gradient(circle at 42% 38%,#1a2531,#0b1016);overflow:hidden}
+  .map .grid{position:absolute;inset:0;background-image:linear-gradient(rgba(255,255,255,.045) 1px,transparent 1px),linear-gradient(90deg,rgba(255,255,255,.045) 1px,transparent 1px);background-size:46px 46px}
+  .map .t{position:absolute;top:16px;left:18px;z-index:2}.map .t b{font-size:13px;letter-spacing:.14em;text-transform:uppercase;color:#eaf0f6}.map .t span{display:block;font-size:12px;color:#93a0ad;margin-top:3px}
+  .pin{position:absolute;transform:translate(-50%,-100%);cursor:pointer;z-index:3;text-align:center}
+  .pin .d{width:16px;height:16px;border-radius:50% 50% 50% 0;background:#c8102e;transform:rotate(-45deg);margin:0 auto;border:2px solid #fff;box-shadow:0 3px 8px rgba(0,0,0,.5)}
+  .pin .l{background:#0b0e12;border:1px solid #232a32;border-radius:6px;font-size:11px;font-weight:800;padding:3px 7px;margin-top:6px;white-space:nowrap;color:#e7ecf1}
+  .pin.sel .d{background:#37d67a}
+  .side{border-left:1px solid #232a32;overflow-y:auto;padding:20px}
+  .side .k{font-size:11px;letter-spacing:.1em;text-transform:uppercase;color:#c8102e;font-weight:800}
+  .side h2{font-size:24px;font-weight:800;margin:5px 0}.side .meta{color:#9aa4ad;font-size:14px}
+  .side .acts{margin:18px 0}
+  .side .kv{display:flex;flex-direction:column;gap:9px}.side .kv .r{display:flex;justify-content:space-between;border-bottom:1px solid #171d24;padding:8px 0;font-size:13px}.side .kv b{color:#e7ecf1}
+  @media(max-width:820px){.layout{grid-template-columns:1fr;height:auto}.map{height:56vh}}
+</style></head>
+<body>
+<div class="layout">
+  <div class="map" data-eln id="map"><div class="grid"></div><div class="t"><b>LA Commercial Map</b><span>Marquee assets by assessed value · click a pin</span></div></div>
+  <div class="side" data-eln id="side"></div>
+</div>
+<script src="../assets/chrome.js"></script><script src="admin-core.js"></script>
+<script>
+ADM.mount('A05', d=>{
+  const esc=ADM.esc;const mq=d.owners.slice(0,10);
+  mq.forEach((m,i)=>{const x=12+((i*41)%76),y=18+((i*31)%62);const p=document.createElement('div');p.className='pin'+(i===0?' sel':'');p.style.left=x+'%';p.style.top=y+'%';p.dataset.i=i;p.innerHTML=`<div class="d"></div><div class="l">${RENTV.shortUsd(m.assessed_total)}</div>`;document.getElementById('map').appendChild(p);});
+  function show(m){const addr=[m.address,m.city].filter(Boolean).join(', ');document.getElementById('side').innerHTML=`<div class="k">${esc(m.type_label||'Asset')} · ${esc(m.use_desc||'')}</div><h2>${esc(m.address||m.ain)}</h2><div class="meta">${esc(addr)}</div><div class="acts">${ADM.actions('owners',m)}</div><div class="kv"><div class="r"><span>Assessed</span><b>${RENTV.shortUsd(m.assessed_total)}</b></div><div class="r"><span>SqFt</span><b>${(m.sqft||0).toLocaleString()}</b></div><div class="r"><span>Built</span><b>${m.year_built||'—'}</b></div><div class="r"><span>Recorded</span><b>${RENTV.dstr(m.recording_date)}</b></div><div class="r"><span>AIN</span><b>${esc(m.ain)}</b></div></div>`;}
+  show(mq[0]);
+  document.querySelectorAll('.pin').forEach(p=>p.onclick=()=>{document.querySelectorAll('.pin').forEach(x=>x.classList.remove('sel'));p.classList.add('sel');show(mq[+p.dataset.i]);});
+});
+</script></body></html>
diff --git a/mockups/admin/A06.html b/mockups/admin/A06.html
new file mode 100644
index 00000000..d500b286
--- /dev/null
+++ b/mockups/admin/A06.html
@@ -0,0 +1,50 @@
+<!doctype html><html lang="en"><head>
+<meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
+<title>A06 · Command Palette — RENTV Admin</title>
+<link rel="stylesheet" href="../assets/base.css"><link rel="stylesheet" href="admin.css">
+<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
+<style>
+  body{background:#0d1014;color:#e7ecf1;font-family:'Inter',system-ui,sans-serif}
+  .stage{max-width:820px;margin:0 auto;padding:60px 20px}
+  .stage .hh{text-align:center;margin-bottom:22px}.stage .hh h1{font-size:26px;font-weight:800}.stage .hh h1 span{color:#c8102e}.stage .hh p{color:#8b939b;margin-top:6px;font-size:14px}
+  .palette{background:#11161c;border:1px solid #2a333d;border-radius:14px;box-shadow:0 30px 80px rgba(0,0,0,.5);overflow:hidden}
+  .palette .in{display:flex;align-items:center;gap:12px;padding:16px 18px;border-bottom:1px solid #232a32}
+  .palette .in .mag{font-size:18px;color:#8b939b}
+  .palette input{flex:1;background:none;border:0;color:#e7ecf1;font:600 18px 'Inter';outline:none}
+  .palette .scope{display:flex;gap:5px;padding:10px 14px;border-bottom:1px solid #232a32}
+  .palette .scope button{background:#161d25;border:0;color:#9aa4ad;font:800 11px system-ui;padding:6px 11px;border-radius:20px;cursor:pointer}.palette .scope button.on{background:#c8102e;color:#fff}
+  .results{max-height:52vh;overflow-y:auto}
+  .res{padding:12px 16px;border-bottom:1px solid #171d24;cursor:pointer;display:flex;justify-content:space-between;align-items:center;gap:12px}
+  .res:hover,.res.hot{background:#141b22}
+  .res .nm{font-weight:700}.res .f{font-size:12px;color:#9aa4ad;margin-top:2px}
+  .res .kbd{font-size:10px;color:#6b747d;border:1px solid #2a333d;border-radius:5px;padding:2px 6px}
+  .res .exp{display:none}.res.open{flex-direction:column;align-items:stretch}.res.open .exp{display:block;margin-top:10px}
+  .hint{text-align:center;color:#6b747d;font-size:12px;margin-top:14px}
+</style></head>
+<body>
+<div class="stage">
+  <div class="hh" data-eln><h1>RENTV <span>Command</span></h1><p>Search 380,000 California brokers and 34,000 firms — then act.</p></div>
+  <div class="palette" data-eln>
+    <div class="in"><span class="mag">⌘</span><input id="q" placeholder="Type a broker, firm, city, or license…" autofocus></div>
+    <div class="scope" id="scope"><button data-s="brokers" class="on">Brokers</button><button data-s="firms">Firms</button><button data-s="owners">Assets</button></div>
+    <div class="results" data-eln id="results"></div>
+  </div>
+  <div class="hint" data-eln>Enter opens the top result's actions · every result links out to LinkedIn, the firm, the map, and the DRE</div>
+</div>
+<script src="../assets/chrome.js"></script><script src="admin-core.js"></script>
+<script>
+let S={s:'brokers'};
+ADM.mount('A06', d=>{
+  S.d=d;const esc=ADM.esc;
+  function results(){let r=(S.s==='brokers'?d.brokers:S.s==='firms'?d.firms:d.owners).slice();const q=(document.getElementById('q').value||'').toLowerCase();if(q)r=r.filter(x=>JSON.stringify(x).toLowerCase().includes(q));return r.slice(0,40);}
+  function render(){
+    const r=results();
+    document.getElementById('results').innerHTML=r.map((x,i)=>`<div class="res${i===0?' hot':''}" data-i="${i}"><div><div class="nm">${esc(ADM.nm(x))}</div><div class="f">${esc(ADM.sub(S.s,x))}${ADM.loc(x)?' · '+esc(ADM.loc(x)):''}</div><div class="exp"><div class="acts">${ADM.actions(S.s,x)}</div></div></div><span class="kbd">↵</span></div>`).join('')||'<div class="res"><div class="f">No matches.</div></div>';
+    document.querySelectorAll('.res').forEach(el=>el.onclick=e=>{if(e.target.closest('a'))return;el.classList.toggle('open');});
+  }
+  document.getElementById('scope').onclick=e=>{const b=e.target.closest('button');if(!b)return;document.querySelectorAll('#scope button').forEach(x=>x.classList.remove('on'));b.classList.add('on');S.s=b.dataset.s;render();};
+  const q=document.getElementById('q');q.oninput=render;
+  q.onkeydown=e=>{if(e.key==='Enter'){const first=document.querySelector('.res');if(first)first.classList.add('open');}};
+  render();
+});
+</script></body></html>
diff --git a/mockups/admin/A07.html b/mockups/admin/A07.html
new file mode 100644
index 00000000..309e07e9
--- /dev/null
+++ b/mockups/admin/A07.html
@@ -0,0 +1,47 @@
+<!doctype html><html lang="en"><head>
+<meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
+<title>A07 · Firm Tree — RENTV Admin</title>
+<link rel="stylesheet" href="../assets/base.css"><link rel="stylesheet" href="admin.css">
+<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
+<style>
+  body{background:#0d1014;color:#e7ecf1;font-family:'Inter',system-ui,sans-serif}
+  .wrap{max-width:1040px;margin:0 auto;padding:14px 20px}
+  .bar{margin:8px 0 14px}.bar input{width:100%;background:#11161c;border:1px solid #232a32;border-radius:9px;color:#e7ecf1;padding:11px 14px;font:inherit}
+  .firm{background:#11161c;border:1px solid #232a32;border-radius:11px;margin-bottom:9px;overflow:hidden}
+  .firm>.h{display:flex;align-items:center;gap:12px;padding:14px 16px;cursor:pointer}
+  .firm>.h:hover{background:#141b22}
+  .firm>.h .car{color:#8b939b;transition:transform .15s}.firm.open>.h .car{transform:rotate(90deg)}
+  .firm .nm{font-weight:800;font-size:15px;flex:1}.firm .ct{font-size:12px;color:#9aa4ad}
+  .firm .kids{display:none;border-top:1px solid #232a32;background:#0e141a}
+  .firm.open .kids{display:block}
+  .kid{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:10px 16px 10px 40px;border-bottom:1px solid #171d24}
+  .kid .nm2{font-weight:600;font-size:13.5px}.kid .lic{font-size:11px;color:#37d67a;font-family:ui-monospace,monospace}
+  .firm .fa{padding:10px 16px 12px 40px}
+</style></head>
+<body>
+<div class="admhead" data-eln><h1>RENTV Admin · <span>Firm Org Tree</span></h1><div class="fresh" id="fresh"></div></div>
+<div class="wrap">
+  <div class="bar" data-eln><input id="q" placeholder="Search firms…"></div>
+  <div data-eln id="tree"></div>
+</div>
+<script src="../assets/chrome.js"></script><script src="admin-core.js"></script>
+<script>
+ADM.mount('A07', d=>{
+  const esc=ADM.esc;
+  // group brokers under their firm_name; list firms that have brokers, plus top firms
+  const byFirm={};d.brokers.forEach(b=>{const f=b.firm_name||'—';(byFirm[f]=byFirm[f]||[]).push(b);});
+  const firms=Object.keys(byFirm).sort((a,b)=>byFirm[b].length-byFirm[a].length);
+  document.getElementById('fresh').textContent=firms.length+' firms represented in the loaded broker set';
+  function render(){
+    const q=(document.getElementById('q').value||'').toLowerCase();
+    const list=firms.filter(f=>!q||f.toLowerCase().includes(q)||byFirm[f].some(b=>b.name.toLowerCase().includes(q)));
+    document.getElementById('tree').innerHTML=list.map(f=>{const kids=byFirm[f];const rec=d.firms.find(x=>x.name&&f&&x.name.toLowerCase()===f.toLowerCase());
+      return `<div class="firm"><div class="h"><span class="car">▶</span><span class="nm">${esc(f)}</span><span class="ct">${kids.length} broker${kids.length>1?'s':''}${rec?' · '+(rec.agent_count||0).toLocaleString()+' statewide':''}</span></div>
+      <div class="kids"><div class="fa"><div class="acts">${ADM.actions('firms',rec||{name:f,hq_city:kids[0].city,hq_state:kids[0].state_code})}</div></div>${
+        kids.map(b=>`<div class="kid"><div><div class="nm2">${esc(b.name)}</div><span class="lic">DRE ${esc(b.license_no||'')} · ${esc(b.city||'')}</span></div><div class="acts">${ADM.actions('brokers',b)}</div></div>`).join('')
+      }</div></div>`;}).join('');
+    document.querySelectorAll('.firm>.h').forEach(h=>h.onclick=()=>h.parentElement.classList.toggle('open'));
+  }
+  document.getElementById('q').oninput=render;render();
+});
+</script></body></html>
diff --git a/mockups/admin/A08.html b/mockups/admin/A08.html
new file mode 100644
index 00000000..f022c0d1
--- /dev/null
+++ b/mockups/admin/A08.html
@@ -0,0 +1,41 @@
+<!doctype html><html lang="en"><head>
+<meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
+<title>A08 · Activity Feed — RENTV Admin</title>
+<link rel="stylesheet" href="../assets/base.css"><link rel="stylesheet" href="admin.css">
+<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
+<style>
+  body{background:#0d1014;color:#e7ecf1;font-family:'Inter',system-ui,sans-serif}
+  .wrap{max-width:860px;margin:0 auto;padding:14px 20px}
+  .tl{position:relative;padding-left:30px;margin-top:8px}
+  .tl::before{content:"";position:absolute;left:8px;top:6px;bottom:6px;width:2px;background:#232a32}
+  .ev{position:relative;padding-bottom:20px}
+  .ev::before{content:"";position:absolute;left:-30px;top:3px;width:14px;height:14px;border-radius:50%;background:#0d1014;border:3px solid #c8102e}
+  .ev.firm::before{border-color:#37d67a}.ev.broker::before{border-color:#7cc0ff}
+  .ev .t{font-size:11px;color:#8b939b;font-weight:700;text-transform:uppercase;letter-spacing:.05em}
+  .ev .card{background:#11161c;border:1px solid #232a32;border-radius:11px;padding:13px 15px;margin-top:5px}
+  .ev .nm{font-weight:700;font-size:15px}.ev .f{font-size:12.5px;color:#9aa4ad;margin-top:2px}
+  .ev .acts{margin-top:11px}
+  .ev .metric{float:right;font-weight:800}
+</style></head>
+<body>
+<div class="admhead" data-eln><h1>RENTV Admin · <span>Activity Feed</span></h1><div class="fresh" id="fresh"></div></div>
+<div class="wrap"><div class="tl" data-eln id="tl"></div></div>
+<script src="../assets/chrome.js"></script><script src="admin-core.js"></script>
+<script>
+ADM.mount('A08', d=>{
+  const esc=ADM.esc;
+  // interleave recorded transfers (owners, dated) with broker/firm registry events
+  const ev=[];
+  d.owners.forEach(o=>ev.push({k:'owner',date:o.recording_date,x:o}));
+  d.firms.slice(0,10).forEach(f=>ev.push({k:'firm',date:null,x:f}));
+  d.brokers.slice(0,14).forEach(b=>ev.push({k:'broker',date:null,x:b}));
+  // dated first (newest), then registry items
+  ev.sort((a,b)=>(new Date(b.date||0))-(new Date(a.date||0)));
+  document.getElementById('fresh').textContent='Recorded transfers + registry activity · newest first';
+  document.getElementById('tl').innerHTML=ev.map(e=>{
+    if(e.k==='owner'){const o=e.x;return `<div class="ev"><div class="t">${RENTV.dstr(o.recording_date)} · Recorded transfer</div><div class="card"><span class="metric">${RENTV.shortUsd(o.assessed_total)}</span><div class="nm">${esc(o.address||o.ain)}</div><div class="f">${esc(o.type_label)} · ${esc(o.use_desc||'')}</div><div class="acts">${ADM.actions('owners',o)}</div></div></div>`;}
+    if(e.k==='firm'){const f=e.x;return `<div class="ev firm"><div class="t">Registry · Brokerage</div><div class="card"><span class="metric">${(f.agent_count||0).toLocaleString()}</span><div class="nm">${esc(f.name)}</div><div class="f">${esc(ADM.loc(f)||'')} · ${(f.agent_count||0).toLocaleString()} agents</div><div class="acts">${ADM.actions('firms',f)}</div></div></div>`;}
+    const b=e.x;return `<div class="ev broker"><div class="t">Registry · ${esc(b.license_type||'Licensee')}</div><div class="card"><div class="nm">${esc(b.name)}</div><div class="f">${esc(b.firm_name||'—')} · ${esc(b.city||'')} · DRE ${esc(b.license_no||'')}</div><div class="acts">${ADM.actions('brokers',b)}</div></div></div>`;
+  }).join('');
+});
+</script></body></html>
diff --git a/mockups/admin/A09.html b/mockups/admin/A09.html
new file mode 100644
index 00000000..8842a20c
--- /dev/null
+++ b/mockups/admin/A09.html
@@ -0,0 +1,51 @@
+<!doctype html><html lang="en"><head>
+<meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
+<title>A09 · Analytics — RENTV Admin</title>
+<link rel="stylesheet" href="../assets/base.css"><link rel="stylesheet" href="admin.css">
+<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
+<style>
+  body{background:#0d1014;color:#e7ecf1;font-family:'Inter',system-ui,sans-serif}
+  .wrap{max-width:1240px;margin:0 auto;padding:16px 20px}
+  .kpis{display:grid;grid-template-columns:repeat(4,1fr);gap:14px;margin-bottom:16px}
+  .kpi{background:#11161c;border:1px solid #232a32;border-radius:12px;padding:16px}
+  .kpi .k{font-size:11px;letter-spacing:.06em;text-transform:uppercase;color:#8b939b}.kpi .v{font-size:30px;font-weight:800;margin-top:4px;letter-spacing:-1px}.kpi .v.red{color:#ff6b6b}
+  .cols{display:grid;grid-template-columns:1.3fr 1fr;gap:16px}
+  .panel{background:#11161c;border:1px solid #232a32;border-radius:12px;padding:16px}
+  .panel h3{margin:0 0 14px;font-size:12px;letter-spacing:.08em;text-transform:uppercase;color:#8b939b}
+  .bar{display:flex;align-items:center;gap:10px;margin-bottom:9px;cursor:pointer}
+  .bar .lab{width:150px;font-size:12.5px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
+  .bar .track{flex:1;height:22px;background:#0e141a;border-radius:5px;overflow:hidden}
+  .bar .fill{height:100%;background:linear-gradient(90deg,#c8102e,#e0455f);border-radius:5px}
+  .bar .val{width:64px;text-align:right;font-weight:800;font-size:13px}
+  .bar.open .acts{display:flex}.acts{display:none}
+  .barwrap.open .acts{display:flex;margin:2px 0 12px 160px}
+  .donut{display:flex;flex-direction:column;gap:8px}
+  .drow{display:flex;justify-content:space-between;align-items:center;font-size:13px;padding:8px 0;border-bottom:1px solid #171d24}
+  .dbadge{width:10px;height:10px;border-radius:3px;display:inline-block;margin-right:8px}
+  @media(max-width:820px){.kpis{grid-template-columns:1fr 1fr}.cols{grid-template-columns:1fr}}
+</style></head>
+<body>
+<div class="admhead" data-eln><h1>RENTV Admin · <span>Market Analytics</span></h1><div class="fresh" id="fresh"></div></div>
+<div class="wrap">
+  <div class="kpis" data-eln id="kpis"></div>
+  <div class="cols">
+    <div class="panel" data-eln><h3>Top brokerages by licensed agents</h3><div id="bars"></div></div>
+    <div class="panel" data-eln><h3>Broker mix by license type</h3><div class="donut" id="mix"></div></div>
+  </div>
+</div>
+<script src="../assets/chrome.js"></script><script src="admin-core.js"></script>
+<script>
+ADM.mount('A09', d=>{
+  const esc=ADM.esc;
+  document.getElementById('fresh').textContent='Live from the CA DRE registry + LA assessor';
+  const totAgents=d.firms.reduce((s,f)=>s+(f.agent_count||0),0);
+  const kpis=[['CA Brokers',d.totalB.toLocaleString()],['CA Firms',d.totalF.toLocaleString()],['Top firm',(d.firms[0]?.agent_count||0).toLocaleString()+' agents'],['Marquee assets',d.owners.length]];
+  document.getElementById('kpis').innerHTML=kpis.map(([k,v])=>`<div class="kpi"><div class="k">${k}</div><div class="v">${v}</div></div>`).join('');
+  const top=d.firms.slice().sort((a,b)=>(b.agent_count||0)-(a.agent_count||0)).slice(0,9);const max=top[0]?.agent_count||1;
+  document.getElementById('bars').innerHTML=top.map(f=>`<div class="barwrap"><div class="bar"><span class="lab" title="${esc(f.name)}">${esc(f.name)}</span><span class="track"><span class="fill" style="width:${Math.round((f.agent_count||0)/max*100)}%"></span></span><span class="val">${(f.agent_count||0).toLocaleString()}</span></div><div class="acts">${ADM.actions('firms',f)}</div></div>`).join('');
+  document.querySelectorAll('.barwrap').forEach(w=>w.querySelector('.bar').onclick=e=>{if(e.target.closest('a'))return;w.classList.toggle('open');});
+  const types={};d.brokers.forEach(b=>{const t=b.license_type||'Other';types[t]=(types[t]||0)+1;});
+  const pal=['#c8102e','#37d67a','#7cc0ff','#f59e0b','#a78bfa'];const ents=Object.entries(types).sort((a,b)=>b[1]-a[1]);
+  document.getElementById('mix').innerHTML=ents.map(([t,n],i)=>`<div class="drow"><span><span class="dbadge" style="background:${pal[i%pal.length]}"></span>${esc(t)}</span><b>${n}</b></div>`).join('');
+});
+</script></body></html>
diff --git a/mockups/admin/A10.html b/mockups/admin/A10.html
new file mode 100644
index 00000000..eb2992da
--- /dev/null
+++ b/mockups/admin/A10.html
@@ -0,0 +1,55 @@
+<!doctype html><html lang="en"><head>
+<meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
+<title>A10 · Triage Inbox — RENTV Admin</title>
+<link rel="stylesheet" href="../assets/base.css"><link rel="stylesheet" href="admin.css">
+<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
+<style>
+  body{background:#0d1014;color:#e7ecf1;font-family:'Inter',system-ui,sans-serif}
+  .three{display:grid;grid-template-columns:190px 360px 1fr;height:calc(100vh - 52px)}
+  .folders{border-right:1px solid #232a32;padding:14px 8px}
+  .folders button{display:flex;justify-content:space-between;width:100%;background:none;border:0;color:#c3cbd3;font:700 13px system-ui;padding:10px 12px;border-radius:8px;cursor:pointer;text-align:left}
+  .folders button.on{background:#11161c;color:#fff}.folders button .c{color:#8b939b;font-size:11px}
+  .msglist{border-right:1px solid #232a32;overflow-y:auto}
+  .msg{padding:12px 14px;border-bottom:1px solid #171d24;cursor:pointer}
+  .msg:hover{background:#141b22}.msg.sel{background:#11161c;border-left:3px solid #c8102e}
+  .msg .top{display:flex;justify-content:space-between}.msg .nm{font-weight:700;font-size:14px}.msg .tag{font-size:10px;color:#8b939b}
+  .msg .f{font-size:12px;color:#9aa4ad;margin-top:3px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
+  .preview{overflow-y:auto;padding:30px 36px}
+  .preview .k{font-size:11px;letter-spacing:.1em;text-transform:uppercase;color:#c8102e;font-weight:800}
+  .preview h2{font-size:28px;font-weight:800;margin:5px 0}.preview .meta{color:#9aa4ad;font-size:14px}
+  .preview .toolbar{margin:18px 0;padding-bottom:18px;border-bottom:1px solid #232a32}
+  .preview .kv{display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-top:18px}
+  .preview .cell{background:#11161c;border:1px solid #232a32;border-radius:10px;padding:13px}.preview .cell .kk{font-size:11px;color:#8b939b;text-transform:uppercase}.preview .cell .vv{font-size:17px;font-weight:700;margin-top:3px}
+  @media(max-width:900px){.three{grid-template-columns:1fr;height:auto}.msglist{max-height:44vh}}
+</style></head>
+<body>
+<div class="three">
+  <div class="folders" data-eln id="folders"></div>
+  <div class="msglist" data-eln id="msglist"></div>
+  <div class="preview" data-eln id="preview"></div>
+</div>
+<script src="../assets/chrome.js"></script><script src="admin-core.js"></script>
+<script>
+let S={tab:'brokers',sel:0};
+ADM.mount('A10', d=>{
+  S.d=d;const esc=ADM.esc;
+  const folders=[['brokers','Brokers',d.brokers.length],['firms','Firms',d.firms.length],['owners','Owners & Assets',d.owners.length]];
+  document.getElementById('folders').innerHTML=folders.map(([t,l,n])=>`<button data-t="${t}" class="${t===S.tab?'on':''}">${l}<span class="c">${n}</span></button>`).join('');
+  function list(){return S.tab==='brokers'?d.brokers:S.tab==='firms'?d.firms:d.owners;}
+  function preview(x){
+    if(!x){document.getElementById('preview').innerHTML='';return;}
+    const kv=S.tab==='owners'?[['Assessed',RENTV.shortUsd(x.assessed_total)],['SqFt',(x.sqft||0).toLocaleString()],['Built',x.year_built||'—'],['AIN',x.ain]]
+      :S.tab==='firms'?[['Agents',(x.agent_count||0).toLocaleString()],['HQ',ADM.loc(x)||'—'],['License',x.license_no||'—'],['Source',x.source||'—']]
+      :[['License',x.license_no||'—'],['Type',x.license_type||'—'],['Firm',x.firm_name||'—'],['City',x.city||'—']];
+    document.getElementById('preview').innerHTML=`<div class="k">${esc(S.tab==='owners'?(x.type_label||'Asset'):S.tab==='firms'?'Brokerage':(x.license_type||'Licensee'))}</div><h2>${esc(ADM.nm(x))}</h2><div class="meta">${esc(ADM.sub(S.tab,x))}${ADM.loc(x)?' · '+esc(ADM.loc(x)):''}</div><div class="toolbar"><div class="acts">${ADM.actions(S.tab,x)}</div></div><div class="kv">${kv.map(([k,v])=>`<div class="cell"><div class="kk">${k}</div><div class="vv">${esc(v)}</div></div>`).join('')}</div>`;
+  }
+  function render(){
+    const r=list();
+    document.getElementById('msglist').innerHTML=r.map((x,i)=>`<div class="msg${i===S.sel?' sel':''}" data-i="${i}"><div class="top"><span class="nm">${esc(ADM.nm(x))}</span><span class="tag">${esc(S.tab==='owners'?RENTV.dstr(x.recording_date):(x.license_no||''))}</span></div><div class="f">${esc(ADM.sub(S.tab,x))}${ADM.loc(x)?' · '+esc(ADM.loc(x)):''}</div></div>`).join('');
+    document.querySelectorAll('.msg').forEach(m=>m.onclick=()=>{S.sel=+m.dataset.i;document.querySelectorAll('.msg').forEach(x=>x.classList.remove('sel'));m.classList.add('sel');preview(r[S.sel]);});
+    preview(r[S.sel]||r[0]);
+  }
+  document.getElementById('folders').onclick=e=>{const b=e.target.closest('button');if(!b)return;document.querySelectorAll('#folders button').forEach(x=>x.classList.remove('on'));b.classList.add('on');S.tab=b.dataset.t;S.sel=0;render();};
+  render();
+});
+</script></body></html>
diff --git a/mockups/admin/admin-core.js b/mockups/admin/admin-core.js
new file mode 100644
index 00000000..2f3b67c6
--- /dev/null
+++ b/mockups/admin/admin-core.js
@@ -0,0 +1,41 @@
+/* RENTV admin core — shared across the 10 back-office layouts (A01..A10).
+   Owns: the real click-out action URLs (LinkedIn/website/Maps/firm/CA-DRE),
+   the broker/firm/owner data loader, and the inline action-button block.
+   Each A##.html supplies only its unique layout + render(). */
+window.ADM = (function () {
+  const enc = encodeURIComponent, esc = s => String(s == null ? '' : s).replace(/[&<>"]/g, c => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;' }[c]));
+  // ── real, functional click-outs ──
+  const LI_P = (n, f) => `https://www.linkedin.com/search/results/people/?keywords=${enc([n, f].filter(Boolean).join(' '))}`;
+  const LI_C = f => `https://www.linkedin.com/search/results/companies/?keywords=${enc(f)}`;
+  const WEB = f => `https://www.google.com/search?q=${enc('"' + f + '" real estate official website')}`;
+  const MAPS = q => `https://www.google.com/maps/search/?api=1&query=${enc(q)}`;
+  const DRE = lic => `https://www2.dre.ca.gov/PublicASP/pplinfo.asp?License_id=${enc(lic)}`;
+  const GOOG = q => `https://www.google.com/search?q=${enc(q)}`;
+  const ASSR = ain => `https://portal.assessor.lacounty.gov/parceldetail/${enc(ain)}`;
+  const loc = x => [x.city || x.hq_city, x.state_code || x.hq_state].filter(Boolean).join(', ');
+
+  const A = (href, icon, label) => `<a class="act" href="${href}" target="_blank" rel="noopener" onclick="event.stopPropagation()"><span class="i">${icon}</span>${label}</a>`;
+  // the inline action set for any record kind — the "like CRCP" click-outs
+  function actions(kind, x) {
+    if (kind === 'firms') return [A(LI_C(x.name), '🏢', 'LinkedIn'), A(WEB(x.name), '🌐', 'Website'), A(MAPS(x.name + ' ' + loc(x)), '📍', 'HQ / Map'), x.license_no ? A(DRE(x.license_no), '🪪', 'DRE') : '', A(GOOG(x.name + ' brokers ' + loc(x)), '🔎', 'Google')].filter(Boolean).join('');
+    if (kind === 'owners') { const addr = [x.address, x.city].filter(Boolean).join(', '); return [A(MAPS(addr), '📍', 'Map'), A(GOOG(addr + ' ' + (x.use_desc || '') + ' owner'), '🔎', 'Owner / deal'), A('https://www.google.com/maps/@?api=1&map_action=pano&viewpoint=' + enc(addr), '📸', 'Street View'), A(ASSR(x.ain), '🏛️', 'Assessor')].join(''); }
+    return [A(LI_P(x.name, x.firm_name), '👤', 'LinkedIn'), A(GOOG(x.name + ' ' + (x.firm_name || '') + ' ' + loc(x)), '🔎', 'Google'), x.firm_name ? A(WEB(x.firm_name), '🌐', 'Firm site') : '', x.firm_name ? A(LI_C(x.firm_name), '🏢', 'Firm') : '', loc(x) ? A(MAPS((x.firm_name || x.name) + ' ' + loc(x)), '📍', 'Map') : '', x.license_no ? A(DRE(x.license_no), '🪪', 'DRE') : ''].filter(Boolean).join('');
+  }
+  const nm = x => x.name || x.address || x.ain;
+  const sub = (kind, x) => kind === 'brokers' ? (x.firm_name || '—') : kind === 'firms' ? ((x.agent_count || 0).toLocaleString() + ' agents') : (x.type_label || '');
+
+  async function load() {
+    const get = (f, fb) => fetch('data/' + f).then(r => r.json()).catch(() => fb);
+    const [b, f, la] = await Promise.all([get('brokers.json', { rows: [] }), get('firms.json', { rows: [] }), get('la-commercial.json', { marquee: [] })]);
+    return {
+      brokers: b.rows || [], firms: f.rows || [], owners: la.marquee || [],
+      totalB: b.total || (b.rows || []).length, totalF: f.total || (f.rows || []).length,
+    };
+  }
+  // preload data FIRST, then mount chrome with a synchronous render → numbering sees the full DOM
+  async function mount(layout, render) {
+    const d = await load();
+    RENTV.mount({ dark: true, layout, render: () => render(d) });
+  }
+  return { esc, LI_P, LI_C, WEB, MAPS, DRE, GOOG, ASSR, loc, A, actions, nm, sub, load, mount };
+})();
diff --git a/mockups/admin/admin.css b/mockups/admin/admin.css
new file mode 100644
index 00000000..b30506b7
--- /dev/null
+++ b/mockups/admin/admin.css
@@ -0,0 +1,15 @@
+/* shared admin styling for A01..A10 (layout-agnostic bits) */
+.act{display:inline-flex;align-items:center;gap:6px;background:#12161c;border:1px solid #2a333d;border-radius:8px;padding:7px 11px;color:#dbe1e7;text-decoration:none;font-weight:700;font-size:12.5px;white-space:nowrap}
+.act:hover{border-color:#c8102e;color:#fff}.act .i{font-size:14px}
+.acts{display:flex;gap:7px;flex-wrap:wrap}
+.abadge{font-size:10px;font-weight:800;letter-spacing:.04em;text-transform:uppercase;padding:2px 7px;border-radius:20px;background:#1a222b;color:#9aa4ad}
+.abadge.lic{color:#37d67a;background:#123020}
+.field-link{color:#7cc0ff;text-decoration:none;border-bottom:1px dotted #3a5064}.field-link:hover{color:#a9d5ff}
+.kv{display:flex;gap:18px;flex-wrap:wrap;font-size:12.5px;color:#9aa4ad}.kv b{color:#e7ecf1}
+.admhead{max-width:1300px;margin:0 auto;padding:16px 20px 0;display:flex;align-items:baseline;justify-content:space-between;flex-wrap:wrap;gap:8px}
+.admhead h1{font-size:22px;font-weight:800}.admhead h1 span{color:#c8102e}
+.admhead .fresh{font-size:12px;color:#8b939b}
+.admtabs{max-width:1300px;margin:0 auto;padding:10px 20px 0;display:flex;gap:4px}
+.admtabs button{background:none;border:0;color:#8b939b;font:800 12px/1 system-ui;letter-spacing:.03em;padding:9px 13px;cursor:pointer;border-radius:8px 8px 0 0}
+.admtabs button.on{color:#fff;background:#11161c;border-bottom:2px solid #c8102e}
+.admtabs button .c{background:#1a222b;border-radius:20px;padding:2px 7px;font-size:10px;margin-left:5px}
diff --git a/mockups/admin/index.html b/mockups/admin/index.html
index 7028977d..c173bb12 100644
--- a/mockups/admin/index.html
+++ b/mockups/admin/index.html
@@ -1,176 +1,52 @@
 <!doctype html><html lang="en"><head>
 <meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
-<title>RENTV Admin · Broker & Owner Intelligence</title>
-<link rel="stylesheet" href="../assets/base.css">
-<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
+<title>RENTV Admin — 10 Back-Office Layouts</title>
+<link href="https://fonts.googleapis.com/css2?family=Playfair+Display:wght@700;900&family=Inter:wght@400;600;700;800&display=swap" rel="stylesheet">
 <style>
-  body{background:#0d1014;color:#e7ecf1;font-family:'Inter',system-ui,sans-serif;font-size:14px}
-  .wrap{max-width:1240px;margin:0 auto;padding:18px}
-  .head{display:flex;align-items:baseline;justify-content:space-between;flex-wrap:wrap;gap:8px;margin-bottom:14px}
-  .head h1{font-size:24px;font-weight:800}.head h1 span{color:#c8102e}
-  .head .fresh{font-size:12px;color:#8b939b}
-  .tabs{display:flex;gap:4px;border-bottom:1px solid #232a32;margin-bottom:14px}
-  .tabs button{background:none;border:0;color:#8b939b;font:800 13px/1 'Inter';letter-spacing:.03em;padding:12px 16px;cursor:pointer;border-bottom:3px solid transparent}
-  .tabs button.on{color:#fff;border-color:#c8102e}
-  .tabs button .c{background:#1a222b;border-radius:20px;padding:2px 8px;font-size:11px;margin-left:6px}
-  .toolbar{display:flex;gap:10px;flex-wrap:wrap;align-items:center;margin-bottom:12px}
-  .toolbar input[type=search]{flex:1;min-width:260px;background:#11161c;border:1px solid #232a32;border-radius:9px;color:#e7ecf1;padding:11px 14px;font:inherit}
-  .toolbar select{background:#11161c;border:1px solid #232a32;border-radius:9px;color:#e7ecf1;padding:11px 12px;font:inherit}
-  .toolbar .n{color:#8b939b;font-size:12px}
-  .toolbar .dens{display:flex;align-items:center;gap:8px;color:#8b939b;font-size:12px}
-  .list{display:flex;flex-direction:column;gap:8px}
-  .row{background:#11161c;border:1px solid #232a32;border-radius:11px;overflow:hidden}
-  .row .r{display:grid;grid-template-columns:1fr auto;gap:12px;padding:13px 16px;cursor:pointer;align-items:center}
-  .row .r:hover{background:#141b22}
-  .who{display:flex;flex-direction:column;gap:3px;min-width:0}
-  .who .nm{font-weight:700;font-size:15px}
-  .who .sub{font-size:12.5px;color:#9aa4ad;display:flex;gap:8px;flex-wrap:wrap}
-  .who .sub a{color:#7cc0ff;text-decoration:none;border-bottom:1px dotted #3a5064}
-  .who .sub a:hover{color:#a9d5ff}
-  .badge{font-size:10px;font-weight:800;letter-spacing:.04em;text-transform:uppercase;padding:2px 7px;border-radius:20px;background:#1a222b;color:#9aa4ad}
-  .badge.lic{color:#37d67a;background:#123020}
-  .metric{text-align:right;white-space:nowrap}.metric b{font-size:16px}.metric span{font-size:11px;color:#8b939b;display:block}
-  .exp{border-top:1px solid #232a32;padding:14px 16px;background:#0e141a;display:none}
-  .row.open .exp{display:block}
-  .row.open{border-color:#3a4652}
-  .acts{display:flex;gap:8px;flex-wrap:wrap}
-  .acts a{display:inline-flex;align-items:center;gap:7px;background:#12161c;border:1px solid #2a333d;border-radius:9px;padding:9px 13px;color:#dbe1e7;text-decoration:none;font-weight:700;font-size:13px}
-  .acts a:hover{border-color:#c8102e;color:#fff}
-  .acts a .i{font-size:15px}
-  .kv{display:flex;gap:20px;flex-wrap:wrap;margin-top:12px;font-size:13px;color:#9aa4ad}
-  .kv b{color:#e7ecf1}
-  .compact .row .r{padding:8px 14px}.compact .who .nm{font-size:14px}
-  .empty{color:#6b747d;padding:30px;text-align:center}
+  :root{--red:#c8102e}*{box-sizing:border-box;margin:0}
+  body{font-family:'Inter',system-ui,sans-serif;background:#0b0e12;color:#e7ecf1}
+  header{padding:34px 26px 18px;max-width:1300px;margin:0 auto}
+  h1{font-family:'Playfair Display',serif;font-size:38px;font-weight:900}h1 span{color:var(--red)}
+  .lede{color:#aab2ba;max-width:860px;margin-top:10px;line-height:1.6;font-size:15px}
+  .rule{background:#12161c;border:1px solid #232a32;border-radius:10px;padding:12px 15px;font-size:13px;margin-top:16px;max-width:860px}
+  .rule b{color:#fff}
+  .grid{display:grid;grid-template-columns:repeat(2,1fr);gap:22px;max-width:1300px;margin:22px auto 60px;padding:0 26px}
+  .cardw{background:#11151b;border:1px solid #232a32;border-radius:14px;overflow:hidden;display:flex;flex-direction:column}
+  .cap{display:flex;align-items:baseline;gap:10px;padding:14px 16px 10px}
+  .cap .id{font:800 13px ui-monospace,monospace;color:#fff;background:var(--red);border-radius:6px;padding:3px 9px}
+  .cap h3{font-size:17px;font-weight:800}.cap .style{font-size:12px;color:#8b939b;margin-left:auto}
+  .desc{padding:0 16px 12px;color:#aab2ba;font-size:13px;line-height:1.5}
+  .framew{position:relative;height:440px;border-top:1px solid #232a32;background:#0d1014;overflow:hidden}
+  .framew iframe{width:1600px;height:1000px;border:0;transform:scale(.55);transform-origin:top left}
+  .open{position:absolute;right:12px;bottom:12px;background:#14171a;color:#fff;border:1px solid #333;border-radius:22px;padding:9px 16px;font-weight:800;font-size:13px;text-decoration:none;box-shadow:0 4px 14px rgba(0,0,0,.4)}
+  .open:hover{background:var(--red)}
+  footer{max-width:1300px;margin:0 auto;padding:0 26px 60px;color:#6b747d;font-size:13px}
+  @media(max-width:960px){.grid{grid-template-columns:1fr}}
 </style></head>
 <body>
-<div class="wrap">
-  <div class="head" data-eln>
-    <h1>RENTV Admin · <span>Broker &amp; Owner Intelligence</span></h1>
-    <div class="fresh" id="fresh">Loading…</div>
-  </div>
-  <div class="tabs" data-eln id="tabs">
-    <button data-tab="brokers" class="on">Brokers <span class="c" id="cB">0</span></button>
-    <button data-tab="firms">Firms <span class="c" id="cF">0</span></button>
-    <button data-tab="owners">Owners &amp; Assets <span class="c" id="cO">0</span></button>
-  </div>
-  <div class="toolbar" data-eln>
-    <input type="search" id="q" placeholder="Search name, firm, city, license…">
-    <select id="sort" title="Sort">
-      <option value="name">Name A→Z</option>
-      <option value="city">City</option>
-      <option value="firm">Firm</option>
-      <option value="agents">Agents ↓</option>
-    </select>
-    <label class="dens">Density <input type="range" id="dens" min="0" max="1" step="1" value="0"></label>
-    <span class="n" id="count"></span>
-  </div>
-  <div class="list" data-eln id="list"></div>
-</div>
-<script src="../assets/chrome.js"></script>
+<header>
+  <h1>REN<span>TV</span> Admin · 10 Back-Office Layouts</h1>
+  <p class="lede">Ten genuinely distinct back-office designs over the <b style="color:#fff">same real data</b> — the California DRE registry (380,944 brokers · 34,727 firms) plus the LA marquee assets, synced from the usrealestate back-end. Different structures, not recolors. In <b style="color:#fff">every one</b>, each record is clickable out to <b style="color:#fff">LinkedIn, the firm's website, the map, the firm page, and the CA-DRE license</b> — the CRCP pattern.</p>
+  <div class="rule">🍔 Same global chrome as the front end: nav + live data in the upper-left hamburger; upper-right is sign-in only. Turn on <b>Numbers</b> to pick components across layouts.</div>
+</header>
+<div class="grid" id="grid"></div>
+<footer>Back-office set · pairs with the 10 front-end layouts (../index.html). Data live via the usrealestate broker API on deploy.</footer>
 <script>
-const A={brokers:[],firms:[],owners:[],tab:'brokers'};
-const enc=encodeURIComponent, esc=RENTV.esc;
-const LI_P=(n,f)=>`https://www.linkedin.com/search/results/people/?keywords=${enc([n,f].filter(Boolean).join(' '))}`;
-const LI_C=f=>`https://www.linkedin.com/search/results/companies/?keywords=${enc(f)}`;
-const WEB=f=>`https://www.google.com/search?q=${enc('"'+f+'" real estate official website')}`;
-const MAPS=q=>`https://www.google.com/maps/search/?api=1&query=${enc(q)}`;
-const DRE=lic=>`https://www2.dre.ca.gov/PublicASP/pplinfo.asp?License_id=${enc(lic)}`;
-const GOOG=q=>`https://www.google.com/search?q=${enc(q)}`;
-const ASSR=ain=>`https://portal.assessor.lacounty.gov/parceldetail/${enc(ain)}`;
-
-function actBtn(href,icon,label){return `<a href="${href}" target="_blank" rel="noopener"><span class="i">${icon}</span>${label}</a>`;}
-
-function rowHTML(kind,x,i){
-  if(kind==='brokers'){
-    const loc=[x.city,x.state_code].filter(Boolean).join(', ');
-    return `<div class="row" data-k="brokers" data-i="${i}">
-      <div class="r"><div class="who">
-        <div class="nm">${esc(x.name)}</div>
-        <div class="sub"><span class="badge">${esc(x.license_type||'Licensee')}</span>
-          ${x.firm_name?`<a href="${LI_C(x.firm_name)}" target="_blank" rel="noopener" onclick="event.stopPropagation()">${esc(x.firm_name)}</a>`:''}
-          ${loc?`<a href="${MAPS(x.firm_name+' '+loc)}" target="_blank" rel="noopener" onclick="event.stopPropagation()">${esc(loc)}</a>`:''}
-          ${x.license_no?`<a href="${DRE(x.license_no)}" target="_blank" rel="noopener" onclick="event.stopPropagation()" class="lic">DRE ${esc(x.license_no)}</a>`:''}
-        </div></div>
-        <div class="metric"><b>${esc((x.license_status||'—'))}</b><span>${esc(x.source||'')}</span></div>
-      </div>
-      <div class="exp"><div class="acts">
-        ${actBtn(LI_P(x.name,x.firm_name),'in','Find on LinkedIn')}
-        ${actBtn(GOOG(x.name+' '+(x.firm_name||'')+' '+loc),'🔎','Google this broker')}
-        ${x.firm_name?actBtn(WEB(x.firm_name),'🌐','Firm website'):''}
-        ${x.firm_name?actBtn(LI_C(x.firm_name),'🏢','Firm on LinkedIn'):''}
-        ${loc?actBtn(MAPS((x.firm_name||x.name)+' '+loc),'📍','Address / Map'):''}
-        ${x.license_no?actBtn(DRE(x.license_no),'🪪','CA DRE license'):''}
-      </div><div class="kv"><span>License <b>${esc(x.license_no||'—')}</b></span><span>Type <b>${esc(x.license_type||'—')}</b></span><span>Firm <b>${esc(x.firm_name||'—')}</b></span><span>City <b>${esc(x.city||'—')}</b></span></div></div>
-    </div>`;
-  }
-  if(kind==='firms'){
-    const loc=[x.hq_city,x.hq_state].filter(Boolean).join(', ');
-    return `<div class="row" data-k="firms" data-i="${i}">
-      <div class="r"><div class="who"><div class="nm">${esc(x.name)}</div>
-        <div class="sub"><span class="badge lic">DRE ${esc(x.license_no||'')}</span>
-          ${loc?`<a href="${MAPS(x.name+' '+loc)}" target="_blank" rel="noopener" onclick="event.stopPropagation()">${esc(loc)}</a>`:''}
-          <a href="${WEB(x.name)}" target="_blank" rel="noopener" onclick="event.stopPropagation()">website</a></div></div>
-        <div class="metric"><b>${(x.agent_count||0).toLocaleString()}</b><span>agents</span></div></div>
-      <div class="exp"><div class="acts">
-        ${actBtn(LI_C(x.name),'🏢','Firm on LinkedIn')}
-        ${actBtn(WEB(x.name),'🌐','Official website')}
-        ${actBtn(MAPS(x.name+' '+loc),'📍','HQ / Map')}
-        ${x.license_no?actBtn(DRE(x.license_no),'🪪','CA DRE (corp)'):''}
-        ${actBtn(GOOG(x.name+' brokers '+loc),'🔎','Google the firm')}
-      </div><div class="kv"><span>Agents <b>${(x.agent_count||0).toLocaleString()}</b></span><span>HQ <b>${esc(loc||'—')}</b></span><span>License <b>${esc(x.license_no||'—')}</b></span></div></div>
-    </div>`;
-  }
-  // owners & assets (marquee)
-  const addr=[x.address,x.city].filter(Boolean).join(', ');
-  return `<div class="row" data-k="owners" data-i="${i}">
-    <div class="r"><div class="who"><div class="nm">${esc(x.address||x.ain)}</div>
-      <div class="sub"><span class="badge">${esc(x.type_label||'')}</span>
-        <a href="${MAPS(addr)}" target="_blank" rel="noopener" onclick="event.stopPropagation()">${esc(addr)}</a>
-        <span>${esc(x.use_desc||'')}</span></div></div>
-      <div class="metric"><b>${RENTV.shortUsd(x.assessed_total)}</b><span>assessed</span></div></div>
-    <div class="exp"><div class="acts">
-      ${actBtn(MAPS(addr),'📍','Map address')}
-      ${actBtn(GOOG(addr+' '+(x.use_desc||'')+' owner'),'🔎','Find owner / deal')}
-      ${actBtn('https://www.google.com/maps/@?api=1&map_action=pano&viewpoint='+enc(addr),'📸','Street View')}
-      ${actBtn(ASSR(x.ain),'🏛️','LA County Assessor')}
-    </div><div class="kv"><span>SqFt <b>${(x.sqft||0).toLocaleString()}</b></span><span>Built <b>${x.year_built||'—'}</b></span><span>AIN <b>${esc(x.ain)}</b></span><span>Recorded <b>${RENTV.dstr(x.recording_date)}</b></span></div></div>
-  </div>`;
-}
-
-function currentRows(){
-  const t=A.tab, q=(document.getElementById('q').value||'').toLowerCase();
-  let rows=A[t].slice();
-  if(q) rows=rows.filter(x=>JSON.stringify(x).toLowerCase().includes(q));
-  const s=document.getElementById('sort').value;
-  const key={name:'name',city:t==='firms'?'hq_city':'city',firm:t==='brokers'?'firm_name':'name',agents:'agent_count'}[s];
-  rows.sort((a,b)=> s==='agents' ? (b.agent_count||0)-(a.agent_count||0) : String(a[key]||'').localeCompare(String(b[key]||'')));
-  return rows;
-}
-function render(){
-  const rows=currentRows();
-  document.getElementById('list').innerHTML = rows.length?rows.map((x,i)=>rowHTML(A.tab,x,i)).join(''):'<div class="empty">No matches.</div>';
-  document.getElementById('count').textContent=rows.length+' shown';
-}
-document.getElementById('tabs').addEventListener('click',e=>{const b=e.target.closest('button');if(!b)return;
-  document.querySelectorAll('#tabs button').forEach(x=>x.classList.remove('on'));b.classList.add('on');A.tab=b.dataset.tab;render();});
-document.getElementById('q').addEventListener('input',render);
-document.getElementById('sort').addEventListener('change',render);
-document.getElementById('dens').addEventListener('input',e=>document.getElementById('list').classList.toggle('compact',e.target.value==='1'));
-document.getElementById('list').addEventListener('click',e=>{const r=e.target.closest('.row');if(!r||e.target.closest('a'))return;r.classList.toggle('open');});
-
-RENTV.mount({dark:true, layout:'A01'}); // hamburger + user chrome + numbered components
-// load the broker/firm registry directly (chrome bundles only the news snapshot):
-(async()=>{
-  const [b,f]=await Promise.all([fetch('data/brokers.json').then(r=>r.json()).catch(()=>({rows:[]})),
-                                 fetch('data/firms.json').then(r=>r.json()).catch(()=>({rows:[]}))]);
-  A.brokers=b.rows||[]; A.firms=f.rows||[];
-  // owners from the la-commercial snapshot the chrome already fetched
-  A.owners=(RENTV.data&&RENTV.data.la&&RENTV.data.la.marquee)||[];
-  document.getElementById('cB').textContent=(b.total||A.brokers.length).toLocaleString();
-  document.getElementById('cF').textContent=(f.total||A.firms.length).toLocaleString();
-  document.getElementById('cO').textContent=A.owners.length;
-  document.getElementById('fresh').textContent='CA DRE registry · '+A.brokers.length+' brokers loaded of '+(b.total||0).toLocaleString()+' statewide · synced from the usrealestate back-end';
-  render();
-})();
+const A=[
+  ['A01','DataGrid','spreadsheet · sortable','A dense sortable table with sticky headers and row-expand actions — the analyst’s spreadsheet view across brokers, firms and owners.'],
+  ['A02','Pipeline Board','kanban · grouped','A kanban board where brokers are grouped into columns by license type — scan the whole registry as a pipeline, expand any card for actions.'],
+  ['A03','Master-Detail','list + panel · classic CRM','The classic CRM split: a searchable list on the left drives a full detail panel on the right with the click-outs and key facts.'],
+  ['A04','Rolodex','contact cards · avatars','An image-forward rolodex of broker cards with initials avatars and every action on the face of the card.'],
+  ['A05','Map Desk','LA map · pinned assets','A map-anchored desk — marquee assets are pins; click one to drive a detail rail with Map, Street View and Assessor links.'],
+  ['A06','Command','search-first · keyboard','A command-palette: one big search over 380k brokers, rich result rows, Enter opens the top result’s actions.'],
+  ['A07','Firm Tree','org hierarchy · expandable','A firm org-tree — brokerages are parent nodes; expand to see their brokers, with actions at both the firm and person level.'],
+  ['A08','Activity Feed','timeline · chronological','A vertical activity spine interleaving recorded property transfers with registry records, newest first.'],
+  ['A09','Analytics','KPIs + charts · drill-down','A dashboard: KPI tiles, a top-brokerages bar chart and a license-type mix — click a bar to reach that firm.'],
+  ['A10','Triage Inbox','three-pane · email-style','An email-client triage view: folders, a message list of records, and a preview pane with the full action toolbar.'],
+];
+document.getElementById('grid').innerHTML=A.map(([id,name,style,desc])=>`
+  <div class="cardw"><div class="cap"><span class="id">${id}</span><h3>${name}</h3><span class="style">${style}</span></div>
+  <div class="desc">${desc}</div>
+  <div class="framew"><iframe src="${id}.html" loading="lazy" scrolling="no"></iframe><a class="open" href="${id}.html" target="_blank">Open ${id} full →</a></div></div>`).join('');
 </script>
 </body></html>

← 819d5e04 auto-save: 2026-07-22T14:14:43 (7 files) — data/markets.json  ·  back to Rentv  ·  auto-save: 2026-07-22T14:44:58 (3 files) — data/markets.json edd2ccb2 →