[object Object]

← back to Rentv

auto-save: 2026-07-25T00:01:48 (4 files) — data/markets.json data/news.json data/ticker.json public/market-pulse.html

ce438f6416d510e90303da83fae62a9f1d08c1ca · 2026-07-25 00:01:54 -0700 · Steve Abrams

Files touched

Diff

commit ce438f6416d510e90303da83fae62a9f1d08c1ca
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Sat Jul 25 00:01:54 2026 -0700

    auto-save: 2026-07-25T00:01:48 (4 files) — data/markets.json data/news.json data/ticker.json public/market-pulse.html
---
 data/markets.json        |  2 +-
 data/news.json           |  2 +-
 data/ticker.json         |  2 +-
 public/market-pulse.html | 63 ++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 66 insertions(+), 3 deletions(-)

diff --git a/data/markets.json b/data/markets.json
index 1eccc30d..27426343 100644
--- a/data/markets.json
+++ b/data/markets.json
@@ -1,5 +1,5 @@
 {
-  "fetched_at": "2026-07-25T06:27:01.642Z",
+  "fetched_at": "2026-07-25T06:47:02.458Z",
   "source": "Yahoo Finance — live (CBOE Treasury yields, Dow Jones/MSCI REIT indices)",
   "curve": [
     {
diff --git a/data/news.json b/data/news.json
index 3302c4e3..ed8f8be5 100644
--- a/data/news.json
+++ b/data/news.json
@@ -1,6 +1,6 @@
 {
  "source": "rentv.com — live",
- "fetched_at": "2026-07-25T06:27:01.447Z",
+ "fetched_at": "2026-07-25T06:47:02.252Z",
  "count": 23,
  "items": [
   {
diff --git a/data/ticker.json b/data/ticker.json
index cccbbd02..2dc6a8fd 100644
--- a/data/ticker.json
+++ b/data/ticker.json
@@ -1,5 +1,5 @@
 {
-  "fetched_at": "2026-07-25T06:27:01.617Z",
+  "fetched_at": "2026-07-25T06:47:02.424Z",
   "la_office_vac": {
     "label": "LA OFFICE VAC",
     "value": "23.1%",
diff --git a/public/market-pulse.html b/public/market-pulse.html
index 05ad55ba..380c31c1 100644
--- a/public/market-pulse.html
+++ b/public/market-pulse.html
@@ -196,6 +196,42 @@
     .funds{grid-template-columns:1fr}
     .wrap{padding:16px 14px 40px}
   }
+
+  /* ---------- Persistent left rail (always populated, categories collapsed) ---------- */
+  .mp-layout{display:flex;align-items:flex-start;gap:0}
+  .mp-layout>.wrap{flex:1;min-width:0;max-width:1200px;margin:0}
+  .mp-rail{
+    position:sticky;top:54px;align-self:flex-start;flex:none;width:288px;
+    height:calc(100vh - 54px);overflow-y:auto;
+    background:linear-gradient(180deg,#0b0f16,#080b11);
+    border-right:1px solid var(--line);padding:16px 12px 30px;
+  }
+  .mp-rail .rail-h{display:flex;align-items:center;gap:8px;padding:4px 8px 12px;
+    font-size:11px;letter-spacing:2px;text-transform:uppercase;color:var(--ink-dim)}
+  .mp-rail .rail-h::before{content:"";width:3px;height:13px;background:var(--accent);border-radius:2px}
+  .mp-cat{border:1px solid var(--line);border-radius:10px;margin-bottom:8px;background:var(--panel);overflow:hidden}
+  .mp-cat>summary{list-style:none;cursor:pointer;display:flex;align-items:center;gap:8px;
+    padding:11px 12px;font-size:12px;font-weight:600;letter-spacing:.4px;color:var(--ink);user-select:none}
+  .mp-cat>summary::-webkit-details-marker{display:none}
+  .mp-cat>summary:hover{background:var(--panel-2)}
+  .mp-cat>summary .badge{font-family:var(--mono);font-size:10.5px;font-weight:700;color:#04120f;
+    background:#2dd4bf;border-radius:20px;padding:1px 7px;min-width:20px;text-align:center}
+  .mp-cat>summary .chev{margin-left:auto;color:var(--ink-faint);font-size:10px;transition:transform .2s}
+  .mp-cat[open]>summary .chev{transform:rotate(90deg)}
+  .mp-cat[open]>summary{border-bottom:1px solid var(--line-soft)}
+  .mp-rows{padding:2px 6px 8px}
+  .mp-row{display:flex;align-items:center;justify-content:space-between;gap:10px;
+    padding:6px;border-top:1px solid var(--line-soft);font-size:12px}
+  .mp-row:first-child{border-top:0}
+  .mp-row .rl{color:var(--ink-dim)}
+  .mp-row .rv{font-family:var(--mono);font-weight:600;font-variant-numeric:tabular-nums}
+  .mp-row .rv.up{color:var(--up)}.mp-row .rv.down{color:var(--down)}.mp-row .rv.flat{color:var(--ink)}
+  .mp-rail .rail-foot{padding:12px 8px 0;font-size:10px;letter-spacing:.3px;color:var(--ink-faint)}
+  @media (max-width:900px){
+    .mp-layout{flex-direction:column}
+    .mp-rail{position:static;width:auto;height:auto;border-right:0;border-bottom:1px solid var(--line)}
+    .mp-layout>.wrap{max-width:none}
+  }
 </style>
 </head>
 <body>
@@ -226,6 +262,8 @@
   <div class="df">RENTV.com — Commercial real estate news, Western U.S.</div>
 </aside>
 
+<div class="mp-layout">
+<aside class="mp-rail" id="mpRail"></aside>
 <div class="wrap">
 
   <div class="pulse-band">
@@ -282,6 +320,7 @@
   </footer>
 
 </div>
+</div>
 
 <script>
 /* ============================================================
@@ -510,8 +549,32 @@ function tick(){
     ' ' + (Intl.DateTimeFormat().resolvedOptions().timeZone || '');
 }
 
+/* ---------- persistent left rail: ALWAYS lists every field, categories
+     collapsed by default. Rebuilt on each refresh but open state is preserved
+     so a live update never blanks the panel or re-collapses what you opened. ---------- */
+const esc = s => String(s == null ? '' : s).replace(/[&<>"]/g, c => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;'}[c]));
+function renderRail(){
+  const rail = $('#mpRail');
+  if(!rail) return;
+  const openSet = new Set([...rail.querySelectorAll('.mp-cat[open]')].map(d => d.dataset.cat));
+  const cats = [
+    {key:'curve',   name:'Yield Curve',         rows: marketData.curve.map(c => ({l:c.label + ' UST', v:c.value, d:c.dir}))},
+    {key:'spreads', name:'Curve Spreads',       rows: marketData.spreads.map(s => ({l:s.label, v:s.value, d:s.dir}))},
+    {key:'reits',   name:'REIT Performance',    rows: marketData.reits.map(r => ({l:r.label, v:r.value, d:r.dir}))},
+    {key:'funds',   name:'Market Fundamentals', rows: marketData.fundamentals.map(f => ({l:f.label, v:f.value, d:'flat'}))}
+  ];
+  rail.innerHTML = '<div class="rail-h">Market Data</div>' + cats.map(c => `
+    <details class="mp-cat" data-cat="${c.key}"${openSet.has(c.key) ? ' open' : ''}>
+      <summary>${esc(c.name)}<span class="badge">${c.rows.length}</span><span class="chev">▶</span></summary>
+      <div class="mp-rows">${c.rows.map(r =>
+        `<div class="mp-row"><span class="rl">${esc(r.l)}</span><span class="rv ${r.d}">${esc(r.v)}</span></div>`
+      ).join('')}</div>
+    </details>`).join('') + '<div class="rail-foot">All fields shown · click a category to expand</div>';
+}
+
 /* ---------- render everything from the current marketData ---------- */
 function renderAll(){
+  renderRail();
   renderKPIs();
   renderSpreads();
   renderReits();

← 78d7b01d Add CRE Market Pulse (rates terminal) page + front-page tick  ·  back to Rentv  ·  market-pulse: pin full site nav into the persistent left rai 83c2c24c →