[object Object]

← back to Rentv 2026

RENTV 2026: element-numbering overlay (all templates) + demo sold-sponsorship creatives

089da29ba092735d9f38c30d358429ee30f8097a · 2026-08-07 11:30:01 -0700 · Steve (RENTV 2026)

- public/2026/elements.js: shared drop-in that numbers every significant element
  (stable DOM-order N + badge + searchable index panel), toggled by button / 'e' key / ?num=1.
  Injected into all 5 templates + selector + advertise + ad-admin so any element is
  referenceable as 'Template C · el N' in future feedback.
- public/ad-creatives/{jll-leaderboard,cbre-masthead,mm-rectangle}.svg: demo banner
  creatives so the sold-slot creative-serving state is visible on the live templates
  (data/ad-state.json seeds 3 sold banners + 3 sold video sponsorships; runtime, gitignored).

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

Files touched

Diff

commit 089da29ba092735d9f38c30d358429ee30f8097a
Author: Steve (RENTV 2026) <steve@designerwallcoverings.com>
Date:   Fri Aug 7 11:30:01 2026 -0700

    RENTV 2026: element-numbering overlay (all templates) + demo sold-sponsorship creatives
    
    - public/2026/elements.js: shared drop-in that numbers every significant element
      (stable DOM-order N + badge + searchable index panel), toggled by button / 'e' key / ?num=1.
      Injected into all 5 templates + selector + advertise + ad-admin so any element is
      referenceable as 'Template C · el N' in future feedback.
    - public/ad-creatives/{jll-leaderboard,cbre-masthead,mm-rectangle}.svg: demo banner
      creatives so the sold-slot creative-serving state is visible on the live templates
      (data/ad-state.json seeds 3 sold banners + 3 sold video sponsorships; runtime, gitignored).
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---
 public/2026/a.html                      |   1 +
 public/2026/b.html                      |   1 +
 public/2026/c.html                      |   1 +
 public/2026/d.html                      |   1 +
 public/2026/e.html                      |   1 +
 public/2026/elements.js                 | 164 ++++++++++++++++++++++++++++++++
 public/2026/index.html                  |   1 +
 public/ad-admin.html                    |   1 +
 public/ad-creatives/cbre-masthead.svg   |  10 ++
 public/ad-creatives/jll-leaderboard.svg |   9 ++
 public/ad-creatives/mm-rectangle.svg    |  10 ++
 public/advertise.html                   |   1 +
 12 files changed, 201 insertions(+)

diff --git a/public/2026/a.html b/public/2026/a.html
index 952f4f0c..2e02b257 100644
--- a/public/2026/a.html
+++ b/public/2026/a.html
@@ -1346,5 +1346,6 @@ img { display: block; }
 
 })();
 </script>
+<script src="/2026/elements.js" defer></script>
 </body>
 </html>
diff --git a/public/2026/b.html b/public/2026/b.html
index 1838974f..91198194 100644
--- a/public/2026/b.html
+++ b/public/2026/b.html
@@ -1467,5 +1467,6 @@ button { cursor: pointer; border: none; background: none; font: inherit; }
 
 })();
 </script>
+<script src="/2026/elements.js" defer></script>
 </body>
 </html>
diff --git a/public/2026/c.html b/public/2026/c.html
index 17195255..efe26532 100644
--- a/public/2026/c.html
+++ b/public/2026/c.html
@@ -1090,5 +1090,6 @@ init();
 
 })();
 </script>
+<script src="/2026/elements.js" defer></script>
 </body>
 </html>
diff --git a/public/2026/d.html b/public/2026/d.html
index a63c35e0..d109ba2f 100644
--- a/public/2026/d.html
+++ b/public/2026/d.html
@@ -1709,5 +1709,6 @@ loadVideos();
 loadOriginals();
 </script>
 
+<script src="/2026/elements.js" defer></script>
 </body>
 </html>
diff --git a/public/2026/e.html b/public/2026/e.html
index 23efaf81..4c0cca3d 100644
--- a/public/2026/e.html
+++ b/public/2026/e.html
@@ -1406,5 +1406,6 @@ footer {
 
 })();
 </script>
+<script src="/2026/elements.js" defer></script>
 </body>
 </html>
diff --git a/public/2026/elements.js b/public/2026/elements.js
new file mode 100644
index 00000000..8360d633
--- /dev/null
+++ b/public/2026/elements.js
@@ -0,0 +1,164 @@
+/* RENTV 2026 — Element Numbering overlay (shared drop-in).
+ *
+ * Purpose: give every significant element on every template a STABLE number so
+ * Steve can reference "Template C, element 14" in future feedback.
+ *
+ * Include on any page:  <script src="/2026/elements.js" defer></script>
+ * Toggle:  the "①② Elements" button (bottom-left) · the "e" key · or ?num=1 in the URL.
+ *
+ * Numbers are assigned in DOM order over a curated selector set, so they are
+ * stable across reloads (as long as the page structure doesn't change). Each
+ * numbered element gets a badge; a searchable index panel lists them all and
+ * clicking an index row scrolls to + flashes that element. State persists in
+ * localStorage per page path. Zero dependencies; fully removable (toggle off
+ * restores the DOM).
+ */
+(function () {
+  var SEL = [
+    'header', 'nav', 'section', 'main', 'footer',
+    '[data-ad-slot]',
+    '.card', '.slot', '.vslot', '.vidcard', '.rescard', '.tile', '.panel',
+    'h1', 'h2', 'h3', 'h4',
+    'nav a', 'footer a',
+    'button', 'a.btn', '.btn', '.chip', '.cta',
+    'input', 'select', 'textarea',
+    '.hero', '.vhero', '.controls', '.filters', '.rate', '.invgrid', '.river', '.vidrow'
+  ].join(',');
+
+  var KEY = 'rentv_num_' + location.pathname;
+  var on = false, badges = [], touched = [];
+
+  function label(el) {
+    var t = (el.getAttribute('aria-label') || el.getAttribute('placeholder') || '').trim();
+    if (!t) {
+      var clone = el.cloneNode(true);
+      // strip our own badge text if present
+      t = (clone.textContent || '').replace(/\s+/g, ' ').trim();
+    }
+    if (!t && el.getAttribute('data-ad-slot')) t = 'ad-slot:' + el.getAttribute('data-ad-slot');
+    if (!t && el.tagName === 'IMG') t = el.getAttribute('alt') || 'image';
+    var cls = (el.className && typeof el.className === 'string') ? '.' + el.className.split(/\s+/).slice(0, 2).join('.') : '';
+    return { tag: el.tagName.toLowerCase() + cls, text: t.slice(0, 60) };
+  }
+
+  function css() {
+    if (document.getElementById('rentv-num-css')) return;
+    var s = document.createElement('style'); s.id = 'rentv-num-css';
+    s.textContent = [
+      '.rentv-el-badge{position:absolute;z-index:2147483000;top:0;left:0;transform:translate(-4px,-9px);',
+      '  background:#c8102e;color:#fff;font:800 10px/1 ui-monospace,Menlo,monospace;padding:2px 4px;border-radius:4px;',
+      '  box-shadow:0 1px 4px rgba(0,0,0,.4);pointer-events:none;letter-spacing:.02em}',
+      '.rentv-num-mark{outline:1px dashed rgba(200,16,46,.5);outline-offset:1px}',
+      '.rentv-num-flash{outline:3px solid #f0b429!important;transition:outline .2s}',
+      '#rentv-num-toggle{position:fixed;left:14px;bottom:14px;z-index:2147483600;background:#0b0e12;color:#fff;',
+      '  border:1px solid #c8102e;border-radius:9px;padding:9px 13px;font:800 12px Inter,system-ui,sans-serif;',
+      '  cursor:pointer;box-shadow:0 8px 26px rgba(0,0,0,.4)}',
+      '#rentv-num-toggle.on{background:#c8102e}',
+      '#rentv-num-index{position:fixed;right:14px;top:70px;bottom:70px;width:290px;z-index:2147483500;background:#0b0e12;',
+      '  color:#e7ecf1;border:1px solid #1c232b;border-radius:12px;display:none;flex-direction:column;box-shadow:0 20px 60px rgba(0,0,0,.5)}',
+      '#rentv-num-index.on{display:flex}',
+      '#rentv-num-index .h{padding:11px 13px;border-bottom:1px solid #1c232b;display:flex;gap:8px;align-items:center}',
+      '#rentv-num-index .h b{font:800 12px Inter;letter-spacing:.04em}',
+      '#rentv-num-index .h .ct{margin-left:auto;color:#7b848d;font:700 11px Inter}',
+      '#rentv-num-index input{width:100%;margin:8px 10px 6px;width:calc(100% - 20px);padding:7px 9px;border:1px solid #1c232b;',
+      '  background:#11161c;color:#e7ecf1;border-radius:7px;font:13px Inter}',
+      '#rentv-num-index .list{overflow:auto;flex:1;padding:2px 6px 10px}',
+      '#rentv-num-index .row{display:flex;gap:8px;padding:6px 8px;border-radius:6px;cursor:pointer;font:12px/1.3 Inter}',
+      '#rentv-num-index .row:hover{background:#161d25}',
+      '#rentv-num-index .row .n{color:#fff;font:800 11px ui-monospace,monospace;background:#c8102e;border-radius:4px;padding:1px 5px;height:fit-content}',
+      '#rentv-num-index .row .m .tg{color:#8b949e;font-weight:700}',
+      '#rentv-num-index .row .m .tx{color:#c3cbd3}',
+      '#rentv-num-index .cp{padding:8px 12px;border-top:1px solid #1c232b;color:#7b848d;font:11px Inter}'
+    ].join('');
+    document.head.appendChild(s);
+  }
+
+  function build() {
+    var seen = new Set();
+    var els = Array.prototype.slice.call(document.querySelectorAll(SEL)).filter(function (el) {
+      if (seen.has(el)) return false; seen.add(el);
+      if (el.closest('#rentv-num-index') || el.id === 'rentv-num-toggle') return false;
+      var r = el.getBoundingClientRect();
+      return r.width > 8 && r.height > 8; // visible-ish
+    });
+    var rows = '';
+    els.forEach(function (el, i) {
+      var n = i + 1;
+      el.setAttribute('data-el', n);
+      el.classList.add('rentv-num-mark');
+      touched.push(el);
+      var cs = getComputedStyle(el);
+      if (cs.position === 'static') { el.style.position = 'relative'; el.setAttribute('data-num-posfix', '1'); }
+      var b = document.createElement('span'); b.className = 'rentv-el-badge'; b.textContent = n;
+      el.appendChild(b); badges.push(b);
+      var L = label(el);
+      rows += '<div class="row" data-goto="' + n + '"><span class="n">' + n + '</span><span class="m"><span class="tg">' +
+        L.tag + '</span> <span class="tx">' + (L.text ? L.text.replace(/[<>&]/g, '') : '') + '</span></span></div>';
+    });
+    idx.querySelector('.list').innerHTML = rows;
+    idx.querySelector('.ct').textContent = els.length + ' elements';
+  }
+
+  function clear() {
+    badges.forEach(function (b) { b.remove(); }); badges = [];
+    touched.forEach(function (el) {
+      el.classList.remove('rentv-num-mark'); el.removeAttribute('data-el');
+      if (el.getAttribute('data-num-posfix')) { el.style.position = ''; el.removeAttribute('data-num-posfix'); }
+    });
+    touched = [];
+    idx.querySelector('.list').innerHTML = '';
+  }
+
+  var idx, toggleBtn;
+  function ui() {
+    css();
+    toggleBtn = document.createElement('button'); toggleBtn.id = 'rentv-num-toggle';
+    toggleBtn.innerHTML = '①② Elements'; document.body.appendChild(toggleBtn);
+    idx = document.createElement('div'); idx.id = 'rentv-num-index';
+    idx.innerHTML = '<div class="h"><b>Element index</b><span class="ct"></span></div>' +
+      '<input placeholder="filter by number or text…" aria-label="Filter elements">' +
+      '<div class="list"></div>' +
+      '<div class="cp">Click a row to jump. Ref future edits as “' + pageTag() + ' · el N”.</div>';
+    document.body.appendChild(idx);
+
+    toggleBtn.addEventListener('click', function () { set(!on); });
+    document.addEventListener('keydown', function (e) {
+      if (e.key === 'e' && !/input|textarea|select/i.test((e.target.tagName || ''))) set(!on);
+    });
+    idx.querySelector('input').addEventListener('input', function (e) {
+      var q = e.target.value.toLowerCase();
+      idx.querySelectorAll('.row').forEach(function (r) {
+        r.style.display = (!q || r.textContent.toLowerCase().indexOf(q) >= 0) ? '' : 'none';
+      });
+    });
+    idx.querySelector('.list').addEventListener('click', function (e) {
+      var r = e.target.closest('[data-goto]'); if (!r) return;
+      var el = document.querySelector('[data-el="' + r.dataset.goto + '"]'); if (!el) return;
+      el.scrollIntoView({ behavior: 'smooth', block: 'center' });
+      el.classList.add('rentv-num-flash'); setTimeout(function () { el.classList.remove('rentv-num-flash'); }, 1400);
+    });
+  }
+
+  function pageTag() {
+    var m = location.pathname.match(/2026\/([a-e])\.html/); if (m) return 'Template ' + m[1].toUpperCase();
+    if (/2026\/?$/.test(location.pathname)) return 'Selector';
+    if (/advertise/.test(location.pathname)) return 'Advertise';
+    return location.pathname;
+  }
+
+  function set(v) {
+    on = v; toggleBtn.classList.toggle('on', on); idx.classList.toggle('on', on);
+    toggleBtn.innerHTML = on ? '✕ Hide numbers' : '①② Elements';
+    if (on) build(); else clear();
+    try { localStorage.setItem(KEY, on ? '1' : '0'); } catch (e) { }
+  }
+
+  function boot() {
+    ui();
+    var auto = /[?&]num=1/.test(location.search);
+    try { if (!auto && localStorage.getItem(KEY) === '1') auto = true; } catch (e) { }
+    if (auto) set(true);
+  }
+  if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', boot); else boot();
+  window.RENTVNumber = { on: function () { set(true); }, off: function () { set(false); } };
+})();
diff --git a/public/2026/index.html b/public/2026/index.html
index 6f072cec..e595bb1c 100644
--- a/public/2026/index.html
+++ b/public/2026/index.html
@@ -206,4 +206,5 @@ function paintChosen(){
   paintChosen();
 })();
 </script>
+<script src="/2026/elements.js" defer></script>
 </body></html>
diff --git a/public/ad-admin.html b/public/ad-admin.html
index b9c39b27..148c9cbd 100644
--- a/public/ad-admin.html
+++ b/public/ad-admin.html
@@ -193,4 +193,5 @@ async function loadRes(){ const d=await jget('/api/ad/admin/reservations'); if(!
 
 initVCols(); loadPlace(); loadVid(); loadRes();
 </script>
+<script src="/2026/elements.js" defer></script>
 </body></html>
diff --git a/public/ad-creatives/cbre-masthead.svg b/public/ad-creatives/cbre-masthead.svg
new file mode 100644
index 00000000..10032938
--- /dev/null
+++ b/public/ad-creatives/cbre-masthead.svg
@@ -0,0 +1,10 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="970" height="250" viewBox="0 0 970 250" font-family="Inter,Arial,sans-serif">
+<defs><linearGradient id="g" x1="0" y1="0" x2="1" y2="1"><stop offset="0" stop-color="#003f2d"/><stop offset="1" stop-color="#0a5c44"/></linearGradient></defs>
+<rect width="970" height="250" fill="url(#g)"/>
+<rect x="0" y="0" width="8" height="250" fill="#17e88f"/>
+<text x="54" y="96" fill="#fff" font-size="46" font-weight="800" letter-spacing="4">CBRE</text>
+<text x="56" y="140" fill="#8fe3c6" font-size="20" font-weight="600">Advisory &amp; Transaction Services</text>
+<text x="56" y="184" fill="#dff5ec" font-size="17">The market intelligence behind every close.</text>
+<rect x="56" y="200" width="210" height="34" rx="5" fill="#17e88f"/>
+<text x="74" y="223" fill="#023" font-size="15" font-weight="800">See our 2026 outlook →</text>
+</svg>
diff --git a/public/ad-creatives/jll-leaderboard.svg b/public/ad-creatives/jll-leaderboard.svg
new file mode 100644
index 00000000..f553d550
--- /dev/null
+++ b/public/ad-creatives/jll-leaderboard.svg
@@ -0,0 +1,9 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="970" height="90" viewBox="0 0 970 90" font-family="Inter,Arial,sans-serif">
+<rect width="970" height="90" fill="#0a1a2f"/>
+<rect x="0" y="0" width="6" height="90" fill="#e30613"/>
+<text x="34" y="42" fill="#fff" font-size="27" font-weight="800" letter-spacing="2">JLL</text>
+<text x="34" y="68" fill="#9fb4cc" font-size="14">Capital Markets · Western U.S.</text>
+<text x="640" y="40" fill="#fff" font-size="17" font-weight="700">Move your portfolio forward.</text>
+<rect x="640" y="52" width="150" height="26" rx="4" fill="#e30613"/>
+<text x="655" y="70" fill="#fff" font-size="13" font-weight="800">Talk to our team →</text>
+</svg>
diff --git a/public/ad-creatives/mm-rectangle.svg b/public/ad-creatives/mm-rectangle.svg
new file mode 100644
index 00000000..2919c837
--- /dev/null
+++ b/public/ad-creatives/mm-rectangle.svg
@@ -0,0 +1,10 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="300" height="250" viewBox="0 0 300 250" font-family="Inter,Arial,sans-serif">
+<rect width="300" height="250" fill="#10233f"/>
+<rect x="0" y="0" width="300" height="70" fill="#c8102e"/>
+<text x="20" y="45" fill="#fff" font-size="22" font-weight="800">Marcus &amp; Millichap</text>
+<text x="20" y="118" fill="#fff" font-size="18" font-weight="700">Multifamily is</text>
+<text x="20" y="144" fill="#fff" font-size="18" font-weight="700">still moving.</text>
+<text x="20" y="180" fill="#9db4d4" font-size="13">See what's trading in your metro.</text>
+<rect x="20" y="196" width="180" height="34" rx="5" fill="#fff"/>
+<text x="34" y="219" fill="#10233f" font-size="13" font-weight="800">Get the report →</text>
+</svg>
diff --git a/public/advertise.html b/public/advertise.html
index 619ca1a1..117b76c1 100644
--- a/public/advertise.html
+++ b/public/advertise.html
@@ -309,4 +309,5 @@ $('#rForm').addEventListener('submit',async e=>{e.preventDefault();
 (function(){const d=new Date();$('#fStart').value=d.getFullYear()+'-'+String(d.getMonth()+1).padStart(2,'0');})();
 loadPersist(); loadRate(); loadInv();
 </script>
+<script src="/2026/elements.js" defer></script>
 </body></html>

← 827eb166 RENTV 2026: 5-template selector + ad-slots-for-sale system (  ·  back to Rentv 2026  ·  rentv: re-gate site behind un/pw (reverse 2026-08-06 public 23094df1 →