[object Object]

← back to Commercialrealestate

TK-10330 CRCP flagship layout redesign — industry-grade (Four Horsemen/graphic-agent panel)

86a0499b6cfa33a2cd0f4771f1969b1a57660382 · 2026-08-08 07:51:10 -0700 · Steve Abrams

Additive override layer on crcp.html (preserves every JS-bound ID; reversible):
- honest cold-load: skeleton KPI tiles (never-blank strip) + amber reconnecting
  pulse + 'can't reach server' banner (was: green 'live' pulse over empty panels)
- fresh session lands useful: opens the primary deal-sourcing panels
  (Call Segments -> Top Brokers -> Top Firms -> FHA Expiring) instead of a wall
  of collapsed drawers; returning users keep saved layout
- terminal chrome: panel radius 13->5px + gold category left-rail, muted section
  headers w/ rule (was shouting gold all-caps), color-railed KPI tiles 30->26px
- tables: header rail + zebra + row hover + discoverable sort (up-down) hint
- compact single-line header, nav muted w/ gold active state
Verified in Day + Night, 0 console errors. Deploy to Kamatera stays Steve-gated.

Files touched

Diff

commit 86a0499b6cfa33a2cd0f4771f1969b1a57660382
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Sat Aug 8 07:51:10 2026 -0700

    TK-10330 CRCP flagship layout redesign — industry-grade (Four Horsemen/graphic-agent panel)
    
    Additive override layer on crcp.html (preserves every JS-bound ID; reversible):
    - honest cold-load: skeleton KPI tiles (never-blank strip) + amber reconnecting
      pulse + 'can't reach server' banner (was: green 'live' pulse over empty panels)
    - fresh session lands useful: opens the primary deal-sourcing panels
      (Call Segments -> Top Brokers -> Top Firms -> FHA Expiring) instead of a wall
      of collapsed drawers; returning users keep saved layout
    - terminal chrome: panel radius 13->5px + gold category left-rail, muted section
      headers w/ rule (was shouting gold all-caps), color-railed KPI tiles 30->26px
    - tables: header rail + zebra + row hover + discoverable sort (up-down) hint
    - compact single-line header, nav muted w/ gold active state
    Verified in Day + Night, 0 console errors. Deploy to Kamatera stays Steve-gated.
---
 public/crcp.html | 107 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 106 insertions(+), 1 deletion(-)

diff --git a/public/crcp.html b/public/crcp.html
index 022161f..de31976 100644
--- a/public/crcp.html
+++ b/public/crcp.html
@@ -143,6 +143,67 @@
   html[data-theme="dark"]  { --panel:var(--card); --panel2:var(--bg2); --fg:var(--ink); }
   html[data-theme="light"] { --panel:var(--card); --panel2:var(--bg2); --fg:var(--ink); }
 </style>
+<style>
+  /* ── TK-10330: industry-grade layout redesign (override layer) ──────────────
+     Loads LAST, so these rules win by cascade order. Additive + fully reversible;
+     preserves every JS-bound ID/DOM hook. Builds on the shared day/night tokens.
+     Design consensus: graphic-designer + ui-ux-designer panels — move from
+     "consumer card app" toward a CoStar/Crexi/Bloomberg-tier deal terminal. */
+
+  /* — Header: compact single-line toolbar, differentiated zones — */
+  header{padding:0 22px;min-height:46px;gap:12px;border-bottom:2px solid var(--line);background:var(--headbg,var(--bg));backdrop-filter:saturate(1.1) blur(6px);}
+  header h1{font-size:15px;font-weight:700;letter-spacing:.2px;white-space:nowrap;}
+  header h1 b{color:var(--gold);}
+  header h1::after{content:'';display:inline-block;width:1px;height:15px;background:var(--line);margin:0 4px 0 12px;vertical-align:-2px;}
+  header .nav a{color:var(--muted);font-size:12.5px;font-weight:500;}
+  header .nav a:hover{color:var(--ink,var(--fg));}
+  header .nav a.active{color:var(--gold);font-weight:600;}
+  .live{font-size:11.5px;}
+  .expandall{border-radius:5px;padding:5px 10px;font-size:11.5px;}
+
+  /* — Panels: terminal radius + gold category rail + muted section headers — */
+  .panel{border-radius:5px;border-left:2px solid var(--gold);margin-bottom:12px;padding:14px 16px;}
+  .panel h3{font-size:11px;font-weight:600;letter-spacing:.5px;color:var(--muted);
+    border-bottom:1px solid var(--line);padding-bottom:9px;margin-bottom:11px;align-items:center;}
+  .panel h3 .caret{color:var(--gold);}
+  .panel h3 .c{font-size:10.5px;}
+
+  /* — KPI tiles: color-coded top rail, tighter terminal density — */
+  .tiles{gap:11px;margin-bottom:16px;}
+  .tile{border-radius:6px;padding:12px 14px;border-top:2px solid var(--line);}
+  .tile.gold{border-top-color:var(--gold);}
+  .tile.active{border-top-color:var(--active);}
+  .tile.draft{border-top-color:var(--draft);}
+  .tile.staged{border-top-color:var(--staged);}
+  .tile .k{font-size:10px;letter-spacing:.7px;font-weight:600;}
+  .tile .v{font-size:26px;line-height:1.08;margin-top:2px;}
+  .tile .sub{font-size:10px;margin-top:1px;}
+  /* persistent (not hover-only) drill affordance so it's discoverable */
+  .tile.clickable::after{opacity:.3;}
+  .tile.clickable:hover::after{opacity:1;}
+
+  /* — Tables: header rail, zebra, hover, discoverable sort affordance — */
+  td,th{padding:7px 8px;}
+  th{font-size:10px;letter-spacing:.5px;}
+  #segTbl thead th,#brTbl thead th,#fmTbl thead th,#xpTbl thead th{
+    background:var(--bg2,transparent);border-bottom:1px solid var(--line);}
+  /* sort affordance is already owned by the page's native th.srt.asc/.desc glyphs
+     + col-resize.js's .cr-sort span — do NOT add an idle ::after here or it double-renders */
+  #segTbl tbody tr:nth-child(even) td,#brTbl tbody tr:nth-child(even) td,
+  #fmTbl tbody tr:nth-child(even) td{background:var(--stripe,transparent);}
+  #segTbl tbody tr:hover td,#brTbl tbody tr:hover td,
+  #fmTbl tbody tr:hover td,#xpTbl tbody tr:hover td{background:var(--hover,rgba(121,184,255,.10));}
+
+  /* — Loading / connection state: skeleton shimmer + honest pulse — */
+  @keyframes crcpShimmer{0%{opacity:.45}50%{opacity:.9}100%{opacity:.45}}
+  .tile.skel .v{color:transparent;background:var(--line);border-radius:4px;height:24px;width:62%;margin-top:5px;animation:crcpShimmer 1.3s ease-in-out infinite;}
+  .tile.skel .sub{color:transparent;background:var(--line);border-radius:3px;height:8px;width:75%;margin-top:6px;opacity:.5;}
+  .pulse.reconnecting{background:var(--gold);animation:pWarn 1.6s infinite;}
+  @keyframes pWarn{0%{box-shadow:0 0 0 0 rgba(200,162,75,.55)}70%{box-shadow:0 0 0 9px rgba(200,162,75,0)}100%{box-shadow:0 0 0 0 rgba(200,162,75,0)}}
+  #crcpConnBanner{display:none;margin:0 0 14px;background:rgba(231,76,60,.10);border:1px solid var(--del);
+    border-radius:6px;padding:9px 13px;font-size:12.5px;color:var(--del);}
+  #crcpConnBanner.show{display:block;}
+</style>
 </head>
 <body>
 <header>
@@ -581,13 +642,22 @@ async function poll(){
   dens.addEventListener('input',()=>{ document.documentElement.style.setProperty('--tilemin', dens.value+'px'); localStorage.setItem(LS_DENS, dens.value); });
   // which panels were left open last time (default: ALL collapsed)
   let open; try{ open=new Set(JSON.parse(localStorage.getItem(LS_OPEN)||'[]')); }catch(e){ open=new Set(); }
+  const fresh = localStorage.getItem(LS_OPEN)===null;          // no saved layout yet (TK-10330)
   const save=()=>localStorage.setItem(LS_OPEN, JSON.stringify([...open]));
   const panels=[...document.querySelectorAll('.panel')];
   panels.forEach((p,i)=>{
     const h=p.querySelector('h3'); if(!h) return;
     const key=h.textContent.trim().slice(0,40)||('p'+i); p.dataset.pk=key;
     if(!h.querySelector('.caret')){ const c=document.createElement('span'); c.className='caret'; c.textContent='▸'; h.prepend(c); }
-    if(!open.has(key)) p.classList.add('collapsed');           // start collapsed until activated
+    // TK-10330: land useful, not a wall of closed drawers. On a FRESH session open the
+    // primary deal-sourcing workflow panels (segments -> call list -> firms -> urgency);
+    // returning users keep whatever layout they saved.
+    if(fresh){
+      const KT=key.toLowerCase();
+      const seedOpen = KT.includes('call segments')||KT.includes('top brokers')||KT.includes('top firms')||KT.includes('fha approval expiring');
+      if(seedOpen) open.add(key); else p.classList.add('collapsed');
+    }
+    else if(!open.has(key)) p.classList.add('collapsed');      // start collapsed until activated
     h.addEventListener('click',()=>{
       p.classList.toggle('collapsed');
       if(p.classList.contains('collapsed')) open.delete(key); else open.add(key);
@@ -827,5 +897,40 @@ loadSegments(); setInterval(loadSegments, 20000);
   document.addEventListener('DOMContentLoaded',wireToggle);
 })();
 </script>
+<script>
+// ── TK-10330: honest loading / connection state ────────────────────────────
+// The cold-load used to read as "broken": empty KPI strip + empty panels while a
+// green "live" pulse sat next to "reconnecting…". This adds (a) skeleton KPI tiles
+// so the strip is never blank, (b) a real error banner, and (c) an honest amber
+// pulse while reconnecting. Purely additive — poll() still owns the real render.
+(function(){
+  function seedSkeletonTiles(){
+    var t=document.getElementById('tiles');
+    if(!t || t.children.length) return;               // poll() already filled it
+    var labels=['Listings','Brokers','Firms','Condos','Res. agents','FHA-approved','Broker edges'];
+    var cls=['gold','active','draft','staged','draft','active','gold'];
+    t.innerHTML=labels.map(function(k,i){
+      return '<div class="tile skel '+cls[i]+'"><div class="k">'+k+'</div><div class="v">—</div><div class="sub"> </div></div>';
+    }).join('');
+  }
+  function ensureBanner(){
+    var w=document.querySelector('.wrap'); if(!w) return null;
+    var b=document.getElementById('crcpConnBanner');
+    if(!b){ b=document.createElement('div'); b.id='crcpConnBanner';
+      b.textContent='⚠ Can’t reach the server — showing the last known data. Retrying…';
+      w.insertBefore(b, w.firstChild); }
+    return b;
+  }
+  function tick(){
+    var lt=document.getElementById('livetxt'); var pulse=document.querySelector('.pulse'); var b=ensureBanner();
+    var reconnecting = lt && /reconnect/i.test(lt.textContent||'');
+    if(pulse) pulse.classList.toggle('reconnecting', !!reconnecting);
+    if(b) b.classList.toggle('show', !!reconnecting);
+    // once real tiles arrive, the skeleton class is naturally gone (poll replaces innerHTML)
+  }
+  function start(){ seedSkeletonTiles(); tick(); setInterval(tick, 1500); }
+  if(document.readyState==='loading') document.addEventListener('DOMContentLoaded',start); else start();
+})();
+</script>
 </body>
 </html>

← af6cf04 TK-10358 yoloforever c2 Cody-gate fix: repair IREA row unquo  ·  back to Commercialrealestate  ·  auto-data-snapshot: 2026-08-08T07:55:02 (9 data files) — .pl 6c1cc89 →