← back to Rentv Market Pulse
RENTV CRE Market Pulse dashboard — rebuilt from Qwen3 14B attempt
8446660f5baa43ca428358f0d08934d6e3fb8196 · 2026-07-24 18:23:49 -0700 · Steve
Files touched
Diff
commit 8446660f5baa43ca428358f0d08934d6e3fb8196
Author: Steve <steve@designerwallcoverings.com>
Date: Fri Jul 24 18:23:49 2026 -0700
RENTV CRE Market Pulse dashboard — rebuilt from Qwen3 14B attempt
---
.gitignore | 8 +
index.html | 485 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 493 insertions(+)
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..1924158
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,8 @@
+node_modules/
+.env*
+tmp/
+*.log
+.DS_Store
+dist/
+build/
+.next/
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..420ff7e
--- /dev/null
+++ b/index.html
@@ -0,0 +1,485 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>RENTV · CRE Market Pulse</title>
+<style>
+ :root{
+ --bg:#080b11;
+ --panel:#0e131c;
+ --panel-2:#111826;
+ --line:#1c2536;
+ --line-soft:#161d2b;
+ --ink:#e8edf5;
+ --ink-dim:#8a97ad;
+ --ink-faint:#5b6879;
+ --up:#22c55e;
+ --up-soft:rgba(34,197,94,.14);
+ --down:#f04141;
+ --down-soft:rgba(240,65,65,.14);
+ --flat:#8a97ad;
+ --accent:#f0b429; /* RENTV amber */
+ --accent-2:#38bdf8; /* data cyan */
+ --mono:"SFMono-Regular",ui-monospace,"JetBrains Mono","Menlo",Consolas,monospace;
+ --sans:"Inter",-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
+ }
+ *{box-sizing:border-box}
+ html,body{margin:0;padding:0}
+ body{
+ background:
+ radial-gradient(1200px 600px at 80% -10%, rgba(56,189,248,.06), transparent 60%),
+ radial-gradient(900px 500px at -10% 110%, rgba(240,180,41,.05), transparent 55%),
+ var(--bg);
+ color:var(--ink);
+ font-family:var(--sans);
+ -webkit-font-smoothing:antialiased;
+ letter-spacing:.1px;
+ min-height:100vh;
+ }
+ .wrap{max-width:1360px;margin:0 auto;padding:22px 26px 46px}
+
+ /* ---------- Header ---------- */
+ header{
+ display:flex;align-items:center;justify-content:space-between;
+ gap:20px;flex-wrap:wrap;
+ padding-bottom:18px;margin-bottom:22px;
+ border-bottom:1px solid var(--line);
+ }
+ .brand{display:flex;align-items:center;gap:14px}
+ .logo{
+ font-weight:800;font-size:22px;letter-spacing:.5px;
+ background:linear-gradient(180deg,#ffe08a,var(--accent));
+ -webkit-background-clip:text;background-clip:text;color:transparent;
+ display:flex;align-items:center;gap:2px;
+ }
+ .logo .tv{
+ background:var(--accent);color:#1a1205;
+ -webkit-text-fill-color:#1a1205;
+ padding:1px 6px;border-radius:4px;font-size:20px;margin-left:1px;
+ }
+ .brand .sub{
+ border-left:1px solid var(--line);padding-left:14px;
+ display:flex;flex-direction:column;line-height:1.25;
+ }
+ .brand .sub .t{font-size:14px;font-weight:600;letter-spacing:2px;color:var(--ink)}
+ .brand .sub .d{font-size:11px;letter-spacing:1.5px;color:var(--ink-faint);text-transform:uppercase}
+
+ .status{display:flex;align-items:center;gap:22px;flex-wrap:wrap}
+ .clock{display:flex;flex-direction:column;align-items:flex-end;line-height:1.3}
+ .clock .lbl{font-size:10px;letter-spacing:1.6px;color:var(--ink-faint);text-transform:uppercase}
+ .clock .val{font-family:var(--mono);font-size:14px;color:var(--ink);font-variant-numeric:tabular-nums}
+ .live{
+ display:flex;align-items:center;gap:8px;
+ font-size:11px;letter-spacing:1.4px;text-transform:uppercase;color:var(--up);
+ padding:5px 11px;border:1px solid rgba(34,197,94,.3);border-radius:999px;
+ background:var(--up-soft);
+ }
+ .dot{width:8px;height:8px;border-radius:50%;background:var(--up);box-shadow:0 0 0 0 rgba(34,197,94,.6);animation:pulse 2s infinite}
+ @keyframes pulse{0%{box-shadow:0 0 0 0 rgba(34,197,94,.55)}70%{box-shadow:0 0 0 7px rgba(34,197,94,0)}100%{box-shadow:0 0 0 0 rgba(34,197,94,0)}}
+
+ /* ---------- Section titles ---------- */
+ .sec-title{
+ display:flex;align-items:center;gap:10px;
+ font-size:11px;letter-spacing:2px;text-transform:uppercase;color:var(--ink-dim);
+ margin:0 0 12px;
+ }
+ .sec-title::before{content:"";width:3px;height:13px;background:var(--accent);border-radius:2px}
+ .sec-title .hint{margin-left:auto;letter-spacing:.5px;text-transform:none;color:var(--ink-faint);font-size:11px}
+
+ /* ---------- KPI tiles ---------- */
+ .kpis{
+ display:grid;grid-template-columns:repeat(6,1fr);gap:12px;margin-bottom:26px;
+ }
+ .kpi{
+ background:linear-gradient(180deg,var(--panel-2),var(--panel));
+ border:1px solid var(--line);border-radius:11px;padding:14px 15px;
+ position:relative;overflow:hidden;
+ }
+ .kpi::after{content:"";position:absolute;left:0;top:0;bottom:0;width:3px;background:var(--accent-2);opacity:.5}
+ .kpi.up::after{background:var(--up)}
+ .kpi.down::after{background:var(--down)}
+ .kpi.flat::after{background:var(--flat)}
+ .kpi .k-lbl{font-size:10.5px;letter-spacing:1.2px;text-transform:uppercase;color:var(--ink-faint)}
+ .kpi .k-val{font-family:var(--mono);font-size:25px;font-weight:600;margin-top:8px;font-variant-numeric:tabular-nums}
+ .kpi .k-delta{
+ margin-top:7px;display:inline-flex;align-items:center;gap:5px;
+ font-family:var(--mono);font-size:12px;padding:2px 8px;border-radius:6px;font-variant-numeric:tabular-nums;
+ }
+ .up .k-delta{color:var(--up);background:var(--up-soft)}
+ .down .k-delta{color:var(--down);background:var(--down-soft)}
+ .flat .k-delta{color:var(--flat);background:rgba(138,151,173,.12)}
+ .arrow{font-size:10px;line-height:1}
+
+ /* ---------- Grid of panels ---------- */
+ .grid{display:grid;grid-template-columns:1.35fr 1fr;gap:16px;margin-bottom:16px}
+ .grid.lower{grid-template-columns:1fr 1fr}
+ .panel{
+ background:linear-gradient(180deg,var(--panel-2),var(--panel));
+ border:1px solid var(--line);border-radius:14px;padding:18px 18px 16px;
+ }
+ canvas{display:block;width:100%}
+
+ /* legend / caption under curve */
+ .curve-foot{
+ display:flex;justify-content:space-between;gap:12px;margin-top:12px;
+ padding-top:12px;border-top:1px solid var(--line-soft);
+ font-size:11px;color:var(--ink-faint);flex-wrap:wrap;
+ }
+ .curve-foot b{color:var(--ink-dim);font-weight:600}
+
+ /* ---------- Credit spreads ---------- */
+ .spreads{display:flex;flex-direction:column;gap:16px;margin-top:4px}
+ .spread .row1{display:flex;justify-content:space-between;align-items:baseline;margin-bottom:8px}
+ .spread .s-lbl{font-size:13px;color:var(--ink)}
+ .spread .s-val{font-family:var(--mono);font-size:16px;font-weight:600}
+ .spread .s-val.up{color:var(--up)}
+ .spread .s-val.down{color:var(--down)}
+ .bar-track{height:9px;background:var(--line-soft);border-radius:6px;overflow:hidden;position:relative}
+ .bar-fill{height:100%;border-radius:6px;background:linear-gradient(90deg,rgba(56,189,248,.5),var(--accent-2));transition:width .8s cubic-bezier(.2,.7,.2,1)}
+ .spread small{color:var(--ink-faint);font-size:10.5px;letter-spacing:.3px}
+
+ /* ---------- REIT bars ---------- */
+ .reits{display:flex;flex-direction:column;gap:15px;margin-top:2px}
+ .reit .r-top{display:flex;justify-content:space-between;align-items:baseline;margin-bottom:8px}
+ .reit .r-lbl{font-size:13px;color:var(--ink)}
+ .reit .r-val{font-family:var(--mono);font-size:15px;color:var(--ink);font-variant-numeric:tabular-nums}
+ .reit .r-track{position:relative;height:26px;background:var(--line-soft);border-radius:7px;display:flex;align-items:center}
+ .reit .r-mid{position:absolute;left:50%;top:0;bottom:0;width:1px;background:var(--line);opacity:.9}
+ .reit .r-bar{position:absolute;top:5px;bottom:5px;border-radius:5px;transition:width .8s cubic-bezier(.2,.7,.2,1)}
+ .reit .r-bar.up{left:50%;background:linear-gradient(90deg,rgba(34,197,94,.35),var(--up))}
+ .reit .r-bar.down{right:50%;background:linear-gradient(270deg,rgba(240,65,65,.35),var(--down))}
+ .reit .r-pct{position:absolute;font-family:var(--mono);font-size:12px;font-weight:600;font-variant-numeric:tabular-nums}
+ .reit .r-pct.up{left:calc(50% + 10px);color:var(--up)}
+ .reit .r-pct.down{right:calc(50% + 10px);color:var(--down)}
+ .reit .r-pct.flat{left:calc(50% + 10px);color:var(--flat)}
+ .reit-scale{display:flex;justify-content:space-between;font-size:10px;color:var(--ink-faint);font-family:var(--mono);margin-top:10px;padding-top:8px;border-top:1px solid var(--line-soft)}
+
+ /* ---------- Fundamentals ---------- */
+ .funds{display:grid;grid-template-columns:1fr 1fr;gap:14px;margin-top:2px}
+ .fund{
+ background:var(--panel);border:1px solid var(--line);border-radius:11px;padding:15px;
+ display:flex;flex-direction:column;gap:2px;
+ }
+ .fund .f-lbl{font-size:10.5px;letter-spacing:1.1px;text-transform:uppercase;color:var(--ink-faint)}
+ .fund .f-val{font-family:var(--mono);font-size:28px;font-weight:600;color:var(--accent);margin:6px 0 4px}
+ .fund .f-meta{font-size:11px;color:var(--ink-dim)}
+ .fund .f-src{font-size:10.5px;color:var(--ink-faint);margin-top:3px}
+ .tag{display:inline-block;font-size:9.5px;letter-spacing:.6px;color:var(--accent-2);border:1px solid rgba(56,189,248,.25);border-radius:5px;padding:1px 6px;margin-top:8px;width:fit-content}
+
+ footer{
+ margin-top:30px;padding-top:16px;border-top:1px solid var(--line);
+ display:flex;justify-content:space-between;gap:14px;flex-wrap:wrap;
+ font-size:11px;color:var(--ink-faint);
+ }
+ footer .disc{max-width:70%}
+
+ @media (max-width:1040px){
+ .kpis{grid-template-columns:repeat(3,1fr)}
+ .grid,.grid.lower{grid-template-columns:1fr}
+ }
+ @media (max-width:560px){
+ .kpis{grid-template-columns:repeat(2,1fr)}
+ .funds{grid-template-columns:1fr}
+ .wrap{padding:16px 14px 40px}
+ }
+</style>
+</head>
+<body>
+<div class="wrap">
+
+ <header>
+ <div class="brand">
+ <div class="logo">REN<span class="tv">TV</span></div>
+ <div class="sub">
+ <span class="t">CRE MARKET PULSE</span>
+ <span class="d">Commercial Real Estate · Rates & Capital Markets</span>
+ </div>
+ </div>
+ <div class="status">
+ <div class="live"><span class="dot"></span>Live Feed</div>
+ <div class="clock">
+ <span class="lbl">Data as of</span>
+ <span class="val" id="timestamp">—</span>
+ </div>
+ </div>
+ </header>
+
+ <!-- KPI TILES -->
+ <div class="sec-title">US Treasury Yield Curve <span class="hint" id="curveHint"></span></div>
+ <div class="kpis" id="kpis"></div>
+
+ <!-- CHARTS -->
+ <div class="grid">
+ <div class="panel">
+ <div class="sec-title">Yield Curve <span class="hint">Constant-maturity Treasury yields (%)</span></div>
+ <canvas id="yieldCurve" height="300"></canvas>
+ <div class="curve-foot" id="curveFoot"></div>
+ </div>
+ <div class="panel">
+ <div class="sec-title">Credit & Curve Spreads <span class="hint">basis points</span></div>
+ <div class="spreads" id="spreads"></div>
+ </div>
+ </div>
+
+ <!-- LOWER -->
+ <div class="grid lower">
+ <div class="panel">
+ <div class="sec-title">REIT Performance <span class="hint">session change</span></div>
+ <div class="reits" id="reits"></div>
+ <div class="reit-scale"><span>−1.0%</span><span>flat</span><span>+1.0%</span></div>
+ </div>
+ <div class="panel">
+ <div class="sec-title">Market Fundamentals <span class="hint">SoCal CRE</span></div>
+ <div class="funds" id="funds"></div>
+ </div>
+ </div>
+
+ <footer>
+ <div class="disc">
+ Snapshot for informational purposes only — not investment advice. Yields are constant-maturity
+ Treasury rates; REIT levels are last-session close. Fundamentals sourced from cited market reports.
+ </div>
+ <div>© <span id="yr"></span> RENTV.com · CRE Market Pulse</div>
+ </footer>
+
+</div>
+
+<script>
+/* ============================================================
+ REAL MARKET SNAPSHOT — embedded verbatim as a JS object.
+ Every visual on the page is rendered from this single source.
+ ============================================================ */
+const marketData = {
+ "curve": [
+ {"label": "3-Month", "value": "3.80%", "change_bps": 5, "dir": "up"},
+ {"label": "5-Year", "value": "4.46%", "change_bps": 5, "dir": "up"},
+ {"label": "10-Year", "value": "4.70%", "change_bps": 5, "dir": "up"},
+ {"label": "30-Year", "value": "5.17%", "change_bps": 2, "dir": "up"}
+ ],
+ "spreads": [
+ {"label": "30Y – 10Y (curve)", "value": "+47 bps", "dir": "up"},
+ {"label": "10Y – 3M (curve)", "value": "+90 bps", "dir": "up"}
+ ],
+ "reits": [
+ {"label": "DJ U.S. Real Estate", "value": "394.84", "change_pct": -0.22, "dir": "down"},
+ {"label": "MSCI U.S. REIT", "value": "1,289.55", "change_pct": 0, "dir": "flat"},
+ {"label": "Vanguard REIT ETF (VNQ)", "value": "$98.69", "change_pct": -0.84, "dir": "down"}
+ ],
+ "fundamentals": [
+ {"label": "LA OFFICE VAC", "value": "23.1%", "as_of": "Q2 2026", "source": "CBRE/JLL market reports"},
+ {"label": "IE INDUSTRIAL CAP", "value": "5.2%", "as_of": "Q2 2026", "source": "Colliers IE industrial"}
+ ]
+};
+
+/* ---------- helpers ---------- */
+const $ = s => document.querySelector(s);
+const num = s => parseFloat(String(s).replace(/[^0-9.\-]/g, ''));
+const arrow = d => d === 'up' ? '▲' : d === 'down' ? '▼' : '●';
+
+/* ---------- 1. KPI tiles (curve tenors + spreads) ---------- */
+function renderKPIs(){
+ const el = $('#kpis');
+ const curveTiles = marketData.curve.map(c => `
+ <div class="kpi ${c.dir}">
+ <div class="k-lbl">${c.label} UST</div>
+ <div class="k-val">${c.value}</div>
+ <div class="k-delta"><span class="arrow">${arrow(c.dir)}</span>${c.change_bps} bps</div>
+ </div>`);
+ const spreadTiles = marketData.spreads.map(s => `
+ <div class="kpi ${s.dir}">
+ <div class="k-lbl">${s.label}</div>
+ <div class="k-val">${s.value.replace(/\s*bps/,'')}<span style="font-size:13px;color:var(--ink-faint)"> bps</span></div>
+ <div class="k-delta"><span class="arrow">${arrow(s.dir)}</span>steepening</div>
+ </div>`);
+ el.innerHTML = curveTiles.concat(spreadTiles).join('');
+
+ const dirs = marketData.curve.map(c => c.dir);
+ const allUp = dirs.every(d => d === 'up');
+ $('#curveHint').textContent = allUp ? 'Bear steepening — all tenors higher' : '';
+}
+
+/* ---------- 2. Yield curve line chart (hand-drawn canvas, DPR-aware) ---------- */
+function drawYieldCurve(){
+ const cv = $('#yieldCurve');
+ const dpr = window.devicePixelRatio || 1;
+ const cssW = cv.clientWidth || 640;
+ const cssH = parseInt(cv.getAttribute('height'), 10);
+ cv.width = cssW * dpr;
+ cv.height = cssH * dpr;
+ const ctx = cv.getContext('2d');
+ ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
+ ctx.clearRect(0, 0, cssW, cssH);
+
+ const data = marketData.curve;
+ const padL = 46, padR = 22, padT = 30, padB = 34;
+ const W = cssW - padL - padR;
+ const H = cssH - padT - padB;
+
+ const vals = data.map(d => num(d.value));
+ const dMin = Math.min(...vals), dMax = Math.max(...vals);
+ // pad the y-domain to whole-ish gridlines
+ const lo = Math.floor((dMin - 0.25) * 4) / 4;
+ const hi = Math.ceil((dMax + 0.25) * 4) / 4;
+ const x = i => padL + (data.length === 1 ? W/2 : i * (W / (data.length - 1)));
+ const y = v => padT + H - ((v - lo) / (hi - lo)) * H;
+
+ ctx.font = '11px ' + getCSS('--mono');
+ ctx.textBaseline = 'middle';
+
+ // horizontal gridlines + y labels
+ const steps = 4;
+ for (let i = 0; i <= steps; i++){
+ const v = lo + (hi - lo) * (i / steps);
+ const yy = y(v);
+ ctx.strokeStyle = 'rgba(28,37,54,.9)';
+ ctx.lineWidth = 1;
+ ctx.beginPath(); ctx.moveTo(padL, yy); ctx.lineTo(padL + W, yy); ctx.stroke();
+ ctx.fillStyle = '#5b6879';
+ ctx.textAlign = 'right';
+ ctx.fillText(v.toFixed(2) + '%', padL - 8, yy);
+ }
+
+ // area fill under curve
+ const grad = ctx.createLinearGradient(0, padT, 0, padT + H);
+ grad.addColorStop(0, 'rgba(56,189,248,.28)');
+ grad.addColorStop(1, 'rgba(56,189,248,0)');
+ ctx.beginPath();
+ ctx.moveTo(x(0), y(vals[0]));
+ for (let i = 1; i < data.length; i++) ctx.lineTo(x(i), y(vals[i]));
+ ctx.lineTo(x(data.length - 1), padT + H);
+ ctx.lineTo(x(0), padT + H);
+ ctx.closePath();
+ ctx.fillStyle = grad;
+ ctx.fill();
+
+ // the curve line
+ ctx.beginPath();
+ ctx.moveTo(x(0), y(vals[0]));
+ for (let i = 1; i < data.length; i++) ctx.lineTo(x(i), y(vals[i]));
+ ctx.strokeStyle = '#38bdf8';
+ ctx.lineWidth = 2.5;
+ ctx.lineJoin = 'round';
+ ctx.shadowColor = 'rgba(56,189,248,.5)';
+ ctx.shadowBlur = 10;
+ ctx.stroke();
+ ctx.shadowBlur = 0;
+
+ // points + value labels + x labels
+ data.forEach((d, i) => {
+ const px = x(i), py = y(vals[i]);
+ ctx.beginPath(); ctx.arc(px, py, 4.5, 0, Math.PI * 2);
+ ctx.fillStyle = '#0e131c'; ctx.fill();
+ ctx.lineWidth = 2.5; ctx.strokeStyle = '#38bdf8'; ctx.stroke();
+
+ // value label above point
+ ctx.fillStyle = '#e8edf5';
+ ctx.font = 'bold 12px ' + getCSS('--mono');
+ ctx.textAlign = 'center';
+ ctx.fillText(d.value, px, py - 16);
+
+ // x-axis tenor label
+ ctx.fillStyle = '#8a97ad';
+ ctx.font = '11px ' + getCSS('--sans');
+ ctx.fillText(d.label.replace('-', ' '), px, padT + H + 18);
+ });
+
+ // caption footer
+ $('#curveFoot').innerHTML = `
+ <span>Front end <b>3.80%</b> · Long end <b>5.17%</b></span>
+ <span>Curve is <b>upward-sloping</b> (+137 bps 3M→30Y)</span>`;
+}
+
+function getCSS(v){ return getComputedStyle(document.documentElement).getPropertyValue(v).trim() || 'monospace'; }
+
+/* ---------- 3. Credit / curve spreads ---------- */
+function renderSpreads(){
+ const el = $('#spreads');
+ const vals = marketData.spreads.map(s => Math.abs(num(s.value)));
+ const max = Math.max(...vals, 1);
+ el.innerHTML = marketData.spreads.map(s => {
+ const v = num(s.value);
+ const pct = Math.max(6, Math.abs(v) / max * 100);
+ return `
+ <div class="spread">
+ <div class="row1">
+ <span class="s-lbl">${s.label}</span>
+ <span class="s-val ${s.dir}">${arrow(s.dir)} ${s.value}</span>
+ </div>
+ <div class="bar-track"><div class="bar-fill" style="width:0%" data-w="${pct}"></div></div>
+ <small>${Math.abs(v)} bps ${v >= 0 ? 'positive' : 'inverted'} · steeper vs prior</small>
+ </div>`;
+ }).join('');
+ // animate widths on next frame
+ requestAnimationFrame(() => el.querySelectorAll('.bar-fill')
+ .forEach(b => b.style.width = b.dataset.w + '%'));
+}
+
+/* ---------- 4. REIT performance (diverging bars around zero) ---------- */
+function renderReits(){
+ const el = $('#reits');
+ const SCALE = 1.0; // ±1% = full half-width
+ el.innerHTML = marketData.reits.map(r => {
+ const p = r.change_pct;
+ const w = Math.min(50, Math.abs(p) / SCALE * 50); // % of full track
+ const bar = r.dir === 'up'
+ ? `<div class="r-bar up" style="width:0%" data-w="${w}"></div>`
+ : r.dir === 'down'
+ ? `<div class="r-bar down" style="width:0%" data-w="${w}"></div>`
+ : '';
+ const pctTxt = (p > 0 ? '+' : '') + p.toFixed(2) + '%';
+ return `
+ <div class="reit">
+ <div class="r-top">
+ <span class="r-lbl">${r.label}</span>
+ <span class="r-val">${r.value}</span>
+ </div>
+ <div class="r-track">
+ <div class="r-mid"></div>
+ ${bar}
+ <span class="r-pct ${r.dir}">${arrow(r.dir)} ${pctTxt}</span>
+ </div>
+ </div>`;
+ }).join('');
+ requestAnimationFrame(() => el.querySelectorAll('.r-bar')
+ .forEach(b => b.style.width = b.dataset.w + '%'));
+}
+
+/* ---------- 5. Fundamentals KPI tiles ---------- */
+function renderFunds(){
+ $('#funds').innerHTML = marketData.fundamentals.map(f => `
+ <div class="fund">
+ <span class="f-lbl">${f.label}</span>
+ <span class="f-val">${f.value}</span>
+ <span class="f-meta">As of ${f.as_of}</span>
+ <span class="f-src">${f.source}</span>
+ <span class="tag">MARKET REPORT</span>
+ </div>`).join('');
+}
+
+/* ---------- live timestamp ---------- */
+function tick(){
+ const now = new Date();
+ const opts = { year:'numeric', month:'short', day:'numeric',
+ hour:'2-digit', minute:'2-digit', second:'2-digit' };
+ $('#timestamp').textContent = now.toLocaleString(undefined, opts) +
+ ' ' + (Intl.DateTimeFormat().resolvedOptions().timeZone || '');
+}
+
+/* ---------- boot ---------- */
+function boot(){
+ $('#yr').textContent = new Date().getFullYear();
+ renderKPIs();
+ renderSpreads();
+ renderReits();
+ renderFunds();
+ drawYieldCurve();
+ tick(); setInterval(tick, 1000);
+}
+boot();
+
+let rt;
+window.addEventListener('resize', () => { clearTimeout(rt); rt = setTimeout(drawYieldCurve, 120); });
+</script>
+</body>
+</html>
(oldest)
·
back to Rentv Market Pulse
·
Keep curve endpoint value labels inside plot area bc3665d →