← back to Rentv
Add 10 numbered front-end layout concepts (mockups/) — shared hamburger-left/user-right chrome, live data, pick-tray
70f6d4b0e08938429b767a6f535bf5527f3bcb10 · 2026-07-22 13:31:51 -0700 · Steve
Files touched
A mockups/L01.htmlA mockups/L02.htmlA mockups/L03.htmlA mockups/L04.htmlA mockups/L05.htmlA mockups/L06.htmlA mockups/L07.htmlA mockups/L08.htmlA mockups/L09.htmlA mockups/L10.htmlA mockups/assets/base.cssA mockups/assets/chrome.jsA mockups/data/la-commercial.jsonA mockups/data/news.jsonA mockups/data/ticker.jsonA mockups/data/videos.jsonA mockups/index.html
Diff
commit 70f6d4b0e08938429b767a6f535bf5527f3bcb10
Author: Steve <steve@designerwallcoverings.com>
Date: Wed Jul 22 13:31:51 2026 -0700
Add 10 numbered front-end layout concepts (mockups/) — shared hamburger-left/user-right chrome, live data, pick-tray
---
mockups/L01.html | 61 +++++++++++++++
mockups/L02.html | 66 ++++++++++++++++
mockups/L03.html | 52 +++++++++++++
mockups/L04.html | 50 ++++++++++++
mockups/L05.html | 53 +++++++++++++
mockups/L06.html | 55 +++++++++++++
mockups/L07.html | 59 ++++++++++++++
mockups/L08.html | 58 ++++++++++++++
mockups/L09.html | 56 +++++++++++++
mockups/L10.html | 53 +++++++++++++
mockups/assets/base.css | 82 +++++++++++++++++++
mockups/assets/chrome.js | 169 ++++++++++++++++++++++++++++++++++++++++
mockups/data/la-commercial.json | 1 +
mockups/data/news.json | 1 +
mockups/data/ticker.json | 1 +
mockups/data/videos.json | 1 +
mockups/index.html | 61 +++++++++++++++
17 files changed, 879 insertions(+)
diff --git a/mockups/L01.html b/mockups/L01.html
new file mode 100644
index 00000000..8ff7fde5
--- /dev/null
+++ b/mockups/L01.html
@@ -0,0 +1,61 @@
+<!doctype html><html lang="en"><head>
+<meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
+<title>L01 · Trading Desk — RENTV redesign</title>
+<link rel="stylesheet" href="assets/base.css">
+<style>
+ body{background:#070a0e;color:#cfd6dd;font:14px/1.4 ui-monospace,Menlo,Consolas,monospace}
+ .wrap{max-width:1320px;margin:0 auto;padding:14px}
+ .rail{display:flex;gap:0;flex-wrap:wrap;border:1px solid #1c232b;background:#0b0e12;border-radius:8px;overflow:hidden;margin-bottom:12px}
+ .rail .c{flex:1;min-width:150px;padding:9px 12px;border-right:1px solid #1c232b}
+ .rail .k{font-size:10px;color:#6b747d;letter-spacing:.08em}.rail .v{font-size:17px;font-weight:700;color:#e7ecf1}
+ .rail .v.dn{color:#ff6b6b}.rail .v.up{color:#37d67a}
+ .cmd{width:100%;background:#0b0e12;border:1px solid #1c232b;border-radius:8px;color:#e7ecf1;padding:11px 14px;font:inherit;margin-bottom:12px}
+ .grid{display:grid;grid-template-columns:1.4fr 1fr;gap:12px}
+ .panel{background:#0b0e12;border:1px solid #1c232b;border-radius:8px;overflow:hidden}
+ .panel h3{margin:0;padding:9px 12px;font-size:11px;letter-spacing:.12em;text-transform:uppercase;color:#8b949e;border-bottom:1px solid #1c232b;background:#0e131a}
+ .lead{padding:14px}.lead .tag{font-size:11px;font-weight:700}.lead h2{margin:6px 0;font:700 20px/1.25 system-ui;color:#fff}
+ .lead .li{display:block;height:150px;border-radius:6px;margin-top:8px}
+ table{width:100%;border-collapse:collapse}
+ td{padding:8px 12px;border-bottom:1px solid #141a21;font-size:12.5px;vertical-align:top}
+ tr:hover td{background:#0e131a}
+ .sx{display:inline-block;width:8px;height:8px;border-radius:2px;margin-right:6px;vertical-align:middle}
+ .watch .r{display:flex;justify-content:space-between;padding:9px 12px;border-bottom:1px solid #141a21}
+ .watch .a{font-size:12px}.watch .p{font-weight:700;color:#fff}
+ .rev{display:flex;gap:8px;overflow:auto;padding:12px}
+ .rev .v{min-width:150px}.rev .t{height:84px;border-radius:6px;display:block}.rev .n{font-size:11px;margin-top:5px;color:#cfd6dd}
+ .foot{color:#4b545c;font-size:11px;text-align:center;padding:16px}
+ @media(max-width:820px){.grid{grid-template-columns:1fr}}
+</style></head>
+<body>
+<div class="wrap">
+ <div class="rail" data-eln id="rail"></div>
+ <input class="cmd" data-eln placeholder="⌘ search deals · addresses · brokers · sectors →">
+ <div class="grid">
+ <div style="display:flex;flex-direction:column;gap:12px">
+ <div class="panel" data-eln id="lead"></div>
+ <div class="panel" data-eln><h3>News Blotter — live tape</h3><table id="blotter"></table></div>
+ <div class="panel" data-eln><h3>Recorded Activity — LA County</h3><table id="recent"></table></div>
+ </div>
+ <div style="display:flex;flex-direction:column;gap:12px">
+ <div class="panel watch" data-eln><h3>LA Marquee Watchlist</h3><div id="watch"></div></div>
+ <div class="panel" data-eln><h3>Brokerage League Table</h3><table id="firms"></table></div>
+ <div class="panel" data-eln><h3>The REview — video tape</h3><div class="rev" id="rev"></div></div>
+ </div>
+ </div>
+ <div class="foot" data-eln>RENTV TERMINAL · L01 Trading Desk · data live from rentv.com + LA County Assessor + CA DRE</div>
+</div>
+<script src="assets/chrome.js"></script>
+<script>
+RENTV.mount({dark:true, layout:'L01', render(d){
+ const {esc,shortUsd,usd,dstr,sectionColor,imgTag}=RENTV;
+ const t=d.ticker||{};
+ rail.innerHTML=Object.values(t).filter(m=>m&&m.label).map(m=>`<div class="c"><div class="k">${m.label}</div><div class="v ${m.dir||''}">${m.value}${m.change_pct!=null?` <span style="font-size:11px">${m.change_pct>0?'+':''}${m.change_pct}%</span>`:''}</div></div>`).join('');
+ const it=d.news.items||[], l=it[0];
+ lead.innerHTML=`<h3>Lead Story</h3><div class="lead"><span class="tag" style="color:${sectionColor(l.cat)}">${esc(l.cat||'').toUpperCase()}</span><h2>${esc(l.title)}</h2>${imgTag(l.image,'li',l.id)}</div>`;
+ blotter.innerHTML=it.slice(1,13).map(n=>`<tr><td style="width:34px;color:#4b545c">${n.id}</td><td><span class="sx" style="background:${sectionColor(n.cat)}"></span>${esc(n.title)}</td></tr>`).join('');
+ document.getElementById('recent').innerHTML=(d.la.recent||[]).slice(0,7).map(r=>`<tr><td style="color:#6b747d;width:60px">${dstr(r.recording_date)}</td><td>${esc(r.type_label)} · ${esc(r.use_desc||'—')}</td><td style="text-align:right;color:#fff">${r.assessed_total?shortUsd(r.assessed_total):'—'}</td></tr>`).join('');
+ watch.innerHTML=(d.la.marquee||[]).slice(0,8).map(m=>`<div class="r"><span class="a">${esc(m.address||m.ain)}<br><span style="color:#6b747d;font-size:10px">${esc(m.type_label)}</span></span><span class="p">${shortUsd(m.assessed_total)}</span></div>`).join('');
+ document.getElementById('firms').innerHTML=(d.la.firms||[]).slice(0,8).map((f,i)=>`<tr><td style="color:#6b747d;width:24px">${i+1}</td><td>${esc(f.name)}</td><td style="text-align:right;color:#37d67a">${(f.agent_count||0).toLocaleString()}</td></tr>`).join('');
+ rev.innerHTML=(d.videos.items||[]).slice(0,8).map(v=>`<div class="v">${imgTag(v.thumb,'t',v.id)}<div class="n">${esc(v.title).slice(0,48)}</div></div>`).join('');
+}});
+</script></body></html>
diff --git a/mockups/L02.html b/mockups/L02.html
new file mode 100644
index 00000000..f9c18943
--- /dev/null
+++ b/mockups/L02.html
@@ -0,0 +1,66 @@
+<!doctype html><html lang="en"><head>
+<meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
+<title>L02 · Editorial Magazine — RENTV redesign</title>
+<link rel="stylesheet" href="assets/base.css">
+<link href="https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,500;0,800;1,500&family=Inter:wght@400;600;800&display=swap" rel="stylesheet">
+<style>
+ body{background:#fbfbf9;color:#14171a;font-family:'Inter',system-ui,sans-serif}
+ .wrap{max-width:1140px;margin:0 auto;padding:26px 22px 60px}
+ .kick{display:flex;justify-content:space-between;align-items:baseline;border-bottom:2px solid #14171a;padding-bottom:10px}
+ .kick .d{font-size:12px;letter-spacing:.16em;text-transform:uppercase;color:#5b636b}
+ .kick .h{font-family:'Playfair Display',serif;font-style:italic;font-size:18px}
+ .hero{display:grid;grid-template-columns:1.5fr 1fr;gap:30px;margin:28px 0;align-items:center}
+ .hero .im{height:380px;border-radius:4px;display:block}
+ .hero .tag{color:#c8102e;font-weight:800;font-size:12px;letter-spacing:.1em;text-transform:uppercase}
+ .hero h1{font-family:'Playfair Display',serif;font-size:44px;line-height:1.06;margin:10px 0 12px;font-weight:800}
+ .hero p{color:#40474e;font-size:15px;line-height:1.6}
+ .secondary{display:grid;grid-template-columns:1fr 1fr;gap:26px;border-top:1px solid #e4e7ea;padding-top:24px}
+ .sec .im{height:180px;border-radius:4px;display:block;margin-bottom:10px}
+ .sec .tag{color:#c8102e;font-weight:800;font-size:11px;letter-spacing:.08em;text-transform:uppercase}
+ .sec h3{font-family:'Playfair Display',serif;font-size:22px;line-height:1.15;margin:5px 0}
+ h2.rule{font-family:'Playfair Display',serif;font-size:15px;text-transform:uppercase;letter-spacing:.05em;border-top:2px solid #14171a;padding-top:10px;margin:40px 0 16px}
+ .river{columns:3;column-gap:26px}.river a{display:block;break-inside:avoid;margin-bottom:14px;color:#14171a;text-decoration:none;border-bottom:1px solid #eee;padding-bottom:12px}
+ .river .t{font-size:10px;color:#c8102e;font-weight:800;letter-spacing:.06em}.river h4{font:600 15px/1.3 'Inter';margin:3px 0}
+ .laed{display:grid;grid-template-columns:1fr 1.3fr;gap:24px;background:#14171a;color:#f3f3f0;border-radius:6px;padding:26px;align-items:center}
+ .laed .big{font-family:'Playfair Display',serif;font-size:40px;font-weight:800;color:#fff}
+ .laed .addr{font-size:15px;font-weight:600;margin:4px 0}.laed .brief{font-size:13px;color:#b9bfc5;line-height:1.6;margin-top:8px}
+ .two{display:grid;grid-template-columns:1.4fr 1fr;gap:30px;margin-top:14px}
+ .revrow{display:flex;gap:14px}.revrow .v{flex:1}.revrow .im{height:110px;border-radius:4px;display:block}.revrow .n{font-size:12.5px;font-weight:600;margin-top:6px}
+ .firms .f{display:flex;justify-content:space-between;border-bottom:1px solid #e4e7ea;padding:9px 0;font-size:14px}
+ .firms .f b{color:#c8102e}
+ .cta{background:#c8102e;color:#fff;border-radius:6px;padding:26px;text-align:center;margin-top:40px}
+ .cta h3{font-family:'Playfair Display',serif;font-size:26px;margin:0 0 6px}.cta button{margin-top:12px;background:#fff;color:#c8102e;border:0;border-radius:4px;padding:11px 26px;font-weight:800;cursor:pointer}
+ .foot{color:#8b939b;font-size:12px;text-align:center;margin-top:40px}
+ @media(max-width:820px){.hero,.secondary,.two,.laed{grid-template-columns:1fr}.river{columns:1}}
+</style></head>
+<body>
+<div class="wrap">
+ <div class="kick" data-eln><span class="d" id="date"></span><span class="h">The Regional Authority in Commercial Real Estate — since 1998</span></div>
+ <div class="hero" data-eln id="hero"></div>
+ <div class="secondary" data-eln id="secondary"></div>
+ <h2 class="rule" data-eln>Across the Sectors</h2>
+ <div class="river" data-eln id="river"></div>
+ <h2 class="rule">LA Commercial Intelligence</h2>
+ <div class="laed" data-eln id="laed"></div>
+ <div class="two">
+ <div><h2 class="rule">The REview</h2><div class="revrow" data-eln id="rev"></div></div>
+ <div><h2 class="rule">Top LA Brokerages</h2><div class="firms" data-eln id="firms"></div></div>
+ </div>
+ <div class="cta" data-eln><h3>The morning CRE briefing, in your inbox.</h3><div>Join thousands of West-Coast dealmakers.</div><button>Subscribe free</button></div>
+ <div class="foot" data-eln>RENTV.com · L02 Editorial Magazine · live data</div>
+</div>
+<script src="assets/chrome.js"></script>
+<script>
+RENTV.mount({layout:'L02', render(d){
+ const {esc,shortUsd,imgTag,sectionColor}=RENTV;
+ date.textContent=new Date(d.news.fetched_at||Date.now()).toLocaleDateString(undefined,{weekday:'long',month:'long',day:'numeric',year:'numeric'});
+ const it=d.news.items||[], l=it[0];
+ hero.innerHTML=`${imgTag(l.image,'im',l.id)}<div><span class="tag">${esc(l.cat)}</span><h1>${esc(l.title)}</h1><p>${esc((d.la.marquee||[])[0]?.brief||'').slice(0,180)||'The definitive regional read on commercial real estate deals, financing and development across the Western U.S.'}</p><a href="${esc(l.url)}" style="color:#c8102e;font-weight:800;text-decoration:none">Read the full story →</a></div>`;
+ secondary.innerHTML=it.slice(1,3).map(n=>`<div class="sec">${imgTag(n.image,'im',n.id)}<span class="tag">${esc(n.cat)}</span><h3>${esc(n.title)}</h3></div>`).join('');
+ river.innerHTML=it.slice(3,15).map(n=>`<a href="${esc(n.url)}"><span class="t">${esc(n.cat).toUpperCase()}</span><h4>${esc(n.title)}</h4></a>`).join('');
+ const m=(d.la.marquee||[])[0];
+ laed.innerHTML=`<div><div class="big">${shortUsd(m.assessed_total)}</div><div style="font-size:11px;letter-spacing:.1em;text-transform:uppercase;color:#9aa1a8">Assessed value · ${esc(m.type_label)}</div></div><div><div class="addr">${esc(m.address)}, ${esc(m.city)}</div><div class="brief">${esc(m.brief)}</div></div>`;
+ rev.innerHTML=(d.videos.items||[]).slice(0,2).map(v=>`<div class="v">${imgTag(v.thumb,'im',v.id)}<div class="n">${esc(v.title)}</div></div>`).join('');
+ document.getElementById('firms').innerHTML=(d.la.firms||[]).slice(0,7).map(f=>`<div class="f"><span>${esc(f.name)}</span><b>${(f.agent_count||0).toLocaleString()}</b></div>`).join('');
+}});
+</script></body></html>
diff --git a/mockups/L03.html b/mockups/L03.html
new file mode 100644
index 00000000..96089da3
--- /dev/null
+++ b/mockups/L03.html
@@ -0,0 +1,52 @@
+<!doctype html><html lang="en"><head>
+<meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
+<title>L03 · Brutalist Grid — RENTV redesign</title>
+<link rel="stylesheet" href="assets/base.css">
+<style>
+ body{background:#f4f2ec;color:#111;font-family:'Arial Black','Helvetica Neue',system-ui,sans-serif}
+ .wrap{max-width:1280px;margin:0 auto;padding:16px}
+ .mh{border:3px solid #111;padding:14px 16px;display:flex;justify-content:space-between;align-items:flex-end;flex-wrap:wrap;gap:8px}
+ .mh h1{font-size:64px;line-height:.9;letter-spacing:-3px;margin:0}.mh h1 i{color:#c8102e;font-style:normal}
+ .mh .sub{font:700 12px/1.2 Arial;text-transform:uppercase;letter-spacing:.1em;max-width:280px;text-align:right}
+ .idx{display:flex;gap:0;border:3px solid #111;border-top:0;overflow:auto}
+ .idx .i{flex:1;min-width:120px;border-right:3px solid #111;padding:10px 12px}
+ .idx .i:last-child{border-right:0}.idx .n{font-size:26px}.idx .k{font:700 10px Arial;text-transform:uppercase;letter-spacing:.08em}
+ .cells{display:grid;grid-template-columns:repeat(4,1fr);gap:0;border-left:3px solid #111;border-top:3px solid #111}
+ .cell{border-right:3px solid #111;border-bottom:3px solid #111;padding:14px;min-height:150px;display:flex;flex-direction:column;justify-content:space-between}
+ .cell .c{font:800 10px Arial;text-transform:uppercase;letter-spacing:.06em;color:#fff;padding:2px 6px;align-self:flex-start}
+ .cell h3{font:800 17px/1.08 'Helvetica Neue';letter-spacing:-.3px;margin:10px 0 0}
+ .cell.big{grid-column:span 2;grid-row:span 2;background:#111;color:#fff}.cell.big h3{font-size:30px}
+ .cell.val{background:#c8102e;color:#fff}.cell.val .big{font-size:44px;line-height:.9;letter-spacing:-2px}
+ .band{border:3px solid #111;border-top:0;padding:12px 16px;display:flex;justify-content:space-between;flex-wrap:wrap;gap:14px}
+ .band .b{font:700 13px Arial}.band .b b{font-size:22px;display:block}
+ .ledger{border:3px solid #111;border-top:0;font:700 12px/1.5 'Courier New',monospace}
+ .ledger .r{display:flex;justify-content:space-between;padding:7px 16px;border-bottom:1px solid #111}
+ .foot{border:3px solid #111;border-top:0;padding:12px 16px;font:700 11px Arial;text-transform:uppercase;letter-spacing:.08em}
+ @media(max-width:820px){.cells{grid-template-columns:1fr 1fr}.mh h1{font-size:40px}.cell.big{grid-column:span 2}}
+</style></head>
+<body>
+<div class="wrap">
+ <div class="mh" data-eln><h1>REN<i>TV</i></h1><div class="sub" id="sub">Commercial Real Estate News · The Regional Authority</div></div>
+ <div class="idx" data-eln id="idx"></div>
+ <div class="cells" data-eln id="cells"></div>
+ <div class="band" data-eln id="firms"></div>
+ <div class="ledger" data-eln id="ledger"></div>
+ <div class="foot" data-eln>L03 Brutalist Grid — every number is real, from the live feed</div>
+</div>
+<script src="assets/chrome.js"></script>
+<script>
+RENTV.mount({layout:'L03', render(d){
+ const {esc,shortUsd,dstr,sectionColor}=RENTV;
+ sub.textContent='Commercial Real Estate News · '+((d.news.items||[]).length)+' live stories · '+new Date(d.news.fetched_at||Date.now()).toLocaleDateString();
+ const t=d.ticker||{};
+ idx.innerHTML=Object.values(t).filter(m=>m&&m.label).map(m=>`<div class="i"><div class="n" style="color:${m.dir==='down'?'#c8102e':'#111'}">${m.value}</div><div class="k">${m.label}</div></div>`).join('');
+ const it=d.news.items||[], m0=(d.la.marquee||[])[0];
+ let cells='';
+ cells+=`<div class="cell big"><span class="c" style="background:${sectionColor(it[0].cat)}">${esc(it[0].cat)}</span><h3>${esc(it[0].title)}</h3></div>`;
+ cells+=`<div class="cell val"><span class="c" style="background:#111">Top LA Asset</span><div class="big">${shortUsd(m0.assessed_total)}</div><h3 style="font-size:14px">${esc(m0.address)}</h3></div>`;
+ it.slice(1,10).forEach(n=>{cells+=`<div class="cell"><span class="c" style="background:${sectionColor(n.cat)}">${esc(n.cat)}</span><h3>${esc(n.title)}</h3></div>`;});
+ cells.innerHTML;document.getElementById('cells').innerHTML=cells;
+ document.getElementById('firms').innerHTML=(d.la.firms||[]).slice(0,6).map(f=>`<div class="b">${esc(f.name)}<b>${(f.agent_count||0).toLocaleString()} agents</b></div>`).join('');
+ document.getElementById('ledger').innerHTML=(d.la.recent||[]).slice(0,8).map(r=>`<div class="r"><span>${dstr(r.recording_date)} · ${esc(r.type_label).toUpperCase()}</span><span>${r.assessed_total?shortUsd(r.assessed_total):'RECORDED'}</span></div>`).join('');
+}});
+</script></body></html>
diff --git a/mockups/L04.html b/mockups/L04.html
new file mode 100644
index 00000000..6d69878a
--- /dev/null
+++ b/mockups/L04.html
@@ -0,0 +1,50 @@
+<!doctype html><html lang="en"><head>
+<meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
+<title>L04 · Card Masonry — RENTV redesign</title>
+<link rel="stylesheet" href="assets/base.css">
+<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700;800&display=swap" rel="stylesheet">
+<style>
+ body{background:#eef0f3;color:#14171a;font-family:'Inter',system-ui,sans-serif}
+ .wrap{max-width:1280px;margin:0 auto;padding:18px}
+ .chips{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:16px}
+ .chip{background:#fff;border:1px solid #dde1e6;border-radius:20px;padding:8px 15px;font-size:13px;font-weight:700;cursor:pointer}
+ .chip.on{background:#14171a;color:#fff;border-color:#14171a}
+ .feat{background:#14171a;color:#fff;border-radius:16px;overflow:hidden;display:grid;grid-template-columns:1.4fr 1fr;margin-bottom:16px}
+ .feat .im{min-height:280px;display:block}.feat .b{padding:26px;display:flex;flex-direction:column;justify-content:center}
+ .feat .tag{color:#ff6b6b;font-weight:800;font-size:12px;letter-spacing:.08em;text-transform:uppercase}
+ .feat h1{font-size:32px;line-height:1.12;margin:8px 0 12px}
+ .masonry{columns:4;column-gap:16px}
+ .card{break-inside:avoid;margin-bottom:16px;background:#fff;border-radius:14px;overflow:hidden;box-shadow:0 2px 10px rgba(20,23,26,.06)}
+ .card .im{display:block}.card .b{padding:13px 14px}
+ .card .tag{font-size:10px;font-weight:800;letter-spacing:.05em;text-transform:uppercase}
+ .card h3{font-size:15px;line-height:1.22;margin:5px 0 0}
+ .card.asset .v{font-size:24px;font-weight:800}.card.asset .a{font-size:12px;color:#5b636b;margin-top:2px}
+ .card.firm{padding:14px}.card.firm .fn{font-weight:800}.card.firm .fa{color:#c8102e;font-weight:800;margin-top:4px}
+ .sh{font-size:13px;text-transform:uppercase;letter-spacing:.1em;font-weight:800;color:#5b636b;margin:22px 0 12px}
+ @media(max-width:1100px){.masonry{columns:3}}@media(max-width:820px){.masonry{columns:2}.feat{grid-template-columns:1fr}}@media(max-width:520px){.masonry{columns:1}}
+</style></head>
+<body>
+<div class="wrap">
+ <div class="chips" data-eln id="chips"></div>
+ <div class="feat" data-eln id="feat"></div>
+ <div class="sh" data-eln>Latest across the market</div>
+ <div class="masonry" data-eln id="masonry"></div>
+ <div class="sh">Marquee LA assets</div>
+ <div class="masonry" data-eln id="assets"></div>
+ <div class="sh">Top brokerages</div>
+ <div class="masonry" data-eln id="firms"></div>
+</div>
+<script src="assets/chrome.js"></script>
+<script>
+RENTV.mount({layout:'L04', render(d){
+ const {esc,shortUsd,imgTag,sectionColor}=RENTV;
+ const cats=['All',...RENTV.NAV.Sections.map(s=>s[0])];
+ chips.innerHTML=cats.map((c,i)=>`<span class="chip${i===0?' on':''}">${c}</span>`).join('');
+ chips.querySelectorAll('.chip').forEach(c=>c.onclick=()=>{chips.querySelectorAll('.chip').forEach(x=>x.classList.remove('on'));c.classList.add('on');});
+ const it=d.news.items||[], l=it[0];
+ feat.innerHTML=`${imgTag(l.image,'im',l.id)}<div class="b"><span class="tag">${esc(l.cat)} · Lead</span><h1>${esc(l.title)}</h1><a href="${esc(l.url)}" style="color:#fff;font-weight:800;text-decoration:underline">Read →</a></div>`;
+ masonry.innerHTML=it.slice(1,17).map(n=>`<div class="card">${imgTag(n.image,'im',n.id)}<div class="b"><span class="tag" style="color:${sectionColor(n.cat)}">${esc(n.cat)}</span><h3>${esc(n.title)}</h3></div></div>`).join('');
+ document.getElementById('assets').innerHTML=(d.la.marquee||[]).slice(0,8).map(m=>`<div class="card asset">${imgTag(null,'im',m.ain)}<div class="b"><span class="tag" style="color:#c8102e">${esc(m.type_label)}</span><div class="v">${shortUsd(m.assessed_total)}</div><div class="a">${esc(m.address)}, ${esc(m.city)}</div></div></div>`).join('');
+ document.getElementById('firms').innerHTML=(d.la.firms||[]).slice(0,9).map(f=>`<div class="card firm"><div class="fn">${esc(f.name)}</div><div class="fa">${(f.agent_count||0).toLocaleString()} licensed agents</div></div>`).join('');
+}});
+</script></body></html>
diff --git a/mockups/L05.html b/mockups/L05.html
new file mode 100644
index 00000000..0e9f96c4
--- /dev/null
+++ b/mockups/L05.html
@@ -0,0 +1,53 @@
+<!doctype html><html lang="en"><head>
+<meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
+<title>L05 · Split-Screen — RENTV redesign</title>
+<link rel="stylesheet" href="assets/base.css">
+<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700;800&display=swap" rel="stylesheet">
+<style>
+ body{background:#fff;color:#14171a;font-family:'Inter',system-ui,sans-serif}
+ .split{display:grid;grid-template-columns:1fr 1fr;min-height:calc(100vh - 52px)}
+ .left{padding:24px 26px;border-right:1px solid #e4e7ea;overflow:auto}
+ .right{background:#0d1014;color:#e7ecf1;padding:24px 26px;position:sticky;top:52px;height:calc(100vh - 52px);overflow:auto}
+ .sh{font-size:12px;text-transform:uppercase;letter-spacing:.14em;font-weight:800;color:#8b939b;margin-bottom:14px}
+ .left .sh{color:#5b636b}
+ .story{display:flex;gap:14px;padding:14px 0;border-bottom:1px solid #eef0f2}
+ .story .im{width:120px;height:80px;border-radius:8px;flex:none;display:block}
+ .story .tag{font-size:10px;font-weight:800;letter-spacing:.05em;text-transform:uppercase}
+ .story h3{font-size:16px;line-height:1.24;margin:5px 0 0}
+ .marq{background:#12161c;border:1px solid #232a32;border-radius:12px;padding:20px;margin-bottom:16px}
+ .marq .v{font-size:40px;font-weight:800;letter-spacing:-1px}
+ .marq .a{font-size:15px;font-weight:600;margin-top:4px}.marq .brief{font-size:13px;color:#aab2ba;line-height:1.55;margin-top:10px}
+ .rec .r{display:flex;justify-content:space-between;padding:9px 0;border-bottom:1px solid #1c232b;font-size:13px}
+ .rec .r span:last-child{color:#37d67a;font-weight:700}
+ .firmrow{display:flex;flex-wrap:wrap;gap:8px;margin-top:8px}
+ .firmrow .f{background:#12161c;border:1px solid #232a32;border-radius:8px;padding:8px 10px;font-size:12px}.firmrow .f b{color:#ff6b6b}
+ @media(max-width:860px){.split{grid-template-columns:1fr}.right{position:static;height:auto}}
+</style></head>
+<body>
+<div class="split">
+ <div class="left" data-eln>
+ <div class="sh" data-eln id="lh">Live newsroom</div>
+ <div id="feed" data-eln></div>
+ </div>
+ <div class="right">
+ <div class="sh">LA Commercial · property intelligence</div>
+ <div class="marq" data-eln id="marq"></div>
+ <div class="sh" data-eln>Recent recorded activity</div>
+ <div class="rec" data-eln id="rec"></div>
+ <div class="sh" style="margin-top:20px" data-eln>Largest brokerages</div>
+ <div class="firmrow" data-eln id="firms"></div>
+ </div>
+</div>
+<script src="assets/chrome.js"></script>
+<script>
+RENTV.mount({dark:false, layout:'L05', render(d){
+ const {esc,shortUsd,dstr,imgTag,sectionColor}=RENTV;
+ const it=d.news.items||[];
+ lh.textContent='Live newsroom · '+it.length+' stories';
+ feed.innerHTML=it.slice(0,14).map(n=>`<div class="story">${imgTag(n.image,'im',n.id)}<div><span class="tag" style="color:${sectionColor(n.cat)}">${esc(n.cat)}</span><h3>${esc(n.title)}</h3></div></div>`).join('');
+ const m=(d.la.marquee||[])[0];
+ marq.innerHTML=`<div style="font-size:11px;letter-spacing:.1em;text-transform:uppercase;color:#8b939b">${esc(m.type_label)} · assessed</div><div class="v">${shortUsd(m.assessed_total)}</div><div class="a">${esc(m.address)}, ${esc(m.city)}</div><div class="brief">${esc(m.brief)}</div>`;
+ document.getElementById('rec').innerHTML=(d.la.recent||[]).slice(0,7).map(r=>`<div class="r"><span>${dstr(r.recording_date)} · ${esc(r.type_label)}</span><span>${r.assessed_total?shortUsd(r.assessed_total):'—'}</span></div>`).join('');
+ document.getElementById('firms').innerHTML=(d.la.firms||[]).slice(0,8).map(f=>`<div class="f">${esc(f.name)} <b>${(f.agent_count||0).toLocaleString()}</b></div>`).join('');
+}});
+</script></body></html>
diff --git a/mockups/L06.html b/mockups/L06.html
new file mode 100644
index 00000000..03e12c59
--- /dev/null
+++ b/mockups/L06.html
@@ -0,0 +1,55 @@
+<!doctype html><html lang="en"><head>
+<meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
+<title>L06 · Broadcast — RENTV redesign</title>
+<link rel="stylesheet" href="assets/base.css">
+<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700;800;900&display=swap" rel="stylesheet">
+<style>
+ body{background:#0a0d11;color:#e7ecf1;font-family:'Inter',system-ui,sans-serif}
+ .tape{background:#c8102e;color:#fff;overflow:hidden;white-space:nowrap;border-bottom:2px solid #000}
+ .tape .run{display:inline-block;padding:9px 0;animation:mar 40s linear infinite;font-weight:800;font-size:13px;letter-spacing:.02em}
+ .tape .run span{margin:0 26px}.tape .run b{background:#000;padding:2px 7px;border-radius:3px;margin-right:8px;font-size:11px}
+ @keyframes mar{from{transform:translateX(0)}to{transform:translateX(-50%)}}
+ .wrap{max-width:1280px;margin:0 auto;padding:16px}
+ .onair{position:relative;border-radius:14px;overflow:hidden;min-height:340px;display:flex;align-items:flex-end;margin-bottom:14px}
+ .onair .bg{position:absolute;inset:0;display:block}
+ .onair .scrim{position:absolute;inset:0;background:linear-gradient(0deg,rgba(0,0,0,.85),transparent 60%)}
+ .onair .b{position:relative;padding:26px}
+ .onair .live{display:inline-flex;align-items:center;gap:7px;background:#c8102e;font-weight:900;font-size:12px;letter-spacing:.1em;padding:5px 12px;border-radius:4px}
+ .onair .live .dot{width:8px;height:8px;border-radius:50%;background:#fff;animation:bl 1s infinite}@keyframes bl{50%{opacity:.3}}
+ .onair h1{font-size:34px;font-weight:900;line-height:1.08;margin:12px 0 0;max-width:760px}
+ .breaking{background:#12161c;border:1px solid #232a32;border-left:5px solid #c8102e;border-radius:8px;padding:12px 16px;margin-bottom:14px;font-weight:700}
+ .cols{display:grid;grid-template-columns:repeat(3,1fr);gap:14px}
+ .col{background:#12161c;border:1px solid #232a32;border-radius:12px;padding:16px}
+ .col h4{font-size:12px;letter-spacing:.1em;text-transform:uppercase;color:#8b939b;margin:0 0 10px;border-bottom:1px solid #232a32;padding-bottom:8px}
+ .col a{display:block;color:#dbe1e7;text-decoration:none;padding:8px 0;border-bottom:1px solid #171d24;font-size:14px;line-height:1.3}
+ .laband{display:grid;grid-template-columns:repeat(4,1fr);gap:12px;margin-top:14px}
+ .la{background:#12161c;border:1px solid #232a32;border-radius:10px;padding:14px}.la .v{font-size:22px;font-weight:800}.la .a{font-size:11px;color:#8b939b;margin-top:3px}
+ .ribbon{display:flex;flex-wrap:wrap;gap:8px;margin-top:14px}.ribbon .f{background:#12161c;border:1px solid #232a32;border-radius:20px;padding:7px 13px;font-size:12px;font-weight:700}.ribbon .f b{color:#ff6b6b}
+ @media(max-width:860px){.cols{grid-template-columns:1fr}.laband{grid-template-columns:1fr 1fr}}
+</style></head>
+<body>
+<div class="tape" data-eln id="tape"></div>
+<div class="wrap">
+ <div class="onair" data-eln id="onair"></div>
+ <div class="breaking" data-eln id="breaking"></div>
+ <div class="cols" data-eln id="cols"></div>
+ <div class="laband" data-eln id="laband"></div>
+ <div class="ribbon" data-eln id="firms"></div>
+</div>
+<script src="assets/chrome.js"></script>
+<script>
+RENTV.mount({dark:true, layout:'L06', render(d){
+ const {esc,shortUsd,imgTag}=RENTV;
+ const it=d.news.items||[];
+ const run=it.slice(0,14).map(n=>`<span><b>${esc(n.cat)}</b>${esc(n.title)}</span>`).join('');
+ tape.innerHTML=`<div class="run">${run}${run}</div>`;
+ const v=(d.videos.items||[])[0]||{}, l=it[0];
+ onair.innerHTML=`${imgTag(v.thumb||l.image,'bg',v.id||l.id)}<div class="scrim"></div><div class="b"><span class="live"><span class="dot"></span>ON AIR · THE REVIEW</span><h1>${esc(v.title||l.title)}</h1></div>`;
+ breaking.innerHTML=`🔴 BREAKING · <span style="color:#ff6b6b">${esc(l.cat)}</span> — ${esc(l.title)}`;
+ const bySect={}; it.forEach(n=>{(bySect[n.cat]=bySect[n.cat]||[]).push(n);});
+ const top3=Object.keys(bySect).slice(0,3);
+ cols.innerHTML=top3.map(s=>`<div class="col"><h4>${esc(s)}</h4>${bySect[s].slice(0,5).map(n=>`<a href="${esc(n.url)}">${esc(n.title)}</a>`).join('')}</div>`).join('');
+ laband.innerHTML=(d.la.marquee||[]).slice(0,4).map(m=>`<div class="la"><div class="v">${shortUsd(m.assessed_total)}</div><div class="a">${esc(m.address)}</div></div>`).join('');
+ document.getElementById('firms').innerHTML=(d.la.firms||[]).slice(0,8).map(f=>`<div class="f">${esc(f.name)} <b>${(f.agent_count||0).toLocaleString()}</b></div>`).join('');
+}});
+</script></body></html>
diff --git a/mockups/L07.html b/mockups/L07.html
new file mode 100644
index 00000000..d683f561
--- /dev/null
+++ b/mockups/L07.html
@@ -0,0 +1,59 @@
+<!doctype html><html lang="en"><head>
+<meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
+<title>L07 · Map-Anchored — RENTV redesign</title>
+<link rel="stylesheet" href="assets/base.css">
+<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700;800&display=swap" rel="stylesheet">
+<style>
+ body{background:#f6f7f9;color:#14171a;font-family:'Inter',system-ui,sans-serif}
+ .wrap{max-width:1300px;margin:0 auto;padding:16px}
+ .hero{display:grid;grid-template-columns:1.6fr 1fr;gap:16px;margin-bottom:18px}
+ .map{position:relative;background:radial-gradient(circle at 40% 40%,#1b2430,#0d1218);border-radius:16px;min-height:420px;overflow:hidden;border:1px solid #cfd4da}
+ .map .grid{position:absolute;inset:0;background-image:linear-gradient(rgba(255,255,255,.05) 1px,transparent 1px),linear-gradient(90deg,rgba(255,255,255,.05) 1px,transparent 1px);background-size:44px 44px}
+ .map .title{position:absolute;top:16px;left:18px;color:#eaf0f6;z-index:2}
+ .map .title b{font-size:14px;letter-spacing:.14em;text-transform:uppercase}.map .title span{display:block;font-size:12px;color:#93a0ad;margin-top:3px}
+ .pin{position:absolute;transform:translate(-50%,-100%);cursor:pointer;z-index:3;text-align:center}
+ .pin .dot{width:16px;height:16px;border-radius:50% 50% 50% 0;background:#c8102e;transform:rotate(-45deg);margin:0 auto;box-shadow:0 3px 8px rgba(0,0,0,.4);border:2px solid #fff}
+ .pin .lab{background:#fff;border-radius:6px;font-size:11px;font-weight:800;padding:3px 7px;margin-top:6px;white-space:nowrap;box-shadow:0 2px 6px rgba(0,0,0,.3)}
+ .pin.sel .dot{background:#0f8a4d}
+ .detail{background:#fff;border:1px solid #e4e7ea;border-radius:16px;padding:22px;display:flex;flex-direction:column;justify-content:center}
+ .detail .k{font-size:11px;letter-spacing:.1em;text-transform:uppercase;color:#c8102e;font-weight:800}
+ .detail .v{font-size:38px;font-weight:800;letter-spacing:-1px;margin:4px 0}
+ .detail .a{font-size:15px;font-weight:600}.detail .brief{font-size:13px;color:#5b636b;line-height:1.55;margin-top:10px}
+ .detail .meta{display:flex;gap:14px;margin-top:14px;flex-wrap:wrap;font-size:12px}.detail .meta b{display:block;font-size:16px}
+ .sh{font-size:12px;letter-spacing:.12em;text-transform:uppercase;font-weight:800;color:#5b636b;margin:20px 0 12px}
+ .wrapnews{display:grid;grid-template-columns:repeat(4,1fr);gap:14px}
+ .card{background:#fff;border:1px solid #e4e7ea;border-radius:12px;overflow:hidden}.card .im{height:120px;display:block}.card .b{padding:12px}.card .tag{font-size:10px;font-weight:800;text-transform:uppercase}.card h3{font-size:14px;line-height:1.25;margin:5px 0 0}
+ .two{display:grid;grid-template-columns:1fr 1fr;gap:16px;margin-top:8px}
+ .list .r{display:flex;justify-content:space-between;background:#fff;border:1px solid #e4e7ea;border-radius:8px;padding:10px 14px;margin-bottom:8px;font-size:13px}.list .r b{color:#0f8a4d}
+ @media(max-width:900px){.hero,.wrapnews,.two{grid-template-columns:1fr}}
+</style></head>
+<body>
+<div class="wrap">
+ <div class="hero">
+ <div class="map" data-eln id="map"><div class="grid"></div><div class="title"><b>LA Commercial Map</b><span>Marquee assets by assessed value · click a pin</span></div></div>
+ <div class="detail" data-eln id="detail"></div>
+ </div>
+ <div class="sh" data-eln>Regional news wrap</div>
+ <div class="wrapnews" data-eln id="news"></div>
+ <div class="two">
+ <div><div class="sh" data-eln>Recent recorded activity</div><div class="list" data-eln id="rec"></div></div>
+ <div><div class="sh" data-eln>Largest brokerages</div><div class="list" data-eln id="firms"></div></div>
+ </div>
+</div>
+<script src="assets/chrome.js"></script>
+<script>
+RENTV.mount({layout:'L07', render(d){
+ const {esc,shortUsd,dstr,imgTag,sectionColor}=RENTV;
+ const mq=(d.la.marquee||[]).slice(0,8);
+ // scatter pins deterministically across the map area
+ const pins=mq.map((m,i)=>{const x=12+((i*37)%76), y=20+((i*29)%60);return {m,x,y};});
+ map.insertAdjacentHTML('beforeend',pins.map((p,i)=>`<div class="pin${i===0?' sel':''}" data-i="${i}" style="left:${p.x}%;top:${p.y}%"><div class="dot"></div><div class="lab">${RENTV.shortUsd(p.m.assessed_total)}</div></div>`).join(''));
+ function showDetail(m){detail.innerHTML=`<div class="k">${esc(m.type_label)} · ${esc(m.use_desc||'')}</div><div class="v">${shortUsd(m.assessed_total)}</div><div class="a">${esc(m.address)}, ${esc(m.city)} ${esc(m.zip||'')}</div><div class="brief">${esc(m.brief)}</div><div class="meta"><span>SqFt<b>${(m.sqft||0).toLocaleString()}</b></span><span>Built<b>${m.year_built||'—'}</b></span><span>Recorded<b>${dstr(m.recording_date)}</b></span></div>`;}
+ showDetail(mq[0]);
+ map.querySelectorAll('.pin').forEach(p=>p.onclick=()=>{map.querySelectorAll('.pin').forEach(x=>x.classList.remove('sel'));p.classList.add('sel');showDetail(mq[+p.dataset.i]);});
+ const it=d.news.items||[];
+ document.getElementById('news').innerHTML=it.slice(0,8).map(n=>`<div class="card">${imgTag(n.image,'im',n.id)}<div class="b"><span class="tag" style="color:${sectionColor(n.cat)}">${esc(n.cat)}</span><h3>${esc(n.title)}</h3></div></div>`).join('');
+ document.getElementById('rec').innerHTML=(d.la.recent||[]).slice(0,6).map(r=>`<div class="r"><span>${dstr(r.recording_date)} · ${esc(r.type_label)}</span><b>${r.assessed_total?shortUsd(r.assessed_total):'recorded'}</b></div>`).join('');
+ document.getElementById('firms').innerHTML=(d.la.firms||[]).slice(0,6).map(f=>`<div class="r"><span>${esc(f.name)}</span><b>${(f.agent_count||0).toLocaleString()}</b></div>`).join('');
+}});
+</script></body></html>
diff --git a/mockups/L08.html b/mockups/L08.html
new file mode 100644
index 00000000..e9c67781
--- /dev/null
+++ b/mockups/L08.html
@@ -0,0 +1,58 @@
+<!doctype html><html lang="en"><head>
+<meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
+<title>L08 · Timeline Feed — RENTV redesign</title>
+<link rel="stylesheet" href="assets/base.css">
+<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700;800&display=swap" rel="stylesheet">
+<style>
+ body{background:#fbfbfc;color:#14171a;font-family:'Inter',system-ui,sans-serif}
+ .wrap{max-width:1080px;margin:0 auto;padding:22px}
+ .dh{font-size:12px;letter-spacing:.16em;text-transform:uppercase;color:#5b636b;font-weight:800;border-bottom:2px solid #14171a;padding-bottom:10px;margin-bottom:22px}
+ .layout{display:grid;grid-template-columns:1fr 300px;gap:30px}
+ .tl{position:relative;padding-left:30px}
+ .tl::before{content:"";position:absolute;left:7px;top:6px;bottom:6px;width:2px;background:#e4e7ea}
+ .ev{position:relative;padding:0 0 22px}
+ .ev::before{content:"";position:absolute;left:-30px;top:4px;width:14px;height:14px;border-radius:50%;background:#fff;border:3px solid #c8102e}
+ .ev.rec::before{border-color:#0f8a4d}
+ .ev .t{font-size:11px;color:#8b939b;font-weight:700}
+ .ev .tag{font-size:10px;font-weight:800;text-transform:uppercase;letter-spacing:.05em}
+ .ev h3{font-size:17px;line-height:1.25;margin:4px 0 0}
+ .ev .brief{font-size:13px;color:#5b636b;margin-top:5px;line-height:1.5}
+ .ev.asset{background:#14171a;color:#fff;border-radius:12px;padding:14px 16px;margin-left:-4px}
+ .ev.asset .v{font-size:26px;font-weight:800}
+ aside .sh{font-size:11px;letter-spacing:.12em;text-transform:uppercase;font-weight:800;color:#5b636b;margin:0 0 12px}
+ aside .card{background:#fff;border:1px solid #e4e7ea;border-radius:12px;overflow:hidden;margin-bottom:12px}
+ aside .card .im{height:110px;display:block}aside .card .b{padding:11px 13px}aside .card h4{font-size:13px;margin:4px 0 0;line-height:1.25}
+ aside .firm{display:flex;justify-content:space-between;font-size:13px;padding:8px 0;border-bottom:1px solid #eef0f2}aside .firm b{color:#c8102e}
+ @media(max-width:860px){.layout{grid-template-columns:1fr}}
+</style></head>
+<body>
+<div class="wrap">
+ <div class="dh" data-eln id="dh">Today in commercial real estate</div>
+ <div class="layout">
+ <div class="tl" data-eln id="tl"></div>
+ <aside>
+ <div class="sh" data-eln>The REview</div>
+ <div data-eln id="rev"></div>
+ <div class="sh" style="margin-top:20px" data-eln>Top brokerages</div>
+ <div data-eln id="firms"></div>
+ </aside>
+ </div>
+</div>
+<script src="assets/chrome.js"></script>
+<script>
+RENTV.mount({layout:'L08', render(d){
+ const {esc,shortUsd,dstr,imgTag,sectionColor}=RENTV;
+ dh.textContent='Timeline · '+new Date(d.news.fetched_at||Date.now()).toLocaleDateString(undefined,{weekday:'long',month:'long',day:'numeric'})+' · '+((d.news.items||[]).length)+' updates';
+ const it=d.news.items||[], rec=d.la.recent||[], mq=d.la.marquee||[];
+ // interleave: news, occasionally an asset callout + a recorded-activity event
+ let html='', ai=0, ri=0;
+ it.slice(0,16).forEach((n,i)=>{
+ html+=`<div class="ev"><div class="t">LIVE · rentv.com</div><span class="tag" style="color:${sectionColor(n.cat)}">${esc(n.cat)}</span><h3>${esc(n.title)}</h3></div>`;
+ if(i===2&&mq[ai]){const m=mq[ai++];html+=`<div class="ev asset"><div class="t" style="color:#9aa1a8">MARQUEE ASSET · LA COUNTY</div><div class="v">${shortUsd(m.assessed_total)}</div><h3 style="font-size:15px;color:#fff">${esc(m.address)}, ${esc(m.city)}</h3><div class="brief" style="color:#b9bfc5">${esc(m.brief).slice(0,150)}</div></div>`;}
+ if(i%4===3&&rec[ri]){const r=rec[ri++];html+=`<div class="ev rec"><div class="t">${dstr(r.recording_date)} · RECORDED TRANSFER</div><h3 style="font-size:15px">${esc(r.type_label)} property · ${esc(r.use_desc||'—')}</h3></div>`;}
+ });
+ tl.innerHTML=html;
+ document.getElementById('rev').innerHTML=(d.videos.items||[]).slice(0,3).map(v=>`<div class="card">${imgTag(v.thumb,'im',v.id)}<div class="b"><h4>${esc(v.title)}</h4></div></div>`).join('');
+ document.getElementById('firms').innerHTML=(d.la.firms||[]).slice(0,7).map(f=>`<div class="firm"><span>${esc(f.name)}</span><b>${(f.agent_count||0).toLocaleString()}</b></div>`).join('');
+}});
+</script></body></html>
diff --git a/mockups/L09.html b/mockups/L09.html
new file mode 100644
index 00000000..44f4803b
--- /dev/null
+++ b/mockups/L09.html
@@ -0,0 +1,56 @@
+<!doctype html><html lang="en"><head>
+<meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
+<title>L09 · Broadsheet — RENTV redesign</title>
+<link rel="stylesheet" href="assets/base.css">
+<link href="https://fonts.googleapis.com/css2?family=Playfair+Display:wght@700;900&family=Spectral:ital,wght@0,400;0,600;1,400&display=swap" rel="stylesheet">
+<style>
+ body{background:#f3efe6;color:#1a1a17;font-family:'Spectral',Georgia,serif}
+ .paper{max-width:1180px;margin:0 auto;padding:22px 26px;background:#fbf8f1;box-shadow:0 0 30px rgba(0,0,0,.08)}
+ .flag{text-align:center;border-bottom:3px double #1a1a17;padding-bottom:10px}
+ .flag h1{font-family:'Playfair Display',serif;font-weight:900;font-size:64px;letter-spacing:-1px;margin:0}.flag h1 span{color:#c8102e}
+ .flag .line{display:flex;justify-content:space-between;font-size:11px;letter-spacing:.08em;text-transform:uppercase;margin-top:8px;border-top:1px solid #1a1a17;padding-top:6px}
+ .lead{display:grid;grid-template-columns:2fr 1fr;gap:24px;border-bottom:2px solid #1a1a17;padding:20px 0}
+ .lead h2{font-family:'Playfair Display',serif;font-size:38px;line-height:1.05;margin:0 0 10px}
+ .lead .deck{font-size:16px;font-style:italic;color:#44413a}
+ .lead .im{height:260px;display:block;border:1px solid #1a1a17}
+ .lead p{font-size:14px;line-height:1.6;margin-top:10px;column-count:2;column-gap:18px}
+ .cols{columns:4;column-gap:20px;padding:18px 0;border-bottom:2px solid #1a1a17}
+ .cols .a{break-inside:avoid;margin-bottom:14px;display:block;color:#1a1a17;text-decoration:none;border-bottom:1px solid #d8d2c4;padding-bottom:10px}
+ .cols .k{font-size:9px;text-transform:uppercase;letter-spacing:.1em;color:#c8102e;font-weight:600}
+ .cols h4{font-family:'Playfair Display',serif;font-size:16px;line-height:1.15;margin:3px 0}
+ .lower{display:grid;grid-template-columns:1.3fr 1fr 1fr;gap:22px;padding-top:18px}
+ .pod{border:2px solid #1a1a17;padding:14px}
+ .pod .k{font-size:10px;text-transform:uppercase;letter-spacing:.12em;font-weight:600}
+ .pod .v{font-family:'Playfair Display',serif;font-size:34px;font-weight:900}
+ .pod .a{font-size:13px;font-weight:600}.pod .brief{font-size:12px;line-height:1.5;margin-top:6px;color:#44413a}
+ h3.rule{font-size:12px;text-transform:uppercase;letter-spacing:.14em;border-bottom:1px solid #1a1a17;padding-bottom:5px;margin:0 0 10px;font-family:'Spectral'}
+ .agate{font-size:12px;line-height:1.7}.agate .r{display:flex;justify-content:space-between;border-bottom:1px dotted #b8b1a2}
+ .stand .s{display:flex;justify-content:space-between;font-size:13px;padding:5px 0;border-bottom:1px solid #e2dccf}.stand b{color:#c8102e}
+ .colophon{text-align:center;border-top:3px double #1a1a17;margin-top:18px;padding-top:10px;font-size:11px;letter-spacing:.06em;text-transform:uppercase}
+ @media(max-width:900px){.lead,.lower{grid-template-columns:1fr}.cols{columns:2}.flag h1{font-size:40px}.lead p{column-count:1}}
+</style></head>
+<body>
+<div class="paper">
+ <div class="flag" data-eln><h1>The REN<span>TV</span> Daily</h1><div class="line" id="flagline"><span>Commercial Real Estate · Western U.S.</span><span>Since 1998</span><span id="fdate"></span></div></div>
+ <div class="lead" data-eln id="lead"></div>
+ <div class="cols" data-eln id="cols"></div>
+ <div class="lower">
+ <div><h3 class="rule" data-eln>Property of the Day</h3><div class="pod" data-eln id="pod"></div></div>
+ <div><h3 class="rule" data-eln>Recorded Activity</h3><div class="agate" data-eln id="agate"></div></div>
+ <div><h3 class="rule" data-eln>Brokerage Standings</h3><div class="stand" data-eln id="stand"></div></div>
+ </div>
+ <div class="colophon" data-eln>The RENTV Daily · a live edition · data from rentv.com, LA County Assessor & CA DRE</div>
+</div>
+<script src="assets/chrome.js"></script>
+<script>
+RENTV.mount({layout:'L09', render(d){
+ const {esc,shortUsd,dstr}=RENTV;
+ fdate.textContent=new Date(d.news.fetched_at||Date.now()).toLocaleDateString(undefined,{weekday:'long',year:'numeric',month:'long',day:'numeric'});
+ const it=d.news.items||[], l=it[0], m=(d.la.marquee||[])[0];
+ lead.innerHTML=`<div><span class="k" style="font-size:10px;text-transform:uppercase;letter-spacing:.1em;color:#c8102e;font-weight:600">${esc(l.cat)}</span><h2>${esc(l.title)}</h2><div class="deck">${esc(m.brief).slice(0,120)}</div><p>${esc(m.brief)} The regional market read continues below, with recorded activity and brokerage standings drawn from the live public record.</p></div>${RENTV.imgTag(l.image,'im',l.id)}`;
+ document.getElementById('cols').innerHTML=it.slice(1,13).map(n=>`<a class="a" href="${esc(n.url)}"><span class="k">${esc(n.cat)}</span><h4>${esc(n.title)}</h4></a>`).join('');
+ document.getElementById('pod').innerHTML=`<div class="k">${esc(m.type_label)} · Assessed</div><div class="v">${shortUsd(m.assessed_total)}</div><div class="a">${esc(m.address)}, ${esc(m.city)}</div><div class="brief">${esc(m.brief)}</div>`;
+ document.getElementById('agate').innerHTML=(d.la.recent||[]).slice(0,9).map(r=>`<div class="r"><span>${dstr(r.recording_date)} ${esc(r.type_label)}</span><span>${r.assessed_total?shortUsd(r.assessed_total):'—'}</span></div>`).join('');
+ document.getElementById('stand').innerHTML=(d.la.firms||[]).slice(0,8).map((f,i)=>`<div class="s"><span>${i+1}. ${esc(f.name)}</span><b>${(f.agent_count||0).toLocaleString()}</b></div>`).join('');
+}});
+</script></body></html>
diff --git a/mockups/L10.html b/mockups/L10.html
new file mode 100644
index 00000000..1acc3c88
--- /dev/null
+++ b/mockups/L10.html
@@ -0,0 +1,53 @@
+<!doctype html><html lang="en"><head>
+<meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
+<title>L10 · Swiss Index — RENTV redesign</title>
+<link rel="stylesheet" href="assets/base.css">
+<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
+<style>
+ body{background:#fff;color:#111;font-family:'Inter',system-ui,sans-serif;font-size:13px}
+ .wrap{max-width:1120px;margin:0 auto;padding:26px 24px}
+ .hl{display:flex;justify-content:space-between;align-items:baseline;border-bottom:1px solid #111;padding-bottom:8px}
+ .hl b{font-size:13px;letter-spacing:.02em}.hl span{font-size:11px;color:#8b8b8b;letter-spacing:.06em;text-transform:uppercase}
+ .mkt{display:flex;gap:26px;flex-wrap:wrap;padding:12px 0;border-bottom:1px solid #e2e2e2;font-variant-numeric:tabular-nums}
+ .mkt .m span{color:#8b8b8b;font-size:10px;text-transform:uppercase;letter-spacing:.08em;display:block}
+ .mkt .m b{font-size:16px;font-weight:600}.mkt .m b.dn{color:#c8102e}
+ .sh{font-size:10px;letter-spacing:.16em;text-transform:uppercase;color:#8b8b8b;margin:26px 0 8px}
+ .index a{display:grid;grid-template-columns:34px 1fr 130px;gap:14px;padding:9px 0;border-bottom:1px solid #ededed;color:#111;text-decoration:none;align-items:baseline}
+ .index a:hover{background:#fafafa}
+ .index .n{color:#c8102e;font-weight:600;font-variant-numeric:tabular-nums}
+ .index .t{font-weight:500;line-height:1.3}
+ .index .c{text-align:right;font-size:11px;color:#8b8b8b;text-transform:uppercase;letter-spacing:.04em}
+ .laidx .r{display:grid;grid-template-columns:34px 1fr 90px 110px;gap:14px;padding:8px 0;border-bottom:1px solid #ededed;align-items:baseline;font-variant-numeric:tabular-nums}
+ .laidx .r b{text-align:right;font-weight:600}.laidx .r .ty{font-size:11px;color:#8b8b8b}
+ .cols2{display:grid;grid-template-columns:1fr 1fr;gap:40px}
+ .firmidx .r{display:grid;grid-template-columns:34px 1fr 70px;gap:12px;padding:7px 0;border-bottom:1px solid #ededed;font-variant-numeric:tabular-nums}.firmidx .r b{text-align:right}
+ .revidx .r{display:grid;grid-template-columns:34px 1fr;gap:12px;padding:7px 0;border-bottom:1px solid #ededed}
+ @media(max-width:820px){.cols2{grid-template-columns:1fr}}
+</style></head>
+<body>
+<div class="wrap">
+ <div class="hl" data-eln><b>RENTV — Commercial Real Estate, indexed.</b><span id="idate"></span></div>
+ <div class="mkt" data-eln id="mkt"></div>
+ <div class="sh" data-eln>01 — The news, in full</div>
+ <div class="index" data-eln id="index"></div>
+ <div class="sh" data-eln>02 — LA commercial, by assessed value</div>
+ <div class="laidx" data-eln id="laidx"></div>
+ <div class="cols2">
+ <div><div class="sh" data-eln>03 — Brokerages</div><div class="firmidx" data-eln id="firms"></div></div>
+ <div><div class="sh" data-eln>04 — The REview</div><div class="revidx" data-eln id="rev"></div></div>
+ </div>
+</div>
+<script src="assets/chrome.js"></script>
+<script>
+RENTV.mount({layout:'L10', render(d){
+ const {esc,shortUsd,dstr}=RENTV;
+ idate.textContent=new Date(d.news.fetched_at||Date.now()).toLocaleDateString(undefined,{year:'numeric',month:'short',day:'numeric'});
+ const t=d.ticker||{};
+ mkt.innerHTML=Object.values(t).filter(m=>m&&m.label).map(m=>`<div class="m"><span>${esc(m.label)}</span><b class="${m.dir==='down'?'dn':''}">${esc(m.value)}${m.change_pct!=null?` ${m.change_pct>0?'+':''}${m.change_pct}%`:''}</b></div>`).join('');
+ const it=d.news.items||[];
+ index.innerHTML=it.map((n,i)=>`<a href="${esc(n.url)}"><span class="n">${String(i+1).padStart(2,'0')}</span><span class="t">${esc(n.title)}</span><span class="c">${esc(n.cat)}</span></a>`).join('');
+ document.getElementById('laidx').innerHTML=(d.la.marquee||[]).slice(0,10).map((m,i)=>`<div class="r"><span class="n" style="color:#c8102e;font-weight:600">${String(i+1).padStart(2,'0')}</span><span>${esc(m.address)}, ${esc(m.city)}<br><span class="ty">${esc(m.use_desc||m.type_label)}</span></span><span class="ty">${esc(m.type_label)}</span><b>${shortUsd(m.assessed_total)}</b></div>`).join('');
+ document.getElementById('firms').innerHTML=(d.la.firms||[]).slice(0,8).map((f,i)=>`<div class="r"><span class="n" style="color:#c8102e">${String(i+1).padStart(2,'0')}</span><span>${esc(f.name)}</span><b>${(f.agent_count||0).toLocaleString()}</b></div>`).join('');
+ document.getElementById('rev').innerHTML=(d.videos.items||[]).slice(0,6).map((v,i)=>`<div class="r"><span class="n" style="color:#c8102e">${String(i+1).padStart(2,'0')}</span><span>${esc(v.title)}</span></div>`).join('');
+}});
+</script></body></html>
diff --git a/mockups/assets/base.css b/mockups/assets/base.css
new file mode 100644
index 00000000..8769ca31
--- /dev/null
+++ b/mockups/assets/base.css
@@ -0,0 +1,82 @@
+/* RENTV redesign — SHARED chrome + numbering + pick-tray.
+ Every layout (L01..L10) includes this. It owns ONLY the pieces Steve locked
+ globally: hamburger (upper-left, all tabs+data), user icon (upper-right, sign-in),
+ the element-number badges, and the "My Picks" tray. Body layout is per-file. */
+*{box-sizing:border-box}
+:root{
+ --rentv-red:#c8102e;--ink:#14171a;--sub:#5b636b;--line:#e4e7ea;--wash:#f5f6f8;
+ --up:#0f8a4d;--dn:#c8102e;--z-chrome:9000;--z-drawer:9500;--z-tray:9200;--z-eln:8000;
+}
+html,body{margin:0}
+/* ── top chrome bar (identical in all 10) ── */
+.rv-top{position:sticky;top:0;z-index:var(--z-chrome);display:flex;align-items:center;
+ justify-content:space-between;height:52px;padding:0 14px;background:#fff;border-bottom:1px solid var(--line)}
+.rv-top.dark{background:#0b0e12;border-bottom:1px solid #232a32}
+.rv-burger{display:inline-flex;align-items:center;gap:10px;border:0;background:none;cursor:pointer;
+ font:inherit;color:inherit;padding:6px 8px;border-radius:8px}
+.rv-burger:hover{background:rgba(0,0,0,.06)}
+.rv-top.dark .rv-burger:hover{background:rgba(255,255,255,.08)}
+.rv-burger svg{display:block}
+.rv-burger .lbl{font-size:12px;font-weight:800;letter-spacing:.14em;text-transform:uppercase;color:var(--sub)}
+.rv-brand{position:absolute;left:50%;transform:translateX(-50%);font-family:Georgia,'Playfair Display',serif;
+ font-weight:700;font-size:20px;letter-spacing:-.5px;color:inherit;text-decoration:none}
+.rv-brand b{color:var(--rentv-red)}
+.rv-user{width:36px;height:36px;border-radius:50%;border:1.5px solid var(--line);background:#fff;cursor:pointer;
+ display:inline-flex;align-items:center;justify-content:center;color:var(--ink)}
+.rv-user:hover{border-color:var(--rentv-red);color:var(--rentv-red)}
+.rv-top.dark .rv-user{background:#11161c;border-color:#2a323b;color:#cfd6dd}
+/* ── hamburger drawer: ALL tabs + ALL data points ── */
+.rv-scrim{position:fixed;inset:0;background:rgba(6,9,12,.5);opacity:0;pointer-events:none;
+ transition:opacity .22s;z-index:var(--z-drawer)}
+.rv-scrim.open{opacity:1;pointer-events:auto}
+.rv-drawer{position:fixed;top:0;left:0;height:100%;width:360px;max-width:88vw;background:#0b0e12;color:#e7ecf1;
+ transform:translateX(-102%);transition:transform .26s cubic-bezier(.4,0,.2,1);z-index:calc(var(--z-drawer) + 1);
+ display:flex;flex-direction:column;overflow-y:auto;box-shadow:2px 0 40px rgba(0,0,0,.4)}
+.rv-drawer.open{transform:none}
+.rv-drawer .dh{display:flex;align-items:center;justify-content:space-between;padding:16px 18px;border-bottom:1px solid #1c232b}
+.rv-drawer .dh .w{font-family:Georgia,serif;font-size:22px;font-weight:700}.rv-drawer .dh .w b{color:var(--rentv-red)}
+.rv-drawer .dh button{background:none;border:0;color:#8b949e;font-size:22px;cursor:pointer;line-height:1}
+.rv-snap{padding:12px 18px;border-bottom:1px solid #1c232b;display:grid;grid-template-columns:1fr 1fr;gap:10px}
+.rv-snap .m{background:#11161c;border:1px solid #1c232b;border-radius:8px;padding:8px 10px}
+.rv-snap .m .k{font-size:10px;letter-spacing:.08em;text-transform:uppercase;color:#7b848d}
+.rv-snap .m .v{font-size:16px;font-weight:800;margin-top:2px}
+.rv-snap .m .v.up{color:#37d67a}.rv-snap .m .v.dn{color:#ff6b6b}
+.rv-grp{padding:14px 18px 4px}
+.rv-grp .gt{font-size:10px;letter-spacing:.16em;text-transform:uppercase;color:#6b747d;margin-bottom:6px}
+.rv-drawer nav a{display:flex;align-items:center;justify-content:space-between;padding:9px 18px;color:#dbe1e7;
+ text-decoration:none;font-size:14.5px;border-left:3px solid transparent}
+.rv-drawer nav a:hover{background:#141a21;border-left-color:var(--rentv-red);color:#fff}
+.rv-drawer nav a .c{font-size:11px;color:#6b747d;background:#161c23;border-radius:20px;padding:2px 8px}
+.rv-drawer .df{margin-top:auto;padding:16px 18px;border-top:1px solid #1c232b;font-size:11px;color:#6b747d}
+/* ── element-number badges (the pick system) ── */
+[data-eln]{position:relative}
+.eln{position:absolute;top:-9px;left:-9px;z-index:var(--z-eln);min-width:34px;height:18px;padding:0 5px;
+ display:none;align-items:center;justify-content:center;gap:3px;font:700 10px/1 ui-monospace,Menlo,monospace;
+ letter-spacing:.02em;color:#fff;background:var(--rentv-red);border:1.5px solid #fff;border-radius:5px;
+ cursor:pointer;box-shadow:0 1px 4px rgba(0,0,0,.35);user-select:none;white-space:nowrap}
+body.eln-on .eln{display:inline-flex}
+body.eln-on [data-eln]{outline:1px dashed rgba(200,16,46,.35);outline-offset:2px}
+.eln.picked{background:#0f8a4d}
+.eln::before{content:"◻";font-size:10px}
+.eln.picked::before{content:"◼"}
+/* number toggle + picks tray */
+.rv-fab{position:fixed;right:16px;bottom:16px;z-index:var(--z-tray);display:flex;gap:8px;align-items:flex-end;flex-direction:column}
+.rv-fab button{border:0;border-radius:22px;padding:10px 16px;font:800 12px/1 system-ui;letter-spacing:.04em;
+ cursor:pointer;box-shadow:0 4px 16px rgba(0,0,0,.22)}
+.rv-fab .t-num{background:#14171a;color:#fff}
+.rv-fab .t-num.on{background:var(--rentv-red)}
+.rv-tray{position:fixed;left:0;right:0;bottom:0;z-index:var(--z-tray);background:#0b0e12;color:#e7ecf1;
+ border-top:2px solid var(--rentv-red);transform:translateY(102%);transition:transform .24s;padding:12px 16px}
+.rv-tray.open{transform:none}
+.rv-tray .th{display:flex;align-items:center;justify-content:space-between;margin-bottom:8px}
+.rv-tray .th b{font-size:13px;letter-spacing:.04em}
+.rv-tray .th .x{background:none;border:0;color:#8b949e;cursor:pointer;font-size:18px}
+.rv-tray .chips{display:flex;flex-wrap:wrap;gap:6px;max-height:22vh;overflow:auto}
+.rv-tray .chip{font:700 11px/1 ui-monospace,monospace;background:#11161c;border:1px solid #232a32;color:#cfd6dd;
+ border-radius:6px;padding:5px 8px;display:inline-flex;gap:6px;align-items:center}
+.rv-tray .chip button{background:none;border:0;color:#ff6b6b;cursor:pointer;font-size:13px;line-height:1}
+.rv-tray .acts{display:flex;gap:8px;margin-top:10px}
+.rv-tray .acts button{border:0;border-radius:8px;padding:8px 14px;font:800 12px system-ui;cursor:pointer}
+.rv-tray .acts .copy{background:var(--rentv-red);color:#fff}
+.rv-tray .acts .clr{background:#232a32;color:#cfd6dd}
+.rv-tray .empty{color:#6b747d;font-size:12px}
diff --git a/mockups/assets/chrome.js b/mockups/assets/chrome.js
new file mode 100644
index 00000000..517658a8
--- /dev/null
+++ b/mockups/assets/chrome.js
@@ -0,0 +1,169 @@
+/* RENTV redesign — shared chrome + numbering engine.
+ Include once per layout: <script src="assets/chrome.js"></script>
+ Then call: RENTV.mount({ dark:false, layout:'L03' })
+ - builds the top bar (hamburger upper-left = ALL tabs+data, user icon upper-right = sign-in)
+ - loads the REAL live data snapshot from ./data/*.json and exposes RENTV.data
+ - scans [data-eln] elements, injects clickable number badges, wires the "My Picks" tray */
+window.RENTV = (function () {
+ // ---- canonical navigation model (single source of truth for every drawer) ----
+ const NAV = {
+ Sections: [
+ ['Sales', '/sales'], ['Leases', '/leases'], ['Financing', '/financing'],
+ ['Development', '/development'], ['Multifamily', '/multifamily'],
+ ['Industrial', '/industrial'], ['Retail', '/retail'],
+ ],
+ Intelligence: [
+ ['Markets', '/markets'], ['LA Commercial', '/la-commercial'], ['The REview', '/review'],
+ ['CRE Talk', '/cre-talk'], ['Conferences', '/conferences'], ['Blog', '/blog'],
+ ],
+ Company: [
+ ['About', '/about'], ['Advertise', '/advertise'],
+ ['Subscribe / Newsletters', '/subscribe'], ['Versions', '/versions'],
+ ],
+ };
+ const usd = n => n == null ? '—' : '$' + Number(n).toLocaleString();
+ const shortUsd = n => { n = Number(n) || 0; return n >= 1e9 ? '$' + (n / 1e9).toFixed(2) + 'B' : n >= 1e6 ? '$' + (n / 1e6).toFixed(1) + 'M' : usd(n); };
+ const dstr = d => d ? new Date(d).toLocaleDateString(undefined, { month: 'short', day: 'numeric' }) : '—';
+
+ async function loadData() {
+ // live-first: same-origin /api/<ep> (works on the real rentv-v1 domain, browser carries auth);
+ // fall back to the bundled snapshot so the gallery also renders anywhere static.
+ const get = (api, file) => fetch('/api/' + api).then(r => { if (!r.ok) throw 0; return r.json(); })
+ .catch(() => fetch('data/' + file).then(r => r.json()).catch(() => null));
+ const [news, videos, la, ticker] = await Promise.all([
+ get('news', 'news.json'), get('videos', 'videos.json'),
+ get('la-commercial', 'la-commercial.json'), get('ticker', 'ticker.json')]);
+ return { news: news || { items: [] }, videos: videos || { items: [] }, la: la || {}, ticker: ticker || {} };
+ }
+
+ // ticker.json is an object of {label,value,dir?,change_pct?,as_of?} metrics
+ function tickerMetrics(t) {
+ return Object.entries(t || {})
+ .filter(([, m]) => m && typeof m === 'object' && m.label && m.value != null)
+ .map(([, m]) => m);
+ }
+
+ function buildChrome(opts) {
+ const d = RENTV.data, t = d.ticker || {};
+ const metrics = tickerMetrics(t);
+ const topAsset = (d.la.marquee || [])[0];
+ const snapCells = metrics.map(m => {
+ const cls = m.dir === 'up' ? ' up' : m.dir === 'down' ? ' dn' : '';
+ const chg = m.change_pct != null ? ` <span style="font-size:11px">${m.change_pct > 0 ? '+' : ''}${m.change_pct}%</span>` : '';
+ return `<div class="m"><div class="k">${m.label}</div><div class="v${cls}">${m.value}${chg}</div></div>`;
+ }).join('') +
+ `<div class="m"><div class="k">Live Stories</div><div class="v">${(d.news.items || []).length}</div></div>` +
+ `<div class="m"><div class="k">Top LA Asset</div><div class="v">${topAsset ? shortUsd(topAsset.assessed_total) : '—'}</div></div>`;
+ // top bar
+ const bar = document.createElement('header');
+ bar.className = 'rv-top' + (opts.dark ? ' dark' : '');
+ bar.innerHTML = `
+ <button class="rv-burger" id="rvBurger" aria-label="Menu">
+ <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round"><path d="M3 6h18M3 12h18M3 18h18"/></svg>
+ <span class="lbl">Menu</span>
+ </button>
+ <a class="rv-brand" href="/">REN<b>TV</b></a>
+ <button class="rv-user" id="rvUser" title="Sign in" aria-label="Sign in">
+ <svg width="19" height="19" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="8" r="3.5"/><path d="M4.5 20a7.5 7.5 0 0 1 15 0" stroke-linecap="round"/></svg>
+ </button>`;
+ document.body.prepend(bar);
+ // scrim + drawer
+ const scrim = document.createElement('div'); scrim.className = 'rv-scrim'; scrim.id = 'rvScrim';
+ const grp = (name) => `<div class="rv-grp"><div class="gt">${name}</div></div><nav>` +
+ NAV[name].map(([label, href]) => `<a href="${href}">${label}</a>`).join('') + `</nav>`;
+ const drawer = document.createElement('aside'); drawer.className = 'rv-drawer'; drawer.id = 'rvDrawer';
+ drawer.innerHTML = `
+ <div class="dh"><span class="w">REN<b>TV</b></span><button id="rvClose" aria-label="Close">✕</button></div>
+ <div class="rv-snap">${snapCells}</div>
+ ${grp('Sections')}${grp('Intelligence')}${grp('Company')}
+ <div class="df">RENTV.com — Commercial Real Estate News · data live from rentv.com + LA County Assessor + CA DRE</div>`;
+ document.body.append(scrim, drawer);
+ const open = () => { scrim.classList.add('open'); drawer.classList.add('open'); };
+ const close = () => { scrim.classList.remove('open'); drawer.classList.remove('open'); };
+ document.getElementById('rvBurger').onclick = open;
+ document.getElementById('rvClose').onclick = close;
+ scrim.onclick = close;
+ document.getElementById('rvUser').onclick = () => alert('Sign in — the ONLY thing the upper-right icon does. All navigation + data lives in the upper-left hamburger.');
+ }
+
+ // ---- numbering + pick tray ----
+ function initNumbers(layout) {
+ const KEY = 'rentvPicks';
+ const picks = new Set(JSON.parse(localStorage.getItem(KEY) || '[]'));
+ let n = 0;
+ document.querySelectorAll('[data-eln]').forEach(el => {
+ n++;
+ const code = layout + '·' + String(n).padStart(2, '0');
+ el.dataset.elnCode = code;
+ const badge = document.createElement('span');
+ badge.className = 'eln' + (picks.has(code) ? ' picked' : '');
+ badge.textContent = code;
+ badge.title = 'Click to add/remove from your picks';
+ badge.onclick = (e) => { e.stopPropagation(); toggle(code, badge); };
+ el.appendChild(badge);
+ });
+ // FAB + tray
+ const fab = document.createElement('div'); fab.className = 'rv-fab';
+ fab.innerHTML = `<button class="t-num">① Numbers</button><button class="t-pick">★ My Picks (<span id="rvPickN">${picks.size}</span>)</button>`;
+ document.body.append(fab);
+ const tray = document.createElement('div'); tray.className = 'rv-tray'; tray.id = 'rvTray';
+ tray.innerHTML = `<div class="th"><b>My Picks — elements to keep in the final design</b><button class="x" id="rvTrayX">✕</button></div>
+ <div class="chips" id="rvChips"></div>
+ <div class="acts"><button class="copy" id="rvCopy">Copy picks</button><button class="clr" id="rvClr">Clear all</button></div>`;
+ document.body.append(tray);
+ const numBtn = fab.querySelector('.t-num'), pickBtn = fab.querySelector('.t-pick');
+ numBtn.onclick = () => { document.body.classList.toggle('eln-on'); numBtn.classList.toggle('on'); };
+ pickBtn.onclick = () => tray.classList.add('open');
+ document.getElementById('rvTrayX').onclick = () => tray.classList.remove('open');
+ document.body.classList.add('eln-on'); numBtn.classList.add('on'); // numbers ON by default
+
+ function save() { localStorage.setItem(KEY, JSON.stringify([...picks])); document.getElementById('rvPickN').textContent = picks.size; renderChips(); }
+ function toggle(code, badge) { if (picks.has(code)) picks.delete(code); else picks.add(code); if (badge) badge.classList.toggle('picked'); save(); }
+ function renderChips() {
+ const box = document.getElementById('rvChips');
+ if (!picks.size) { box.innerHTML = '<span class="empty">No picks yet. Turn on Numbers, then click the ▦ badge on any element you want in the final build.</span>'; return; }
+ box.innerHTML = [...picks].sort().map(c => `<span class="chip">${c}<button data-c="${c}">✕</button></span>`).join('');
+ box.querySelectorAll('button').forEach(b => b.onclick = () => {
+ picks.delete(b.dataset.c);
+ const badge = [...document.querySelectorAll('.eln')].find(e => e.dataset && e.textContent === b.dataset.c);
+ document.querySelectorAll('.eln').forEach(e => { if (e.textContent === b.dataset.c) e.classList.remove('picked'); });
+ save();
+ });
+ }
+ document.getElementById('rvCopy').onclick = () => {
+ const txt = [...picks].sort().join(', ');
+ navigator.clipboard?.writeText(txt); alert('Copied ' + picks.size + ' picks:\n\n' + (txt || '(none)'));
+ };
+ document.getElementById('rvClr').onclick = () => { picks.clear(); document.querySelectorAll('.eln.picked').forEach(e => e.classList.remove('picked')); save(); };
+ renderChips();
+ }
+
+ const esc = s => String(s == null ? '' : s).replace(/[&<>"]/g, c => ({ '&': '&', '<': '<', '>': '>', '"': '"' }[c]));
+ const SECT_COLOR = { Financing: '#2563eb', Sales: '#c8102e', Leases: '#0f8a4d', Development: '#b45309', Multifamily: '#7c3aed', Industrial: '#0891b2', Retail: '#db2777' };
+ const sectionColor = c => SECT_COLOR[c] || '#5b636b';
+ // image with graceful fallback to a section-tinted gradient
+ const imgTag = (src, cls, seed) => {
+ const hue = ((String(seed || src).length * 37) % 360);
+ const grad = `linear-gradient(135deg,hsl(${hue} 45% 32%),hsl(${(hue + 40) % 360} 50% 20%))`;
+ return `<span class="${cls || ''}" style="display:block;background:${grad};background-size:cover;background-position:center"${src ? ` data-bg="${esc(src)}"` : ''}></span>`;
+ };
+ function hydrateImages() { // set bg images after mount; hide broken ones gracefully (gradient remains)
+ document.querySelectorAll('[data-bg]').forEach(el => {
+ const im = new Image(); const url = el.getAttribute('data-bg');
+ im.onload = () => { el.style.backgroundImage = `url("${url}")`; };
+ im.src = url;
+ });
+ }
+
+ return {
+ NAV, usd, shortUsd, dstr, esc, sectionColor, imgTag, hydrateImages, data: null,
+ async mount(opts) {
+ opts = opts || {};
+ this.data = await loadData();
+ buildChrome(opts);
+ if (opts.render) opts.render(this.data);
+ this.hydrateImages();
+ initNumbers(opts.layout || 'L');
+ },
+ };
+})();
diff --git a/mockups/data/la-commercial.json b/mockups/data/la-commercial.json
new file mode 100644
index 00000000..dc559a2c
--- /dev/null
+++ b/mockups/data/la-commercial.json
@@ -0,0 +1 @@
+{"fetched_at":"2026-07-22T18:31:04.117Z","county":"06037","marquee":[{"ain":"4334007008","address":"121 N LA CIENEGA BLVD","city":"LOS ANGELES","zip":"90048","type":"retail","type_label":"Retail","use_desc":"Shopping Center (Regional)","assessed_total":995837586,"assessed_land":476270150,"assessed_improvement":519567436,"roll_year":"2025","recording_date":"2020-12-29T00:00:00.000Z","sqft":1284654,"year_built":1981,"units":0,"brief":"The 1,284,654 sqft, 1981-built regional shopping center at 121 N La Cienega Blvd in Los Angeles has an assessed value of $995,837,586, last recorded on Dec 29."},{"ain":"2424043022","address":"3900 LANKERSHIM BLVD","city":"LOS ANGELES","zip":"90068","type":"industrial","type_label":"Industrial","use_desc":"Heavy Manufacturing","assessed_total":919928415,"assessed_land":160335677,"assessed_improvement":199521231,"roll_year":"2025","recording_date":"2011-01-28T00:00:00.000Z","sqft":8048,"year_built":1930,"units":0,"brief":"The 1930-built, 8,048 sqft heavy manufacturing property at 3900 Lankershim Blvd in Los Angeles has an assessed value of $919,928,415, last recorded transferring on Jan 28."},{"ain":"5151023400","address":"525 S FLOWER ST","city":"LOS ANGELES","zip":"90071","type":"office","type_label":"Office","use_desc":"Office Building","assessed_total":909370171,"assessed_land":134403685,"assessed_improvement":774966486,"roll_year":"2025","recording_date":"2013-10-01T00:00:00.000Z","sqft":2832748,"year_built":1971,"units":0,"brief":"The 2.8M sqft office building at 525 S Flower St in Los Angeles has an assessed value of $909M and was last transferred on October 1, 1971."},{"ain":"2673021041","address":"15800 ROSCOE BLVD","city":"LOS ANGELES","zip":"91406","type":"industrial","type_label":"Industrial","use_desc":"Food Processing Plant","assessed_total":856465517,"assessed_land":104517723,"assessed_improvement":391931829,"roll_year":"2025","recording_date":"2016-09-21T00:00:00.000Z","sqft":393032,"year_built":1954,"units":0,"brief":"The 393,032 sqft food processing plant at 15800 Roscoe Blvd in Los Angeles has an assessed value of $856,465,517 and last transferred on September 21."},{"ain":"2485021011","address":"4000 WARNER BLVD","city":"BURBANK","zip":"91522","type":"studio","type_label":"Studio / Media","use_desc":"Motion Picture, Radio and Television Industry","assessed_total":737677562,"assessed_land":288849385,"assessed_improvement":448828177,"roll_year":"2025","recording_date":"2022-04-08T00:00:00.000Z","sqft":152217,"year_built":1926,"units":0,"brief":"The 152,217 sqft studio at 4000 Warner Blvd in Burbank, valued at $737,677,562 as of last transfer on April 8, 2022, supports the Motion Picture, Radio and Television Industry."},{"ain":"5151014031","address":"333 S HOPE ST","city":"LOS ANGELES","zip":"90071","type":"office","type_label":"Office","use_desc":"Office Building","assessed_total":729339835,"assessed_land":112546537,"assessed_improvement":616793298,"roll_year":"2025","recording_date":"2013-10-15T00:00:00.000Z","sqft":1392719,"year_built":1974,"units":0,"brief":"The office building located at 333 S HOPE ST in Los Angeles, with a assessed value of $729,339,835 and built in 1974, spans 1,392,719 sqft. Last recorded transfer occurred on Oct 15."},{"ain":"2484024003","address":"3000 W ALAMEDA AVE","city":"BURBANK","zip":"91523","type":"studio","type_label":"Studio / Media","use_desc":"Motion Picture, Radio and Television Industry","assessed_total":726811997,"assessed_land":310816518,"assessed_improvement":415995479,"roll_year":"2025","recording_date":"2007-12-21T00:00:00.000Z","sqft":178427,"year_built":1954,"units":0,"brief":"The motion picture and media property at 3000 W Alameda Ave in Burbank, assessed at $726,811,997 for 178,427 sqft, last transferred on December 21st."},{"ain":"2424043032","address":"100 UNIVERSAL CITY PLAZA","city":"LOS ANGELES","zip":"91608","type":"studio","type_label":"Studio / Media","use_desc":"Motion Picture, Radio and Television Industry","assessed_total":708909993,"assessed_land":178478332,"assessed_improvement":530431661,"roll_year":"2025","recording_date":"2011-01-28T00:00:00.000Z","sqft":65532,"year_built":1924,"units":0,"brief":"The 65,532 sqft 1924-built Universal City Plaza studio property in Los Angeles, valued at $708,909,993, was last transferred on January 28."},{"ain":"5151015012","address":"333 S GRAND AVE","city":"LOS ANGELES","zip":"90071","type":"office","type_label":"Office","use_desc":"Office Building","assessed_total":682082790,"assessed_land":72313693,"assessed_improvement":609769097,"roll_year":"2025","recording_date":"2013-10-15T00:00:00.000Z","sqft":1480365,"year_built":1981,"units":0,"brief":"The office building at 333 S Grand Ave in Los Angeles has an assessed value of $682,082,790 and spans 1,480,365 square feet, last recorded to have transferred on October 15. Built in 1981."},{"ain":"2424043034","address":"3900 LANKERSHIM BLVD","city":"LOS ANGELES","zip":"90068","type":"studio","type_label":"Studio / Media","use_desc":"Motion Picture, Radio and Television Industry","assessed_total":652153484,"assessed_land":134253441,"assessed_improvement":517900043,"roll_year":"2025","recording_date":"2011-01-28T00:00:00.000Z","sqft":21585,"year_built":1965,"units":0,"brief":"The 21,585 sqft studio at 3900 Lankershim Blvd in Los Angeles, valued at $652,153,484 as of last transfer on Jan 28, 1965, supports the motion picture, radio, and television industry."},{"ain":"2424043021","address":"3900 LANKERSHIM BLVD","city":"LOS ANGELES","zip":"90068","type":"industrial","type_label":"Industrial","use_desc":"Heavy Manufacturing","assessed_total":631556702,"assessed_land":73689555,"assessed_improvement":557867147,"roll_year":"2025","recording_date":"2011-01-28T00:00:00.000Z","sqft":125487,"year_built":1981,"units":0,"brief":"The industrial property at 3900 Lankershim Blvd in Los Angeles, used for heavy manufacturing and with a assessed value of $631,556,702, last recorded transferred on January 28, features a building size of 125,487 sqft and was constructed in 1981."},{"ain":"4211010118","address":"5865 S CAMPUS CENTER DR","city":"LOS ANGELES","zip":"90094","type":"office","type_label":"Office","use_desc":"Office Building","assessed_total":583333400,"assessed_land":175581577,"assessed_improvement":407751823,"roll_year":"2025","recording_date":"2016-12-22T00:00:00.000Z","sqft":514260,"year_built":1943,"units":0,"brief":"The 514,260 sqft office building at 5865 S Campus Center Dr in Los Angeles has an assessed value of $583,333,400 and last transferred on Dec 22."}],"recent":[{"ain":"8264005913","address":null,"city":null,"zip":null,"type":"industrial","type_label":"Industrial","use_desc":"Industrial","assessed_total":0,"assessed_land":0,"assessed_improvement":0,"roll_year":"2025","recording_date":"2025-04-02T00:00:00.000Z","sqft":0,"year_built":0,"units":0,"brief":"Last recorded transfer for this industrial property was on April 2nd."},{"ain":"5147007901","address":"501 E 5TH ST","city":"LOS ANGELES","zip":"90013","type":"industrial","type_label":"Industrial","use_desc":"Industrial","assessed_total":0,"assessed_land":0,"assessed_improvement":0,"roll_year":"2025","recording_date":"2025-03-13T00:00:00.000Z","sqft":0,"year_built":0,"units":0,"brief":"The industrial property at 501 E 5th St, Los Angeles, had its last recorded transfer on March 13."},{"ain":"6015036011","address":"1745 W FLORENCE AVE","city":"LOS ANGELES","zip":"90047","type":"retail","type_label":"Retail","use_desc":"Store","assessed_total":405756,"assessed_land":312120,"assessed_improvement":93636,"roll_year":"2025","recording_date":"2025-01-21T00:00:00.000Z","sqft":1350,"year_built":1928,"units":0,"brief":"The retail store at 1745 W Florence Ave in Los Angeles, with a assessed value of $405,756 and 1,350 sqft of building space, was last transferred on January 21. Built in 1928."},{"ain":"7436006923","address":"1667 W 9TH ST","city":"LONG BEACH","zip":"90813","type":"industrial","type_label":"Industrial","use_desc":"Light Manufacturing","assessed_total":0,"assessed_land":0,"assessed_improvement":0,"roll_year":"2025","recording_date":"2025-01-17T00:00:00.000Z","sqft":16500,"year_built":1973,"units":0,"brief":"The 16,500 sqft light manufacturing property at 1667 W 9th St in Long Beach, built in 1973, last recorded transferred on Jan 17."},{"ain":"6239015023","address":null,"city":null,"zip":null,"type":"industrial","type_label":"Industrial","use_desc":"Light Manufacturing","assessed_total":0,"assessed_land":0,"assessed_improvement":0,"roll_year":"2025","recording_date":"2025-01-06T00:00:00.000Z","sqft":0,"year_built":0,"units":0,"brief":"Last recorded transfer for the light manufacturing industrial property was on January 6."},{"ain":"5586035044","address":"5766 HOLLYWOOD BLVD","city":"LOS ANGELES","zip":"90028","type":"retail","type_label":"Retail","use_desc":"Store Combination","assessed_total":78189905,"assessed_land":28000000,"assessed_improvement":50000000,"roll_year":"2025","recording_date":"2024-12-31T00:00:00.000Z","sqft":178421,"year_built":2021,"units":161,"brief":"The retail property at 5766 Hollywood Blvd in Los Angeles, a store combination occupying 178,421 sqft and built in 2021, has an assessed value of $78,189,905 as of last recorded transfer on December 31."},{"ain":"6303019014","address":"3385 LEONIS BLVD","city":"VERNON","zip":"90058","type":"industrial","type_label":"Industrial","use_desc":"Heavy Manufacturing","assessed_total":18724000,"assessed_land":8274000,"assessed_improvement":10450000,"roll_year":"2025","recording_date":"2024-12-31T00:00:00.000Z","sqft":55136,"year_built":1947,"units":0,"brief":"The 55,136 sqft heavy manufacturing property at 3385 Leonis Blvd in Vernon has an assessed value of $18,724,000 and was last recorded to have transferred on December 31st."},{"ain":"5442025039","address":"2965 ALLESANDRO ST","city":"LOS ANGELES","zip":"90039","type":"retail","type_label":"Retail","use_desc":"Store Combination","assessed_total":10404000,"assessed_land":3060000,"assessed_improvement":7344000,"roll_year":"2025","recording_date":"2024-12-31T00:00:00.000Z","sqft":33888,"year_built":2024,"units":25,"brief":"The retail property at 2965 Allesandro St in Los Angeles, with a assessed value of $10,404,000 and a building size of 33,888 sqft, was last recorded to have been transferred on December 31."},{"ain":"2350004070","address":"11410 BURBANK BLVD","city":"LOS ANGELES","zip":"91601","type":"retail","type_label":"Retail","use_desc":"Store","assessed_total":9000000,"assessed_land":9000000,"assessed_improvement":0,"roll_year":"2025","recording_date":"2024-12-31T00:00:00.000Z","sqft":12388,"year_built":1963,"units":6,"brief":"The retail store located at 11410 Burbank Blvd in Los Angeles, with a assessed value of $9,000,000 and a building size of 12,388 sqft, was last recorded to have transferred on December 31st."},{"ain":"2258012026","address":"17257 VENTURA BLVD","city":"LOS ANGELES","zip":"91316","type":"retail","type_label":"Retail","use_desc":"Store","assessed_total":8939799,"assessed_land":5231865,"assessed_improvement":3707934,"roll_year":"2025","recording_date":"2024-12-31T00:00:00.000Z","sqft":30162,"year_built":1980,"units":0,"brief":"The retail store located at 17257 Ventura Blvd in Los Angeles, with a assessed value of $8,939,799 and a building size of 30,162 sqft, was last recorded to have transferred on December 31st."},{"ain":"7432016034","address":"1442 W 16TH ST","city":"LONG BEACH","zip":"90813","type":"industrial","type_label":"Industrial","use_desc":"Light Manufacturing","assessed_total":8500000,"assessed_land":6200000,"assessed_improvement":2300000,"roll_year":"2025","recording_date":"2024-12-31T00:00:00.000Z","sqft":2212,"year_built":1982,"units":1,"brief":"The 2,212 sqft light manufacturing industrial property at 1442 W 16th St in Long Beach, assessed at $8,500,000, last transferred on December 31."},{"ain":"8382005032","address":"440 E ARROW HWY","city":"SAN DIMAS","zip":"91773","type":"industrial","type_label":"Industrial","use_desc":"Light Manufacturing","assessed_total":6869000,"assessed_land":3200000,"assessed_improvement":3669000,"roll_year":"2025","recording_date":"2024-12-31T00:00:00.000Z","sqft":18369,"year_built":1997,"units":0,"brief":"The 18,369 sqft light manufacturing property at 440 E Arrow Hwy in San Dimas has an assessed value of $6,869,000 and was last transferred on December 31st. Built in 1997."},{"ain":"6203022043","address":"4149 TWEEDY BLVD","city":"SOUTH GATE","zip":"90280","type":"retail","type_label":"Retail","use_desc":"Shopping Center (Neighborhood, Community)","assessed_total":5167728,"assessed_land":1118308,"assessed_improvement":4049420,"roll_year":"2025","recording_date":"2024-12-31T00:00:00.000Z","sqft":12321,"year_built":1989,"units":0,"brief":"The 12,321 sqft shopping center at 4149 Tweedy Blvd in South Gate, assessed at $5,167,728, last transferred on December 31st."},{"ain":"5282010030","address":"8966 GARVEY AVE","city":"ROSEMEAD","zip":"91770","type":"retail","type_label":"Retail","use_desc":"Store","assessed_total":5021460,"assessed_land":3258900,"assessed_improvement":1762560,"roll_year":"2025","recording_date":"2024-12-31T00:00:00.000Z","sqft":17745,"year_built":1985,"units":0,"brief":"The retail store located at 8966 Garvey Ave in Rosemead, with a assessed value of $5,021,460 and 17,745 sqft of building space built in 1985, last recorded transferred on December 31."},{"ain":"6213003027","address":"3480 E FLORENCE AVE","city":"HUNTINGTON PARK","zip":"90255","type":"retail","type_label":"Retail","use_desc":"Restaurant, Cocktail Lounge","assessed_total":4500000,"assessed_land":2750000,"assessed_improvement":1750000,"roll_year":"2025","recording_date":"2024-12-31T00:00:00.000Z","sqft":1866,"year_built":2024,"units":1,"brief":"The retail property at 3480 E Florence Ave in Huntington Park, a restaurant and cocktail lounge with a assessed value of $4,500,000 and 1,866 sqft, last recorded transferred on December 31."},{"ain":"6203022045","address":"4161 TWEEDY BLVD","city":"SOUTH GATE","zip":"90280","type":"retail","type_label":"Retail","use_desc":"Shopping Center (Neighborhood, Community)","assessed_total":4229779,"assessed_land":919898,"assessed_improvement":3309881,"roll_year":"2025","recording_date":"2024-12-31T00:00:00.000Z","sqft":10238,"year_built":1989,"units":0,"brief":"The 10,238 sqft shopping center at 4161 Tweedy Blvd in South Gate, assessed at $4,229,779, last transferred on December 31."},{"ain":"7432005047","address":"1501 SANTA FE AVE","city":"LONG BEACH","zip":"90813","type":"retail","type_label":"Retail","use_desc":"Store","assessed_total":4000000,"assessed_land":1325000,"assessed_improvement":2675000,"roll_year":"2025","recording_date":"2024-12-31T00:00:00.000Z","sqft":7760,"year_built":1947,"units":0,"brief":"The retail store located at 1501 Santa Fe Ave in Long Beach, with a assessed value of $4,000,000 and a building size of 7,760 sqft, was last recorded to have transferred on Tue Dec 31, 1947."},{"ain":"5511008002","address":"8240 BEVERLY BLVD","city":"LOS ANGELES","zip":"90048","type":"office","type_label":"Office","use_desc":"Office Building","assessed_total":3899998,"assessed_land":2924999,"assessed_improvement":974999,"roll_year":"2025","recording_date":"2024-12-31T00:00:00.000Z","sqft":6786,"year_built":1955,"units":0,"brief":"The 6,786 sqft office building located at 8240 Beverly Blvd in Los Angeles has an assessed value of $3,899,998 and was last transferred on December 31st. Built in 1955."},{"ain":"5636015189","address":"428 ARDEN AVE","city":"GLENDALE","zip":"91203","type":"office","type_label":"Office","use_desc":"Professional Building","assessed_total":3760000,"assessed_land":2300000,"assessed_improvement":1460000,"roll_year":"2025","recording_date":"2024-12-31T00:00:00.000Z","sqft":8436,"year_built":1985,"units":0,"brief":"The 8,436 sqft professional office building located at 428 Arden Ave in Glendale, assessed at $3,760,000, last transferred on December 31st."},{"ain":"2242027022","address":"15007 OXNARD ST","city":"LOS ANGELES","zip":"91411","type":"industrial","type_label":"Industrial","use_desc":"Light Manufacturing","assessed_total":3659487,"assessed_land":2639941,"assessed_improvement":1019546,"roll_year":"2025","recording_date":"2024-12-31T00:00:00.000Z","sqft":17015,"year_built":1946,"units":1,"brief":"The 17,015 sqft light manufacturing property at 15007 Oxnard St, Los Angeles, has an assessed value of $3,659,487 and was last transferred on December 31st. Built in 1946."}],"firms":[{"name":"CBRE, Inc.","agent_count":1213,"city":"LOS ANGELES","license_no":"00409987"},{"name":"New Star Realty, Inc.","agent_count":366,"city":"LOS ANGELES","license_no":"01096925"},{"name":"Graff Real Estate","agent_count":278,"city":"LOS ANGELES","license_no":"02041450"},{"name":"Forward Wilshire Inc","agent_count":208,"city":"LOS ANGELES","license_no":"01870534"},{"name":"Nourmand & Associates","agent_count":172,"city":"LOS ANGELES","license_no":"00656371"},{"name":"Newmark of Southern California Inc","agent_count":157,"city":"LOS ANGELES","license_no":"01355491"},{"name":"LF Assoc, Inc.","agent_count":146,"city":"LOS ANGELES","license_no":"01430290"},{"name":"Erdelyi Real Estate, Inc.","agent_count":137,"city":"LOS ANGELES","license_no":"01920782"},{"name":"D/Aq Corporation","agent_count":111,"city":"LOS ANGELES","license_no":"01129558"},{"name":"Savills Inc.","agent_count":94,"city":"LOS ANGELES","license_no":"00388260"},{"name":"Dream Realty & Investments Inc","agent_count":91,"city":"LOS ANGELES","license_no":"02205941"},{"name":"Transwestern Property Company West","agent_count":81,"city":"LOS ANGELES","license_no":"01263636"}]}
\ No newline at end of file
diff --git a/mockups/data/news.json b/mockups/data/news.json
new file mode 100644
index 00000000..806e6de7
--- /dev/null
+++ b/mockups/data/news.json
@@ -0,0 +1 @@
+{"source":"rentv.com — live","fetched_at":"2026-07-22T20:20:02.708Z","count":30,"items":[{"id":"34236","title":"The Swig Company Obtains Refi on Office High-Rise in San Franciscos Financial District","url":"https://www.rentv.com/content/homepage/mainnews/news/34236","cat":"Financing","image":"https://www.rentv.com/images/dynamic/news_20294_220MontgomeryStSFNancyAmaralA2N2Jul162026.jpg"},{"id":"34233","title":"Tailwind Investment Group Spends $65 Mil on Two Sacramento Res Properties","url":"https://www.rentv.com/content/homepage/mainnews/news/34233","cat":"Multifamily","image":"https://www.rentv.com/images/dynamic/news_20291_6633ValleyHiDrSacramentoDavidHulmeTailwindJul182026.jpg"},{"id":"34239","title":"Fresno Multifamily Sale Works Out to $177k per Unit","url":"https://www.rentv.com/content/homepage/mainnews/news/34239","cat":"Multifamily","image":"https://www.rentv.com/images/dynamic/news_20297_585WestSierraAveFresnoDaveEbelingJul212026.jpg"},{"id":"34235","title":"Retail Portfolio in Downtown Long Beach Fetches $30 Mil","url":"https://www.rentv.com/content/homepage/mainnews/news/34235","cat":"Retail","image":"https://www.rentv.com/images/dynamic/news_20293_MosiacLongBeachAnneMonaghanJul202026.jpg"},{"id":"34224","title":"Shorenstein Buys 77k sf Bay Area Office Building","url":"https://www.rentv.com/content/homepage/mainnews/news/34224","cat":"Sales","image":"https://www.rentv.com/images/dynamic/news_20283_550AllertonRedwoodCityNickTrombolaNWMKJul162026.jpg"},{"id":"34226","title":"Carson Distribution Facility Acquired by WareSpace in a $23 Mil Deal","url":"https://www.rentv.com/content/homepage/mainnews/news/34226","cat":"Sales","image":"https://www.rentv.com/images/dynamic/news_20285_860SandhillAveCarsonElisabethManvilleSmartJul162026.jpg"},{"id":"34218","title":"Camden Property Trust Buys Gilbert, AZ Res Community in $124 Mil Deal","url":"https://www.rentv.com/content/homepage/mainnews/news/34218","cat":"Multifamily","image":"https://www.rentv.com/images/dynamic/news_20278_3150ERayRdGilbertKimHoidalCBREJul132026.jpg"},{"id":"34225","title":"CentrePoint Properties Spends $30 Mil on Trio of Colorado Retail Assets","url":"https://www.rentv.com/content/homepage/mainnews/news/34225","cat":"Retail","image":""},{"id":"34227","title":"MG Properties Buys 188-Unit Portland Res Asset","url":"https://www.rentv.com/content/homepage/mainnews/news/34227","cat":"Multifamily","image":"https://www.rentv.com/images/dynamic/news_20286_TupeloAlleyPortlandPaulKaseburgMGPropJul162026.jpg"},{"id":"34216","title":"Meta Housing Well Underway with 300-Unit Res Community in Woodland Hills","url":"https://www.rentv.com/content/homepage/mainnews/news/34216","cat":"Multifamily","image":"https://www.rentv.com/images/dynamic/news_20277_21300WOxnardStWHillsHollyJenvyJul152026.jpg"},{"id":"34214","title":"Intercontinental Real Estate Corporation Closes $69 Mil Retail Buy in Washington","url":"https://www.rentv.com/content/homepage/mainnews/news/34214","cat":"Retail","image":"https://www.rentv.com/images/dynamic/news_20274_1402LakeTappsPkwySEAuburnWABruceBeckJul152026.jpg"},{"id":"34208","title":"Eagle Partners and The Vistria Group Spend $132 Mil on Garden Grove Mixed-Income Res Community","url":"https://www.rentv.com/content/homepage/mainnews/news/34208","cat":"Multifamily","image":"https://www.rentv.com/images/dynamic/news_20267_12091BayportStGardenGroveCOSTARAnnieNoebelCornersCommJul142026.jpg"},{"id":"34204","title":"Oceanside Res Community Trades for $77.5 Mil","url":"https://www.rentv.com/content/homepage/mainnews/news/34204","cat":"Multifamily","image":"https://www.rentv.com/images/dynamic/news_20262_3851SherbourneDrOceansideDANKIRKSEYClaudiaLipmannCBREJul142026.jpg"},{"id":"34210","title":"BKM Capital Partners Boosts Pac Northwest Holdings with Purchase of 400k sf Industrial Buy in Washington","url":"https://www.rentv.com/content/homepage/mainnews/news/34210","cat":"Industrial","image":"https://www.rentv.com/images/dynamic/news_20270_SouthcenterWestBusinessParkTukwilaWASuleAygorenConnectJul142026.jpg"},{"id":"34203","title":"The Bascom Group Pays Over $53 Mil for OC Res Community","url":"https://www.rentv.com/content/homepage/mainnews/news/34203","cat":"Multifamily","image":"https://www.rentv.com/images/dynamic/news_20261_6968CampusDrBuenaParkJerryFinkBascomJul132026.jpg"},{"id":"34199","title":"Leasing Assignment for 650k sf San Francisco Office Tower Awarded to CBRE","url":"https://www.rentv.com/content/homepage/mainnews/news/34199","cat":"Sales","image":"https://www.rentv.com/images/dynamic/news_20259_415NatomaSFranciscoIrineSpivakCBREJun162026.jpg"},{"id":"34202","title":"SummerHill Apartment Communities Breaks Ground on New 251-Unit Bay Area Res Development","url":"https://www.rentv.com/content/homepage/mainnews/news/34202","cat":"Development","image":"https://www.rentv.com/images/dynamic/news_20260_11ElcaminoRealSMateoAnneMonaghanJul72026.jpg"},{"id":"34196","title":"Rubicon Point Partners Spends $53 Mil on Silicon Valley Medical/Office Property","url":"https://www.rentv.com/content/homepage/mainnews/news/34196","cat":"Sales","image":""},{"id":"34195","title":"Borstein Enterprises Picks Up 282k sf OC Industrial Asset","url":"https://www.rentv.com/content/homepage/mainnews/news/34195","cat":"Industrial","image":"https://www.rentv.com/images/dynamic/news_20256_15339BarrancaPkwyIrvineSavannahDurbinCWJul62026.jpg"},{"id":"34188","title":"Chino Distribution Facility Leased by Auto Parts Distributor in $42 Mil Deal","url":"https://www.rentv.com/content/homepage/mainnews/news/34188","cat":"Leases","image":"https://www.rentv.com/images/dynamic/news_20252_16380EuclidAveChinoElisabethManvilleSmartJul22026.jpg"},{"id":"34186","title":"Santa Clara Industrial Property Trades for $25.75 Mil","url":"https://www.rentv.com/content/homepage/mainnews/news/34186","cat":"Industrial","image":"https://www.rentv.com/images/dynamic/news_20250_651MartinAveSClaraGraceLewisJLLJul12026.jpg"},{"id":"34181","title":"Redwood West and Panattoni Buy The Pike Destination Center in Long Beach","url":"https://www.rentv.com/content/homepage/mainnews/news/34181","cat":"Retail","image":"https://www.rentv.com/images/dynamic/news_20245_95SPineAveLongBeachJessicaSpauldingJun302026.jpg"},{"id":"34180","title":"Kennedy Wilson and Jamison Form Partnership to Develop 4,000 Units of Affordable Housing in Los Angeles","url":"https://www.rentv.com/content/homepage/mainnews/news/34180","cat":"Development","image":"https://www.rentv.com/images/dynamic/news_20244_350SHopeBruceBeckJune302026.jpg"},{"id":"34086","title":"Oak Park Investments and Arise Investments Acquire Westlake Village Warehouse/Office Building in $19 Mil Transaction","url":"https://www.rentv.com/content/homepage/mainnews/news/34086","cat":"Industrial","image":"https://www.rentv.com/images/dynamic/news_20161_5785LinderoCanyonRdWstlkVillageDannySoroudiAriseJune22026.jpg"},{"id":"34177","title":"Holland Partner Group Closes $105 Mil Bay Area Res Buy","url":"https://www.rentv.com/content/homepage/mainnews/news/34177","cat":"Multifamily","image":"https://www.rentv.com/images/dynamic/news_20242_1432WestSanCarlosSt.jpg"},{"id":"34172","title":"Haven Housing buys 264-Unit Res Community in Austin Metro Area","url":"https://www.rentv.com/content/homepage/mainnews/news/34172","cat":"Multifamily","image":"https://www.rentv.com/images/dynamic/news_20237_15001StrathavenPassPflugervilleTXElliottGolanConnectJun262026.jpg"},{"id":"34174","title":"MG Properties Acquires Two Multifamily Properties in Gresham, OR in Deals Totaling $60.8 Mil","url":"https://www.rentv.com/content/homepage/mainnews/news/34174","cat":"Multifamily","image":"https://www.rentv.com/images/dynamic/news_20239_ParksideApartmentsGreshamORPaulKasebergMGPropJune262026.jpg"},{"id":"34171","title":"Leading AI Firm Leases 1.1 msf of Space at Luke Field in Glendale, AZ","url":"https://www.rentv.com/content/homepage/mainnews/news/34171","cat":"Leases","image":"https://www.rentv.com/images/dynamic/news_20236_7733LitchfieldRdGlendaleAZStaceyHershauerJun242026.jpg"},{"id":"34169","title":"Seattle Area Res Community Refinanced with $82.5 Mil Loan from Mesa West Capital","url":"https://www.rentv.com/content/homepage/mainnews/news/34169","cat":"Financing","image":"https://www.rentv.com/images/dynamic/news_20233_OlinFieldsEverettWABruceBeckJun242026.jpg"},{"id":"34160","title":"Apollo Lands in Gardena","url":"https://www.rentv.com/content/homepage/mainnews/news/34160","cat":"Sales","image":"https://www.rentv.com/images/dynamic/news_20227_12888CrenshawBlvdGardenaJadeTerryCityviewJun232026.jpg"}]}
\ No newline at end of file
diff --git a/mockups/data/ticker.json b/mockups/data/ticker.json
new file mode 100644
index 00000000..d4ae0a07
--- /dev/null
+++ b/mockups/data/ticker.json
@@ -0,0 +1 @@
+{"fetched_at":"2026-07-22T20:20:02.971Z","la_office_vac":{"label":"LA OFFICE VAC","value":"23.1%","as_of":"Q2 2026","source":"CBRE/JLL market reports"},"ie_industrial_cap":{"label":"IE INDUSTRIAL CAP","value":"5.2%","as_of":"Q2 2026","source":"Colliers IE industrial"},"cre_index":{"label":"CRE INDEX","value":"4,159","change_pct":-0.49,"dir":"down","source":"Dow Jones U.S. Real Estate"},"ten_yr":{"label":"10-YR","value":"4.66%","change_bps":3,"dir":"up","source":"CBOE 10-Yr (^TNX)"}}
\ No newline at end of file
diff --git a/mockups/data/videos.json b/mockups/data/videos.json
new file mode 100644
index 00000000..ff1fd4f1
--- /dev/null
+++ b/mockups/data/videos.json
@@ -0,0 +1 @@
+{"source":"rentvreview.com (The REview) — live","fetched_at":"2026-07-20T18:46:15.717Z","count":17,"cats":["RENTV Originals","JLL TV","Most Recent","Industry Events","Featured","Most Viewed"],"items":[{"id":"orig-brand","cat":"RENTV Originals","mp4":"/media/originals/brand.mp4","thumb":"/media/originals/brand.jpg","title":"RENTV.com — The Regional Authority","desc":"Western U.S. commercial real estate news since 1998."},{"id":"orig-deals","cat":"RENTV Originals","mp4":"/media/originals/deals.mp4","thumb":"/media/originals/deals.jpg","title":"The Deals Break Here First","desc":"Financing, leases, development and sales across the Western market."},{"id":"orig-review","cat":"RENTV Originals","mp4":"/media/originals/review.mp4","thumb":"/media/originals/review.jpg","title":"The REview — Face to Face with the Dealmakers","desc":"Interviews and insight from the people shaping CRE."},{"id":"orig-daily","cat":"RENTV Originals","mp4":"/media/originals/daily.mp4","thumb":"/media/originals/daily.jpg","title":"RENTV Daily — In Your Inbox, Free","desc":"Every Western CRE deal each morning."},{"id":"2898","yt":"79WFDGZYos4","title":"JLL’s Supply Chain & Logistics Solutions Overview","desc":"We are at the forefront of supply chain trends, focused on delivering top-down supply chain strategy and ground-up execution to help you save time, reduce costs, improve services and minimize risk.
When it comes to recognizing the ","cat":"JLL TV","view_url":"https://www.rentvreview.com/Videos/View/2898","thumb":"https://img.youtube.com/vi/79WFDGZYos4/hqdefault.jpg","embed":"https://www.youtube.com/embed/79WFDGZYos4"},{"id":"3302","yt":"xKYEQkUG5zM","title":"Commercial Real Estate Tech & AI 2026 with Helen Calvin","desc":"Adoption and investment in tech and AI have accelerated in the commercial real estate industry. Finally, right? How should you pick the right tech, the right system, the right provider? When picking a broker to sell your property should you","cat":"Most Recent","view_url":"https://www.rentvreview.com/Videos/View/3302","thumb":"https://img.youtube.com/vi/xKYEQkUG5zM/hqdefault.jpg","embed":"https://www.youtube.com/embed/xKYEQkUG5zM"},{"id":"3273","yt":"eC-oSUBMjIc","title":"ICSC Southern California Kickoff Panel","desc":"Commercial real estate is a constantly evolving industry, and staying ahead of the curve is crucial for success. Recently, a panel discussion at the Hyatt Regency Huntington Beach Resort & Spa brought together leading developers to share th","cat":"Industry Events","view_url":"https://www.rentvreview.com/Videos/View/3273","thumb":"https://img.youtube.com/vi/eC-oSUBMjIc/hqdefault.jpg","embed":"https://www.youtube.com/embed/eC-oSUBMjIc"},{"id":"3301","yt":"O6Mh2VmXGzg","title":"Office Update with Yardi","desc":"In this episode of America’s Commercial Real Estate Show, host Michael Bull is joined by Doug Ressler and Peter Kolaczynski from Yardi Matrix to break down the evolving office market in 2026.

They explore key trends in of","cat":"Most Recent","view_url":"https://www.rentvreview.com/Videos/View/3301","thumb":"https://img.youtube.com/vi/O6Mh2VmXGzg/hqdefault.jpg","embed":"https://www.youtube.com/embed/O6Mh2VmXGzg"},{"id":"3300","yt":"Wm_dhTl8gn8","title":"Single Tenant Cap Rates and Market Intel with Nancy Miller, CCIM","desc":"Nancy Miller with Bull Realty joins Michael to discuss the latest cap rates, loan rates, and expanding tenants in the single tenant net lease investment sector. Discussions include market intel, cap rates, mistakes to avoid, and investor st","cat":"Featured","view_url":"https://www.rentvreview.com/Videos/View/3300","thumb":"https://img.youtube.com/vi/Wm_dhTl8gn8/hqdefault.jpg","embed":"https://www.youtube.com/embed/Wm_dhTl8gn8"},{"id":"3299","yt":"6EJEWbXFQ-c","title":"People Live in Homes Not Corporations Housing Bill Update with Mitch Roschelle","desc":"The 21st Century ROAD to Housing Act (HR6644) passed by the Senate on March 12th, aims to increase U.S housing supply, reduce costs and modernize financing representing a major federal investment in housing. Part of the bill, with some carv","cat":"Featured","view_url":"https://www.rentvreview.com/Videos/View/3299","thumb":"https://img.youtube.com/vi/6EJEWbXFQ-c/hqdefault.jpg","embed":"https://www.youtube.com/embed/6EJEWbXFQ-c"},{"id":"3298","yt":"_1W6U47VN6g","title":"U.S. Hotel Performance Forecast with Ryan Meliker","desc":"As world events unfold and America first implications impact the economy and travel, how might U.S. hotels perform moving forward? What cities hold promise for investors and developers? Join show host and commercial broker Michael Bull, CCI","cat":"Featured","view_url":"https://www.rentvreview.com/Videos/View/3298","thumb":"https://img.youtube.com/vi/_1W6U47VN6g/hqdefault.jpg","embed":"https://www.youtube.com/embed/_1W6U47VN6g"},{"id":"1034","yt":"ql3qVQNRQJ8","title":"LoopNet vs Crexi: Which is Better for Net Lease Property Sales?","desc":"In this video, we take a deep dive into the comparisons between the leading commercial real estate marketplace platforms, LoopNet and Crexi.

SUBSCRIBE to our newsletter to have the top stories hand-delivered to your inbox","cat":"Most Viewed","view_url":"https://www.rentvreview.com/Videos/View/1034","thumb":"https://img.youtube.com/vi/ql3qVQNRQJ8/hqdefault.jpg","embed":"https://www.youtube.com/embed/ql3qVQNRQJ8"},{"id":"1650","yt":"-_QFDzl48tU","title":"1031 Exchange Explained: 45 Day Rule Checklist","desc":"Looking to invest in real estate and want the 1031 exchange explained? Make sure you understand the 1031 exchange rule with this free 45 day rule checklist! 

The 1031 exchange rule is a complicated but important process f","cat":"Featured","view_url":"https://www.rentvreview.com/Videos/View/1650","thumb":"https://img.youtube.com/vi/-_QFDzl48tU/hqdefault.jpg","embed":"https://www.youtube.com/embed/-_QFDzl48tU"},{"id":"3261","yt":"v8CaeZHpTMM","title":"VeganBurg Now Open at High Desert Gateway","desc":"VeganBurg Now Open at High Desert Gateway
","cat":"Industry Events","view_url":"https://www.rentvreview.com/Videos/View/3261","thumb":"https://img.youtube.com/vi/v8CaeZHpTMM/hqdefault.jpg","embed":"https://www.youtube.com/embed/v8CaeZHpTMM"},{"id":"3237","yt":"ujVQ-m0gEpw","title":"Stater Bros. Markets Grand Opening Celebration at The Collection","desc":"Stater Bros. Markets Grand Opening Celebration at The Collection
","cat":"Featured","view_url":"https://www.rentvreview.com/Videos/View/3237","thumb":"https://img.youtube.com/vi/ujVQ-m0gEpw/hqdefault.jpg","embed":"https://www.youtube.com/embed/ujVQ-m0gEpw"},{"id":"3230","yt":"8UtGSxHYQXY","title":"Project Spotlight: The Commons at Meridian","desc":"Project Spotlight: The Commons at Meridian
","cat":"Featured","view_url":"https://www.rentvreview.com/Videos/View/3230","thumb":"https://img.youtube.com/vi/8UtGSxHYQXY/hqdefault.jpg","embed":"https://www.youtube.com/embed/8UtGSxHYQXY"},{"id":"3229","yt":"HzCoIw71Cv8","title":"ICSC & The Western Conference 2024 with Vicky Hammond","desc":"ICSC & The Western Conference with Vicky Hammond 2024
","cat":"Featured","view_url":"https://www.rentvreview.com/Videos/View/3229","thumb":"https://img.youtube.com/vi/HzCoIw71Cv8/hqdefault.jpg","embed":"https://www.youtube.com/embed/HzCoIw71Cv8"}]}
\ No newline at end of file
diff --git a/mockups/index.html b/mockups/index.html
new file mode 100644
index 00000000..0139cdd7
--- /dev/null
+++ b/mockups/index.html
@@ -0,0 +1,61 @@
+<!doctype html><html lang="en"><head>
+<meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
+<title>RENTV — 10 Layout Concepts · pick & choose</title>
+<link href="https://fonts.googleapis.com/css2?family=Playfair+Display:wght@700;900&family=Inter:wght@400;600;700;800&display=swap" rel="stylesheet">
+<style>
+ :root{--red:#c8102e;--ink:#14171a}
+ *{box-sizing:border-box;margin:0}
+ body{font-family:'Inter',system-ui,sans-serif;background:#0b0e12;color:#e7ecf1}
+ header{padding:34px 26px 20px;max-width:1300px;margin:0 auto}
+ h1{font-family:'Playfair Display',serif;font-size:40px;font-weight:900}h1 span{color:var(--red)}
+ .lede{color:#aab2ba;max-width:820px;margin-top:10px;line-height:1.6;font-size:15px}
+ .rules{display:flex;flex-wrap:wrap;gap:12px;margin-top:18px}
+ .rule{background:#12161c;border:1px solid #232a32;border-radius:10px;padding:12px 15px;font-size:13px;max-width:400px}
+ .rule b{color:#fff}.rule .n{color:var(--red);font-weight:800}
+ .grid{display:grid;grid-template-columns:repeat(2,1fr);gap:22px;max-width:1300px;margin:22px auto 60px;padding:0 26px}
+ .cardw{background:#11151b;border:1px solid #232a32;border-radius:14px;overflow:hidden;display:flex;flex-direction:column}
+ .cap{display:flex;align-items:baseline;gap:10px;padding:14px 16px 10px}
+ .cap .id{font:800 13px ui-monospace,monospace;color:#fff;background:var(--red);border-radius:6px;padding:3px 9px}
+ .cap h3{font-size:17px;font-weight:800}
+ .cap .style{font-size:12px;color:#8b939b;margin-left:auto}
+ .desc{padding:0 16px 12px;color:#aab2ba;font-size:13px;line-height:1.5}
+ .framew{position:relative;height:440px;border-top:1px solid #232a32;background:#fff;overflow:hidden}
+ .framew iframe{width:1440px;height:1100px;border:0;transform:scale(.55);transform-origin:top left}
+ .open{position:absolute;right:12px;bottom:12px;background:var(--ink);color:#fff;border:1px solid #333;border-radius:22px;padding:9px 16px;font-weight:800;font-size:13px;text-decoration:none;box-shadow:0 4px 14px rgba(0,0,0,.4)}
+ .open:hover{background:var(--red)}
+ footer{max-width:1300px;margin:0 auto;padding:0 26px 60px;color:#6b747d;font-size:13px}
+ @media(max-width:960px){.grid{grid-template-columns:1fr}}
+</style></head>
+<body>
+<header>
+ <h1>REN<span>TV</span> · 10 Layout Concepts</h1>
+ <p class="lede">Ten genuinely distinct front-page structures for <b style="color:#fff">rentv-v1.agentabrams.com</b> — different skeletons, not palette swaps. Every one renders the <b style="color:#fff">real live data</b> (today's 30 rentv.com headlines, the $995.8M Beverly Center marquee, the CA-DRE brokerage table). Open any layout, then <b style="color:#fff">click the ▦ number badge on any element</b> to drop it into "My Picks" — mix the masthead from one, the ticker from another, the map from a third, and hand me the list.</p>
+ <div class="rules">
+ <div class="rule">🍔 <b>Global chrome (all 10):</b> a modern <b>hamburger upper-left</b> holds <b>every</b> nav tab + all live data points.</div>
+ <div class="rule">👤 <b>Upper-right = user icon only</b>, and it does exactly one thing: <b>sign in</b>. Nothing else lives up there.</div>
+ <div class="rule"><span class="n">L#·##</span> <b>Every element is numbered.</b> Turn numbers on (they're on by default), click a badge to pick it.</div>
+ </div>
+</header>
+<div class="grid" id="grid"></div>
+<footer>Built on the live prod data snapshot · rentv-v1 (:9704) — the canonical fork rentv.agentabrams.com now serves.</footer>
+<script>
+const L=[
+ ['L01','Trading Desk','dark · dense · monospace','A Bloomberg-style terminal: a live metric rail, a news blotter tape, an LA "watchlist", a brokerage league table and a video tape — maximum data density.'],
+ ['L02','Editorial Magazine','light · serif · airy','A glossy magazine front: one giant serif lead with photo, secondary features, a section river, and a dark LA-intelligence editorial block. Calm and premium.'],
+ ['L03','Brutalist Grid','raw · bold · hard rules','Oversized black type, 3px rules, a hard 4-column cell grid where the lead story and the $995.8M value block dominate. Loud and confident.'],
+ ['L04','Card Masonry','image-forward · filterable','A Pinterest-style masonry of image cards with section filter chips — the most visual, browse-y option; assets and firms become cards too.'],
+ ['L05','Split-Screen','news left · LA sticky right','A hard 50/50 split: the live newsroom scrolls on the left while a dark LA-Commercial intelligence panel stays pinned on the right.'],
+ ['L06','Broadcast','ticker-first · "on air"','A scrolling headline tape, an ON-AIR video hero, a BREAKING band, then three section columns — the most TV-news, urgent feel.'],
+ ['L07','Map-Anchored','LA map hero · interactive','The hero is an LA map with clickable pins for each marquee asset; clicking a pin drives a live detail panel. News wraps below.'],
+ ['L08','Timeline Feed','chronological · single spine','One vertical timeline interleaving live news, marquee-asset callouts and recorded transfers — reads like a running market ticker of events.'],
+ ['L09','Broadsheet','newspaper · "RENTV Daily"','A full newspaper front page — flag, dateline, a two-column lead, a 4-column news river, a Property-of-the-Day box and brokerage standings.'],
+ ['L10','Swiss Index','minimal · numbered · tabular','Extreme restraint: a numbered index of every story, an LA-by-value table and tabular market data. The "index of the market" — nothing wasted.'],
+];
+document.getElementById('grid').innerHTML=L.map(([id,name,style,desc])=>`
+ <div class="cardw">
+ <div class="cap"><span class="id">${id}</span><h3>${name}</h3><span class="style">${style}</span></div>
+ <div class="desc">${desc}</div>
+ <div class="framew"><iframe src="${id}.html" loading="lazy" scrolling="no"></iframe><a class="open" href="${id}.html" target="_blank">Open ${id} full →</a></div>
+ </div>`).join('');
+</script>
+</body></html>
← f7a39cc6 auto-save: 2026-07-22T13:14:23 (3 files) — data/markets.json
·
back to Rentv
·
auto-save: 2026-07-22T13:44:34 (3 files) — data/markets.json 5a1f12a0 →