[object Object]

← back to Rentv

social console F1: Role×Market×Sector facet builder — composes LinkedIn search, honest registry✓ vs lens tag, pin segment, load real names from CA-metro registry

bd17052e0f24fb3bb6f87d792cabcab7ffdfd72b · 2026-07-30 20:54:56 -0700 · Steve

Files touched

Diff

commit bd17052e0f24fb3bb6f87d792cabcab7ffdfd72b
Author: Steve <steve@designerwallcoverings.com>
Date:   Thu Jul 30 20:54:56 2026 -0700

    social console F1: Role×Market×Sector facet builder — composes LinkedIn search, honest registry✓ vs lens tag, pin segment, load real names from CA-metro registry
---
 public/social.html | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 85 insertions(+)

diff --git a/public/social.html b/public/social.html
index b0e78540..610b392c 100644
--- a/public/social.html
+++ b/public/social.html
@@ -102,6 +102,21 @@
   .gp-a:hover { background:#eef5fc; }
   .gperson .gstatus { margin-top:0; }
   .gperson .gp-let { margin:0; }
+  .gfacet { border:1px solid var(--line); border-radius:10px; padding:10px 11px; margin-bottom:12px; background:#fbfcfe; }
+  .gfacet h4 { margin:0 0 6px; font-size:10.5px; text-transform:uppercase; letter-spacing:.08em; color:var(--muted); font-weight:700; }
+  .gfacet .fgl { font-size:9.5px; color:#9aa7b4; text-transform:uppercase; letter-spacing:.06em; margin:2px 0 3px; }
+  .gfacet .frow { display:flex; flex-wrap:wrap; gap:5px; margin-bottom:7px; }
+  .gchip { border:1px solid var(--line); background:#fff; color:var(--ink); border-radius:99px; padding:3px 9px; font-size:11px; cursor:pointer; user-select:none; }
+  .gchip.on { background:var(--li); border-color:var(--li); color:#fff; }
+  .gchip.reg::after { content:' •'; color:#1a7f37; }
+  .gchip.on.reg::after { color:#bfe9c9; }
+  .gfacet .fout { font-size:11px; color:var(--muted); margin:4px 0 8px; display:flex; align-items:center; gap:6px; flex-wrap:wrap; }
+  .gfacet .ftag { font-size:9.5px; font-weight:700; text-transform:uppercase; letter-spacing:.04em; padding:2px 7px; border-radius:99px; white-space:nowrap; }
+  .gfacet .ftag.reg { background:#e8f7ec; color:#1a7f37; }
+  .gfacet .ftag.lens { background:#eef2f7; color:#5b6b7b; }
+  .gfacet .fbtns { display:flex; flex-wrap:wrap; gap:6px; }
+  .gfacet .fbtns button { padding:6px 9px; font-size:11.5px; }
+  .gfacet .fbtns .ghost { background:#eef2f7; color:var(--ink); }
   @media (max-width:900px){ .grail.open { position:fixed; top:0; bottom:0; height:100vh; width:86vw; max-width:340px; } .grail.open .grail-body { max-height:100vh; } }
 </style>
 </head>
@@ -116,6 +131,18 @@
   <div class="grail-body">
     <div class="grail-top"><strong>Growth · LinkedIn Targets</strong><button class="grail-close" id="grailClose" title="Collapse">×</button></div>
     <p class="grail-note">CRE conference attendees to grow on LinkedIn — owners &amp; investors, developers, brokers, lenders &amp; capital sources, vendors, legal, construction, title &amp; escrow.</p>
+    <div class="gfacet" id="gfacet">
+      <h4>Build a segment</h4>
+      <div class="fgl">Role</div><div class="frow" id="fRoles"></div>
+      <div class="fgl">Market · <span style="color:#1a7f37">•</span> = registry-backed (real names)</div><div class="frow" id="fMarkets"></div>
+      <div class="fgl">Sector</div><div class="frow" id="fSectors"></div>
+      <div class="fout"><span id="fTag" class="ftag lens">LinkedIn search</span><span id="fOut">Pick role · market · sector…</span></div>
+      <div class="fbtns">
+        <button id="fLi">🔗 LinkedIn ↗</button>
+        <button class="ghost" id="fPin">📌 Pin segment</button>
+        <button class="ghost" id="fReg" style="display:none">⤵ Load registry</button>
+      </div>
+    </div>
     <div class="grail-actions">
       <button id="grailAddAll">+ Add all categories</button>
       <button class="ghost" id="grailPull">Pull registry contacts</button>
@@ -459,6 +486,64 @@
     fillCompose(letterFor({kind:'contact',cat:firm.cat,name:p.name,market:firm.market},p.lv), cap(p.name)); GSEL=key; grender();
   }
 
+  // ── F1: facet/segment builder — Role × Market × Sector → LinkedIn search + (registry) real names ──
+  var GROLES_F=['Owners / Investors','Developers','Brokers','Lenders & Capital Sources','Vendors / Service Providers','Legal','Construction / GCs','Title & Escrow'];
+  var GF_REGROLE={ 'Brokers':'brokers', 'Owners / Investors':'firms' };  // only these are in the CA DRE registry
+  var GMKTS=[ {k:'Los Angeles',reg:1},{k:'Orange County',reg:1},{k:'Inland Empire',reg:1},{k:'San Diego',reg:1},{k:'Bay Area',reg:1},{k:'Ventura',reg:0},{k:'Sacramento',reg:0},{k:'Arizona (Scottsdale)',reg:0} ];
+  var GSECS=['Office','Industrial','Multifamily','Retail','Finance'];
+  var ROLE_KW={ 'Owners / Investors':'owner OR investor','Developers':'developer','Brokers':'broker','Lenders & Capital Sources':'lender OR "capital markets"','Vendors / Service Providers':'services','Legal':'attorney','Construction / GCs':'general contractor','Title & Escrow':'title escrow officer' };
+  var GF={ roles:{}, markets:{}, sectors:{} };
+  function fchip(txt,on,reg,group){ return '<span class="gchip'+(on?' on':'')+(reg?' reg':'')+'" data-fg="'+group+'" data-fk="'+esc(txt)+'">'+esc(txt)+'</span>'; }
+  function selArr(o){ return Object.keys(o).filter(function(k){ return o[k]; }); }
+  function renderFacets(){
+    document.getElementById('fRoles').innerHTML=GROLES_F.map(function(r){ return fchip(r,GF.roles[r],!!GF_REGROLE[r],'roles'); }).join('');
+    document.getElementById('fMarkets').innerHTML=GMKTS.map(function(m){ return fchip(m.k,GF.markets[m.k],m.reg,'markets'); }).join('');
+    document.getElementById('fSectors').innerHTML=GSECS.map(function(s){ return fchip(s,GF.sectors[s],false,'sectors'); }).join('');
+    composeSeg();
+  }
+  function composeSeg(){
+    var roles=selArr(GF.roles), mkts=selArr(GF.markets), secs=selArr(GF.sectors);
+    var roleKw=roles.map(function(r){ return ROLE_KW[r]||r; }).join(' OR ');
+    var terms=[secs.join(' '),'commercial real estate',roleKw,mkts.map(function(m){ return m.replace(/\s*\(.*\)/,''); }).join(' ')].filter(Boolean).join(' ').replace(/\s+/g,' ').trim();
+    var regMkt=mkts.filter(function(m){ var g=GMKTS.filter(function(z){return z.k===m;})[0]; return g&&g.reg; })[0];
+    var regRole=roles.filter(function(r){ return GF_REGROLE[r]; })[0];
+    var registry=!!(regMkt&&regRole);
+    var tag=document.getElementById('fTag'); tag.className='ftag '+(registry?'reg':'lens'); tag.textContent=registry?'registry ✓ real names':'LinkedIn search';
+    document.getElementById('fOut').textContent=(roles.length||mkts.length||secs.length)?terms:'Pick role · market · sector…';
+    var rb=document.getElementById('fReg'); rb.style.display=registry?'':'none';
+    if(registry) rb.textContent='⤵ Load '+regMkt+' '+regRole.split(' ')[0].toLowerCase();
+    GF._terms=terms; GF._regMkt=regMkt; GF._regRole=regRole; GF._registry=registry; GF._roles=roles; GF._mkts=mkts; GF._secs=secs;
+  }
+  document.getElementById('gfacet').addEventListener('click',function(e){
+    var c=e.target.closest('.gchip'); if(!c)return;
+    var g=c.getAttribute('data-fg'), k=c.getAttribute('data-fk');
+    GF[g][k]=!GF[g][k]; renderFacets();
+  });
+  document.getElementById('fLi').onclick=function(){ window.open(liUrl(GF._terms||'commercial real estate'),'_blank','noopener'); };
+  document.getElementById('fPin').onclick=function(){
+    if(!(GF._roles.length||GF._mkts.length||GF._secs.length)){ gmsg('Pick a role/market/sector first.'); return; }
+    var role=GF._roles[0]||'Other';
+    var label=[GF._secs.join('/'),role.split(' ')[0],GF._mkts.join('/')].filter(Boolean).join(' · ')||'Segment';
+    var l=gload(); l.push({id:gid(),kind:'segment',cat:role,name:label,sub:(GF._registry?'Registry-backed segment':'LinkedIn-search segment'),terms:GF._terms,market:GF._mkts[0],sector:GF._secs[0],status:0,added:Date.now()});
+    gsave(l); grender(); gmsg('Segment pinned ✓');
+  };
+  document.getElementById('fReg').onclick=function(){
+    if(!GF._registry)return; var type=GF_REGROLE[GF._regRole], metro=GF._regMkt;
+    gmsg('Loading '+metro+'…');
+    fetch('/api/summit/leads?type='+type+'&metro='+encodeURIComponent(metro)+'&limit=100',{credentials:'same-origin'})
+      .then(function(r){ return r.json(); }).then(function(d){
+        var l=gload(), seen={}; l.forEach(function(x){ if(x.rid) seen[x.rid]=1; }); var now=Date.now(), added=0;
+        (d.rows||[]).forEach(function(r){
+          if(type==='firms'){ var rid='f'+r.id; if(seen[rid])return; seen[rid]=1;
+            l.push({id:gid(),rid:rid,kind:'contact',cat:'Owners / Investors',name:cap(r.name),sub:(r.hq_city?cap(r.hq_city):metro)+(r.agent_count?(' · '+r.agent_count+' agents'):''),terms:r.name+' commercial real estate',market:metro,status:0,added:now}); added++; }
+          else { var rb='b'+r.id; if(seen[rb])return; seen[rb]=1;
+            l.push({id:gid(),rid:rb,kind:'contact',cat:'Brokers',name:cap(r.name),sub:cap(r.firm_name||'')+(r.city?(' · '+cap(r.city)):''),terms:r.name+' '+(r.firm_name||'')+' commercial real estate',market:metro,status:0,added:now}); added++; }
+        });
+        gsave(l); grender(); gmsg(added ? ('Loaded '+added+' from '+metro+' ✓') : 'No new records.');
+      }).catch(function(){ gmsg('Registry unavailable.'); });
+  };
+  renderFacets();
+
   gsetOpen(localStorage.getItem(GOPEN)==='1'); // collapsed by default
   grender();
 </script>

← 3db0aeaf social console: /api/firm/:id proxy + firm→people drilldown,  ·  back to Rentv  ·  yoloforever: cycle 4 ledger — SHIP-AND-PAUSE; public-channel 4a1cb2d0 →