[object Object]

← back to Commercialrealestate

Refine corner nav: thoughtful 6-group IA, all sections collapsed, you-are-here marker + counts

3814ddc00547b8533cea7752a58f47c69d5f5869 · 2026-08-06 15:41:15 -0700 · Steve

- 6 deliberate groups covering all 26 pages (Properties & Listings / Deals & Flow /
  Brokers & Agents / Licensing & Social / Lending & Capital / Data & Admin)
- every section starts COLLAPSED; current-page group gets a gold you-are-here dot +
  gold-ringed count, its link highlighted, so location is clear without auto-expanding
- per-section count badges; drawer subtitles ('the whole site' / 'jump to a section')

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Files touched

Diff

commit 3814ddc00547b8533cea7752a58f47c69d5f5869
Author: Steve <steve@designerwallcoverings.com>
Date:   Thu Aug 6 15:41:15 2026 -0700

    Refine corner nav: thoughtful 6-group IA, all sections collapsed, you-are-here marker + counts
    
    - 6 deliberate groups covering all 26 pages (Properties & Listings / Deals & Flow /
      Brokers & Agents / Licensing & Social / Lending & Capital / Data & Admin)
    - every section starts COLLAPSED; current-page group gets a gold you-are-here dot +
      gold-ringed count, its link highlighted, so location is clear without auto-expanding
    - per-section count badges; drawer subtitles ('the whole site' / 'jump to a section')
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---
 public/corner-nav.js | 173 ++++++++++++++++++++++++---------------------------
 1 file changed, 83 insertions(+), 90 deletions(-)

diff --git a/public/corner-nav.js b/public/corner-nav.js
index 0a43e27..1fb26e6 100644
--- a/public/corner-nav.js
+++ b/public/corner-nav.js
@@ -1,36 +1,38 @@
 /* corner-nav.js — shared two-hamburger navigation for every CRCP page.
    Drop-in: <script src="/corner-nav.js" defer></script>
-   • TOP-LEFT  ☰  → slide-in drawer of SITE navigation, grouped into collapsible sections.
-   • TOP-RIGHT ▤  → slide-in drawer of "On this page" jump links, built from the page's own headings.
+   • TOP-LEFT  ☰  → the whole site, grouped into thoughtful, collapsed accordion sections.
+   • TOP-RIGHT ▤  → "On this page" jump links (from the page's own headings) + the full site menu.
+   All sections start COLLAPSED; the group holding the current page is marked (gold dot) and its
+   link highlighted, so you always know where you are without the menu unfolding on its own.
    Self-contained (injects its own CSS, reads nothing from the host page), idempotent, a11y-friendly. */
 (function () {
   if (window.__cornerNav) return; window.__cornerNav = true;
 
   var CUR = (location.pathname.split('/').pop() || 'index.html').toLowerCase();
 
-  // ── site map: grouped so each group is one collapsible accordion section ──────────
+  // ── site map — every page, grouped so each group is one collapsible section ──────────
   var GROUPS = [
-    { title: 'Explore & Properties', items: [
-      ['index.html', '🏙️ CRE Explorer'], ['just-listed.html', '🆕 Just Listed'],
-      ['sales.html', '💰 Sales'], ['condos.html', '🏢 Condos'],
-      ['rent-rolls.html', '📋 Rent Rolls'], ['mls.html', '🗂️ MLS'], ['map.html', '🗺️ Map'] ] },
-    { title: 'Deals', items: [
-      ['deals.html', '🤝 Deals'], ['deals-flow.html', '🔀 Deal Flow'] ] },
+    { title: 'Properties & Listings', items: [
+      ['index.html', '🏙️ CRE Explorer'], ['mls.html', '📋 Listings'], ['just-listed.html', '🆕 Just Listed'],
+      ['sales.html', '💰 Sales & Comps'], ['condos.html', '🏢 Condos'], ['rent-rolls.html', '📑 Rent Rolls'],
+      ['map.html', '🗺️ Map'] ] },
+    { title: 'Deals & Flow', items: [
+      ['deals.html', '◆ Deal Intelligence'], ['deals-flow.html', '🔀 Deal Flow'] ] },
     { title: 'Brokers & Agents', items: [
-      ['crcp.html', '🎛️ Control Panel'], ['brokers.html', '🎯 Brokers'],
-      ['broker-grid.html', '▦ Broker Grid'], ['licensed.html', '📇 Licensed'],
-      ['licensed-agents.html', '🧾 Licensed Agents'], ['loan-officers.html', '🏦 Loan Officers'],
+      ['crcp.html', '🎛️ Control Panel'], ['broker-grid.html', '▦ Broker Grid'],
+      ['brokers.html', '🕸️ Broker Map'], ['loan-officers.html', '🏦 Loan Officers'] ] },
+    { title: 'Licensing & Social', items: [
+      ['licensed.html', '📇 Licensed Directory'], ['licensed-agents.html', '🧾 Licensed Agents'],
       ['linkedin.html', '💼 LinkedIn'] ] },
     { title: 'Lending & Capital', items: [
-      ['lending.html', '🏛️ Lending'], ['fha-loans.html', '◆ FHA Loans'],
-      ['fha-leads.html', '◆ FHA Leads'], ['lenders.html', '🏦 Lenders'],
-      ['credit-unions.html', '🏦 Credit Unions'], ['sba-lenders.html', '🏦 SBA Lenders'],
-      ['mortgage-market.html', '📈 Mortgage Market'] ] },
-    { title: 'Ops & Admin', items: [
-      ['admin.html', '⚙️ Admin'], ['gov-agents.html', '🏛️ Gov Agents'], ['graphics.html', '🖼️ Graphics'] ] }
+      ['lending.html', '🏛️ Lending'], ['fha-loans.html', '◆ FHA Loans'], ['fha-leads.html', '◆ FHA Leads'],
+      ['lenders.html', '🏦 Lenders'], ['credit-unions.html', '🏦 Credit Unions'],
+      ['sba-lenders.html', '🏦 SBA Lenders'], ['mortgage-market.html', '📈 Mortgage Market'] ] },
+    { title: 'Data & Admin', items: [
+      ['graphics.html', '📊 Charts & Graphics'], ['gov-agents.html', '🏛️ Gov Agents'], ['admin.html', '⚙️ Admin'] ] }
   ];
 
-  // ── styles (scoped under .cnav*, safe over any dark page; falls back if no CSS vars) ─
+  // ── styles (scoped under .cnav*, safe over any dark page) ─────────────────────────────
   var css = '' +
     '.cnav-burger{position:fixed;top:10px;z-index:9998;display:flex;align-items:center;gap:6px;' +
       'background:rgba(20,25,37,.92);color:#e6e8ec;border:1px solid rgba(255,255,255,.14);' +
@@ -41,26 +43,32 @@
     '.cnav-burger .lbl{font-size:12px;letter-spacing:.3px;} @media(max-width:640px){.cnav-burger .lbl{display:none;}}' +
     '.cnav-ov{position:fixed;inset:0;background:rgba(0,0,0,.5);z-index:9998;opacity:0;pointer-events:none;transition:opacity .18s;}' +
     '.cnav-ov.on{opacity:1;pointer-events:auto;}' +
-    '.cnav-drawer{position:fixed;top:0;bottom:0;width:min(320px,86vw);background:#11161f;color:#e6e8ec;z-index:9999;' +
-      'border:0;box-shadow:0 0 40px rgba(0,0,0,.5);display:flex;flex-direction:column;transition:transform .2s ease;' +
+    '.cnav-drawer{position:fixed;top:0;bottom:0;width:min(330px,88vw);background:#11161f;color:#e6e8ec;z-index:9999;' +
+      'box-shadow:0 0 40px rgba(0,0,0,.5);display:flex;flex-direction:column;transition:transform .2s ease;' +
       'font:14px/1.5 -apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif;}' +
     '.cnav-drawer.left{left:0;border-right:1px solid #232a38;transform:translateX(-104%);}' +
     '.cnav-drawer.right{right:0;border-left:1px solid #232a38;transform:translateX(104%);}' +
     '.cnav-drawer.on{transform:translateX(0);}' +
     '.cnav-hd{display:flex;align-items:center;justify-content:space-between;padding:14px 16px;border-bottom:1px solid #232a38;' +
       'font-size:12px;text-transform:uppercase;letter-spacing:.7px;color:#C8A24B;}' +
+    '.cnav-hd .sub{display:block;color:#8A93A2;font-size:10.5px;letter-spacing:.3px;text-transform:none;margin-top:2px;}' +
     '.cnav-hd button{background:none;border:0;color:#8A93A2;font-size:20px;cursor:pointer;line-height:1;}' +
     '.cnav-hd button:hover{color:#e6e8ec;}' +
-    '.cnav-body{overflow-y:auto;padding:8px 0 20px;flex:1;}' +
+    '.cnav-body{overflow-y:auto;padding:6px 0 24px;flex:1;}' +
     '.cnav-sec{border-bottom:1px solid #1b212c;}' +
-    '.cnav-sec>h4{margin:0;padding:11px 16px;font-size:12px;font-weight:700;letter-spacing:.4px;color:#c7cdd6;' +
-      'cursor:pointer;user-select:none;display:flex;align-items:center;justify-content:space-between;}' +
-    '.cnav-sec>h4:hover{color:#fff;}' +
+    '.cnav-sec>h4{margin:0;padding:12px 16px;font-size:12.5px;font-weight:700;letter-spacing:.3px;color:#c7cdd6;' +
+      'cursor:pointer;user-select:none;display:flex;align-items:center;justify-content:space-between;gap:8px;}' +
+    '.cnav-sec>h4:hover{color:#fff;background:rgba(255,255,255,.02);}' +
+    '.cnav-sec>h4 .lt{display:flex;align-items:center;gap:8px;min-width:0;}' +
+    '.cnav-sec>h4 .rt{display:flex;align-items:center;gap:9px;color:#8A93A2;}' +
+    '.cnav-sec>h4 .cnt{font-size:11px;font-variant-numeric:tabular-nums;background:#1b212c;border:1px solid #232a38;border-radius:9px;padding:0 7px;line-height:17px;}' +
     '.cnav-sec>h4 .car{color:#C8A24B;transition:transform .15s;font-size:11px;}' +
     '.cnav-sec.collapsed>h4 .car{transform:rotate(-90deg);}' +
     '.cnav-sec.collapsed .cnav-list{display:none;}' +
+    '.cnav-sec.cur-sec>h4{color:#fff;} .cnav-sec.cur-sec .cnt{border-color:#C8A24B;color:#C8A24B;}' +
+    '.cnav-sec .curdot{width:7px;height:7px;border-radius:50%;background:#C8A24B;box-shadow:0 0 6px rgba(200,162,75,.8);flex:0 0 7px;}' +
     '.cnav-list{padding:2px 0 8px;}' +
-    '.cnav-list a{display:block;padding:7px 16px 7px 22px;color:#c7cdd6;text-decoration:none;font-size:13.5px;border-left:2px solid transparent;}' +
+    '.cnav-list a{display:block;padding:8px 16px 8px 22px;color:#c7cdd6;text-decoration:none;font-size:13.5px;border-left:2px solid transparent;}' +
     '.cnav-list a:hover{background:rgba(200,162,75,.08);color:#fff;}' +
     '.cnav-list a.cur{color:#C8A24B;border-left-color:#C8A24B;background:rgba(200,162,75,.10);font-weight:600;}' +
     '.cnav-empty{padding:14px 16px;color:#8A93A2;font-size:13px;}';
@@ -69,9 +77,7 @@
   function el(tag, cls, html) { var e = document.createElement(tag); if (cls) e.className = cls; if (html != null) e.innerHTML = html; return e; }
 
   // ── overlay ───────────────────────────────────────────────────────────────────────
-  var ov = el('div', 'cnav-ov');
-  document.body.appendChild(ov);
-
+  var ov = el('div', 'cnav-ov'); document.body.appendChild(ov);
   function closeAll() {
     document.querySelectorAll('.cnav-drawer.on').forEach(function (d) { d.classList.remove('on'); });
     ov.classList.remove('on');
@@ -80,77 +86,75 @@
   ov.addEventListener('click', closeAll);
   document.addEventListener('keydown', function (e) { if (e.key === 'Escape') closeAll(); });
 
-  // ── a collapsible accordion section ────────────────────────────────────────────────
-  function section(title, expanded) {
-    var sec = el('div', 'cnav-sec' + (expanded ? '' : ' collapsed'));
-    var h = el('h4', null, '<span>' + title + '</span><span class="car">▾</span>');
+  // ── a collapsible accordion section (always starts COLLAPSED) ────────────────────────
+  function section(title, opts) {
+    opts = opts || {};
+    var sec = el('div', 'cnav-sec collapsed' + (opts.current ? ' cur-sec' : ''));
+    var dot = opts.current ? '<span class="curdot" title="you are here"></span>' : '';
+    var cnt = (opts.count != null) ? '<span class="cnt">' + opts.count + '</span>' : '';
+    var h = el('h4', null, '<span class="lt">' + dot + '<span>' + title + '</span></span>' +
+                           '<span class="rt">' + cnt + '<span class="car">▾</span></span>');
     var list = el('div', 'cnav-list');
     h.addEventListener('click', function () { sec.classList.toggle('collapsed'); });
-    sec.appendChild(h); sec.appendChild(list);
-    sec._list = list;
+    sec.appendChild(h); sec.appendChild(list); sec._list = list;
     return sec;
   }
 
-  // ── build one drawer ────────────────────────────────────────────────────────────────
-  function drawer(side, title) {
+  function drawer(side, title, sub) {
     var d = el('div', 'cnav-drawer ' + side);
-    var hd = el('div', 'cnav-hd', '<span>' + title + '</span>');
+    var hd = el('div', 'cnav-hd', '<span>' + title + (sub ? '<span class="sub">' + sub + '</span>' : '') + '</span>');
     var x = el('button', null, '✕'); x.setAttribute('aria-label', 'Close'); x.addEventListener('click', closeAll);
     hd.appendChild(x);
     var body = el('div', 'cnav-body');
-    d.appendChild(hd); d.appendChild(body);
-    document.body.appendChild(d);
-    d._body = body;
+    d.appendChild(hd); d.appendChild(body); document.body.appendChild(d); d._body = body;
     return d;
   }
 
-  // LEFT — site navigation
-  var left = drawer('left', 'Navigate');
+  function navSection(body, title, opts) {
+    var sec = section(title, opts);
+    (opts && opts.groups ? opts.groups : GROUPS).forEach(function (g) {
+      g.items.forEach(function (it) {
+        var a = el('a', it[0].toLowerCase() === CUR ? 'cur' : '', it[1]);
+        a.href = '/' + it[0]; a.addEventListener('click', closeAll); sec._list.appendChild(a);
+      });
+    });
+    body.appendChild(sec); return sec;
+  }
+
+  // LEFT — full site navigation, one collapsed section per group
+  var left = drawer('left', 'Navigate', 'the whole site');
   GROUPS.forEach(function (g) {
     var hasCur = g.items.some(function (it) { return it[0].toLowerCase() === CUR; });
-    var sec = section(g.title, hasCur); // auto-expand the group holding the current page
+    var sec = section(g.title, { count: g.items.length, current: hasCur });
     g.items.forEach(function (it) {
       var a = el('a', it[0].toLowerCase() === CUR ? 'cur' : '', it[1]);
-      a.href = '/' + it[0];
-      a.addEventListener('click', closeAll);
-      sec._list.appendChild(a);
+      a.href = '/' + it[0]; a.addEventListener('click', closeAll); sec._list.appendChild(a);
     });
     left._body.appendChild(sec);
   });
 
-  // RIGHT — "On this page" jump links, from the page's own headings
-  var right = drawer('right', 'On this page');
+  // RIGHT — "On this page" jump links + the full site menu (all collapsed)
+  var right = drawer('right', 'On this page', 'jump to a section');
   function slug(s) { return s.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/(^-|-$)/g, '').slice(0, 40) || 'sec'; }
   function buildOnThisPage() {
     right._body.innerHTML = '';
-    var sec = section('Sections', true);
-    // section-like headings: page h2s + panel/section h3s, visible + with text, excluding our own chrome
+    var jump = section('Sections on this page', { current: true });
     var nodes = Array.prototype.slice.call(document.querySelectorAll('main h2, section > h2, h2.sec, .panel > h3, section > h3'));
     var seen = {}, count = 0;
     nodes.forEach(function (n) {
       if (n.closest('.cnav-drawer')) return;
-      if (!n.offsetParent && n.offsetHeight === 0) return; // hidden
+      if (!n.offsetParent && n.offsetHeight === 0) return;
       var txt = (n.textContent || '').replace(/\s+/g, ' ').trim();
-      txt = txt.replace(/^[▸▾▿▶◀⌄˅▼>\s]+/, ''); // strip leading collapse-caret glyphs
+      txt = txt.replace(/^[▸▾▿▶◀⌄˅▼>\s]+/, '');
       if (!txt) return; txt = txt.slice(0, 46);
-      if (!n.id) { var s = slug(txt); var u = s, i = 2; while (document.getElementById(u)) { u = s + '-' + (i++); } n.id = u; }
+      if (!n.id) { var s = slug(txt), u = s, i = 2; while (document.getElementById(u)) { u = s + '-' + (i++); } n.id = u; }
       if (seen[n.id]) return; seen[n.id] = 1;
-      var a = el('a', '', txt);
-      a.href = '#' + n.id;
-      a.addEventListener('click', function () { closeAll(); });
-      sec._list.appendChild(a); count++;
+      var a = el('a', '', txt); a.href = '#' + n.id; a.addEventListener('click', closeAll);
+      jump._list.appendChild(a); count++;
     });
-    if (count) { right._body.appendChild(sec); }
-    else { right._body.appendChild(el('div', 'cnav-empty', 'No jump-to sections on this page. Use the ☰ menu (top-left) to navigate the site.')); }
-    // always offer the full site menu as a second collapsed section on the right too
-    var nav = section('Go to another page', false);
-    GROUPS.forEach(function (g) {
-      g.items.forEach(function (it) {
-        var a = el('a', it[0].toLowerCase() === CUR ? 'cur' : '', it[1]);
-        a.href = '/' + it[0]; a.addEventListener('click', closeAll); nav._list.appendChild(a);
-      });
-    });
-    right._body.appendChild(nav);
+    if (count) { jump.querySelector('.rt').insertAdjacentHTML('afterbegin', '<span class="cnt">' + count + '</span>'); right._body.appendChild(jump); }
+    else { right._body.appendChild(el('div', 'cnav-empty', 'No jump-to sections on this page. Use “Go to another page” below or the ☰ menu (top-left).')); }
+    navSection(right._body, 'Go to another page', { count: GROUPS.reduce(function (n, g) { return n + g.items.length; }, 0) });
   }
 
   // ── the two hamburgers ──────────────────────────────────────────────────────────────
@@ -160,39 +164,29 @@
     var target = side === 'left' ? left : right;
     b.addEventListener('click', function (e) {
       e.stopPropagation();
-      var open = target.classList.contains('on');
-      closeAll();
+      var open = target.classList.contains('on'); closeAll();
       if (!open) { if (onOpen) onOpen(); target.classList.add('on'); ov.classList.add('on'); b.setAttribute('aria-expanded', 'true'); }
     });
-    document.body.appendChild(b);
-    return b;
+    document.body.appendChild(b); return b;
   }
   burger('left', '☰', 'Menu', null);
   burger('right', '▤', 'Sections', buildOnThisPage);
 
-  // Reserve corner space on the page's top bar so its title / remaining controls
-  // don't hide under the two hamburgers (they sit as fixed chips over the corners).
+  // Reserve corner space on the top bar so its title / remaining controls clear the burgers.
   function reserve() {
-    var bar = document.querySelector('header');
-    if (!bar) return;
+    var bar = document.querySelector('header'); if (!bar) return;
     var cs = getComputedStyle(bar);
-    var pl = parseInt(cs.paddingLeft, 10) || 0, pr = parseInt(cs.paddingRight, 10) || 0;
-    bar.style.paddingLeft = Math.max(pl, 104) + 'px';
-    bar.style.paddingRight = Math.max(pr, 120) + 'px';
+    bar.style.paddingLeft = Math.max(parseInt(cs.paddingLeft, 10) || 0, 104) + 'px';
+    bar.style.paddingRight = Math.max(parseInt(cs.paddingRight, 10) || 0, 120) + 'px';
   }
-
-  // Fold the old horizontal top-tabs into the hamburgers: hide the inter-page nav
-  // LINKS in the header (every destination already lives in the left ☰ menu), but
-  // LEAVE page controls (sliders, buttons, selects, the down-payment toggle) intact.
+  // Fold old horizontal top-tabs into the hamburgers: hide inter-page nav LINKS in the header
+  // (every destination lives in the ☰ menu) but LEAVE page controls (sliders, buttons, selects).
   function hideTopTabs() {
-    var links = document.querySelectorAll('header a[href], .topnav a[href], nav.nav a[href], .nav a[href]');
-    links.forEach(function (a) {
-      if (a.closest('.cnav-drawer')) return;                 // never our own menu
+    document.querySelectorAll('header a[href], .topnav a[href], nav.nav a[href], .nav a[href]').forEach(function (a) {
+      if (a.closest('.cnav-drawer')) return;
       var href = a.getAttribute('href') || '';
       if (/\.html([?#].*)?$/i.test(href) || href === '/' || href.charAt(0) === '#') a.style.display = 'none';
     });
-    // Collapse now-empty nav wrappers so they don't leave a gap (only if they hold no
-    // visible controls left behind).
     ['.topnav', 'nav.nav', 'header .nav'].forEach(function (sel) {
       document.querySelectorAll(sel).forEach(function (w) {
         var hasVisibleControl = Array.prototype.some.call(w.children, function (c) {
@@ -203,7 +197,6 @@
       });
     });
   }
-
   function apply() { reserve(); hideTopTabs(); }
   if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', apply); else apply();
 })();

← 82c7270 Fold top tab-bars into the hamburgers; strip horizontal nav  ·  back to Commercialrealestate  ·  Polish corner nav: type-to-filter search, focus-on-open, cur bd5c023 →