← back to Rentv 2026
public/index.html
416 lines
<!doctype html><html lang="en"><head>
<script>
// Credential-safe fetch guard (fleet drop-in) — if this page is opened with
// credentials in the URL (a saved bookmark / Chrome-remembered basic-auth:
// https://user:pass@host/…), the browser poisons document.baseURI, so any
// relative or root-relative fetch('/api/…') throws "Request cannot be constructed
// from a URL that includes credentials" and callers silently fall to an empty
// state. Resolve every non-absolute request URL against the credential-free
// location instead of baseURI. Placed first so it wraps window.fetch before any
// app script runs. Ref: creds-in-url-fetch-guard-fleet-pattern.
(function () {
var _fetch = window.fetch.bind(window);
var cleanBase = function () { return location.origin + location.pathname; };
window.fetch = function (input, init) {
try {
if (typeof input === 'string' && !/^[a-z]+:\/\//i.test(input) && input.indexOf('//') !== 0) {
input = new URL(input, cleanBase()).href;
} else if (input instanceof Request && !/^[a-z]+:\/\//i.test(input.url)) {
input = new Request(new URL(input.url, cleanBase()).href, input);
}
} catch (_) { /* fall through to native */ }
return _fetch(input, init);
};
})();
</script>
<meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
<title>RENTV.com — Commercial Real Estate News · Western U.S.</title>
<meta name="description" content="RENTV.com — commercial real estate news, deals, financing, development and conferences across the Western United States since 1998.">
<link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,700;0,800;1,600&family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
<style>
:root{--red:#c8102e;--ink:#14171a;--sub:#5b636b;--line:#e4e7ea;--wash:#f5f6f8;--up:#0f8a4d;--dn:#c8102e}
*{box-sizing:border-box;margin:0}
body{font-family:'Inter',system-ui,sans-serif;color:var(--ink);background:#fbfbfc;line-height:1.45}
a{color:inherit;text-decoration:none}
.top{position:sticky;top:0;z-index:900;display:flex;align-items:center;justify-content:space-between;height:54px;padding:0 16px;background:#fff;border-bottom:1px solid var(--line)}
.burger{display:inline-flex;align-items:center;gap:10px;border:0;background:none;cursor:pointer;font:inherit;color:inherit;padding:7px 9px;border-radius:8px}
.burger:hover{background:rgba(0,0,0,.06)}
.burger .lbl{font-size:12px;font-weight:800;letter-spacing:.14em;text-transform:uppercase;color:var(--sub)}
.brand{position:absolute;left:50%;transform:translateX(-50%);font-family:'Playfair Display',Georgia,serif;font-weight:800;font-size:25px;letter-spacing:.02em}.brand span{color:var(--red)}
.user{width:37px;height:37px;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)}
.user:hover{border-color:var(--red);color:var(--red)}
.scrim{position:fixed;inset:0;background:rgba(6,9,12,.5);opacity:0;pointer-events:none;transition:opacity .22s;z-index:950}
.scrim.open{opacity:1;pointer-events:auto}
.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:951;display:flex;flex-direction:column;overflow-y:auto;box-shadow:2px 0 40px rgba(0,0,0,.4)}
.drawer.open{transform:none}
/* RIGHT-corner drawer: mirror of the left drawer, slides in from the right (Sections). */
.drawer.right{left:auto;right:0;transform:translateX(102%);box-shadow:-2px 0 40px rgba(0,0,0,.4)}
.drawer.right.open{transform:none}
.rightnav{display:inline-flex;align-items:center;gap:6px}
.drawer .dh{display:flex;align-items:center;justify-content:space-between;padding:16px 18px;border-bottom:1px solid #1c232b}
.drawer .dh .w{font-family:'Playfair Display',serif;font-size:23px;font-weight:800}.drawer .dh .w span{color:var(--red)}
.drawer .dh button{background:none;border:0;color:#8b949e;font-size:22px;cursor:pointer;line-height:1}
.snap{padding:12px 18px;border-bottom:1px solid #1c232b;display:grid;grid-template-columns:1fr 1fr;gap:10px}
.snap .m{background:#11161c;border:1px solid #1c232b;border-radius:8px;padding:8px 10px}
.snap .m .k{font-size:10px;letter-spacing:.06em;text-transform:uppercase;color:#7b848d}.snap .m .v{font-size:15px;font-weight:800;margin-top:2px}.snap .m .v.up{color:#37d67a}.snap .m .v.dn{color:#ff6b6b}
/* Collapsible group ("module"): the .gt title is a click header; the <nav> after it
is the panel. Default = COLLAPSED (all modules closed on load) via sibling adjacency,
so even the Regions group rentv-nav.js injects async starts closed with no JS timing. */
.grp{padding:14px 18px 4px;display:flex;align-items:center;justify-content:space-between;cursor:pointer;user-select:none}
.grp .gt{font-size:10px;letter-spacing:.16em;text-transform:uppercase;color:#6b747d;margin-bottom:0}
.grp:hover .gt{color:#aeb6bd}
.grp::after{content:'▸';color:#6b747d;font-size:12px;line-height:1;transition:transform .18s;flex:none}
.grp.open::after{transform:rotate(90deg)}
.drawer .grp + nav{display:none}
.drawer .grp.open + nav{display:block}
/* .snap is a grid module — collapse it under its own header, restore grid (not block) when open. */
.drawer .grp + .snap{display:none}
.drawer .grp.open + .snap{display:grid}
.drawer nav a{display:block;padding:9px 18px;color:#dbe1e7;font-size:14.5px;border-left:3px solid transparent}
.drawer nav a:hover{background:#141a21;border-left-color:var(--red);color:#fff}
.drawer nav a.active{background:#141a21;border-left-color:var(--red);color:#fff;font-weight:700}
.sechat{display:none;align-items:baseline;gap:14px;flex-wrap:wrap;padding:20px 0 4px;border-bottom:1px solid #e8e4e0;margin-bottom:4px}
.sechat.on{display:flex}
.sechat h1{font-family:'Playfair Display',serif;font-size:clamp(30px,3vw,42px);font-weight:700;letter-spacing:-.01em}
.sechat .ct{color:var(--sub);font-size:13px;font-weight:600}
.sechat .all{margin-left:auto;color:var(--red);font-weight:800;font-size:12px;letter-spacing:.06em;text-transform:uppercase}
.sechat .all:hover{text-decoration:underline}
.empty{padding:60px 0 40px;text-align:center;color:var(--sub)}
.empty h3{font-family:'Playfair Display',serif;font-size:26px;color:var(--ink);margin-bottom:8px}
.empty a{color:var(--red);font-weight:800}
.drawer .df{margin-top:auto;padding:16px 18px;border-top:1px solid #1c232b;font-size:11px;color:#6b747d}
.datastrip{border-bottom:1px solid #e8e4e0;background:#f7f5f2}
.datastrip .in{max-width:1240px;margin:0 auto;display:flex;gap:34px;padding:9px 22px;font-size:11px;font-weight:500;letter-spacing:.06em;color:#4a4f55;overflow:hidden;white-space:nowrap}
.datastrip .m b{color:#14171a;font-weight:700;margin-left:6px}.datastrip .m b.dn{color:var(--red)}.datastrip .m b.up{color:var(--up)}
.datastrip .mp-link{margin-left:auto;white-space:nowrap;color:var(--red);font-weight:800;letter-spacing:.08em;text-transform:uppercase;font-size:10.5px;flex:none}
.datastrip .mp-link:hover{text-decoration:underline}
.fold{min-height:calc(100vh - 54px);display:flex;flex-direction:column}
.wrap{max-width:1240px;margin:0 auto;padding:0 22px;width:100%}
.hero{flex:1;display:grid;grid-template-columns:1.55fr 1fr;gap:34px;align-items:center;padding:28px 0}
.lead{padding-right:34px;border-right:1px solid #e2ded9}
.lead .im{display:block;height:380px;border-radius:6px;margin-bottom:16px;background:linear-gradient(135deg,#1f2a37,#c8102e) center/cover;box-shadow:inset 0 -90px 70px -50px rgba(0,0,0,.35)}
.lead .tag{color:var(--red);font-weight:800;font-size:12px;letter-spacing:.14em;text-transform:uppercase}
.lead h1{font-family:'Playfair Display',serif;font-size:clamp(44px,3.9vw,62px);line-height:1.08;margin:9px 0 12px;font-weight:700;letter-spacing:-.01em}
.lead .byl{color:#6b7178;font-size:13px}
.chipo{display:inline-block;background:var(--red);color:#fff;font-size:9px;font-weight:800;text-transform:uppercase;letter-spacing:.05em;padding:1px 6px;border-radius:8px;vertical-align:middle;margin-left:6px}
.aside{display:flex;flex-direction:column;gap:0}
.aside a{display:grid;grid-template-columns:80px 1fr;gap:13px;border-bottom:1px solid #e8e4e0;padding:13px 0}
.aside a:first-child{padding-top:0}
.aside .im{height:58px;border-radius:5px;background:linear-gradient(135deg,#1f2a37,#c8102e) center/cover;display:block}
.aside .tag{font-size:9px;font-weight:800;letter-spacing:.1em;text-transform:uppercase}.aside h4{font:600 14px/1.32 'Inter';margin-top:3px}
/* Featured video hero — Boomer's CRE Talk, front & center above the news hero. */
.vhero{background:#0b0e12;border-bottom:3px solid var(--red);padding:26px 0}
.vhero .in{max-width:1080px;margin:0 auto;padding:0 22px}
.vhero .eyebrow{color:var(--red);font-weight:800;font-size:12px;letter-spacing:.16em;text-transform:uppercase;margin-bottom:12px}
.vhero .fr{position:relative;padding-top:56.25%;border-radius:12px;overflow:hidden;background:#05080c;box-shadow:0 14px 44px rgba(0,0,0,.45)}
.vhero .fr iframe{position:absolute;inset:0;width:100%;height:100%;border:0}
.vhero .cap{display:flex;align-items:baseline;justify-content:space-between;gap:14px;flex-wrap:wrap;margin-top:13px}
.vhero .cap h2{font-family:'Playfair Display',serif;color:#fff;font-size:clamp(20px,2.2vw,26px);font-weight:700;letter-spacing:-.01em}
.vhero .cap span{color:#8b949e;font-size:13px;font-weight:600;white-space:nowrap}
.breaking{background:#0b0e12;color:#fff;border-top:3px solid var(--red)}
.breaking .in{max-width:1240px;margin:0 auto;display:flex;align-items:stretch;height:52px}
.breaking .lbl{background:var(--red);display:flex;align-items:center;gap:9px;padding:0 20px;font-weight:800;font-size:12px;letter-spacing:.18em;text-transform:uppercase;white-space:nowrap;flex:none}
.breaking .lbl .dot{width:8px;height:8px;border-radius:50%;background:#fff;animation:bp 1.1s infinite}@keyframes bp{50%{opacity:.3;transform:scale(.8)}}
.ticker{position:relative;overflow:hidden;flex:1}
.ticker .track{display:flex;align-items:center;height:100%;white-space:nowrap;animation:bs 52s linear infinite;will-change:transform}
.ticker:hover .track{animation-play-state:paused}
@keyframes bs{from{transform:translateX(0)}to{transform:translateX(-50%)}}
.ticker .it{display:inline-flex;align-items:center;gap:11px;padding:0 24px;height:100%;color:#c3cbd3;font-size:14px;font-weight:600;cursor:pointer;border-right:1px solid #1a2129;transition:background .14s,color .14s}
.ticker .it .c{font-size:10px;font-weight:800;letter-spacing:.06em;text-transform:uppercase;padding:2px 8px;border-radius:20px;background:#161d25;color:#8b939b}
.ticker .it:hover{background:rgba(200,16,46,.16);color:#fff}.ticker .it:hover .c{background:var(--red);color:#fff}
.ticker .it.sel{background:var(--red);color:#fff}.ticker .it.sel .c{background:#fff;color:var(--red)}
.ticker .paused{position:absolute;right:16px;top:50%;transform:translateY(-50%);z-index:3;font-size:9px;font-weight:700;letter-spacing:.16em;color:rgba(255,255,255,.5);opacity:0;transition:opacity .3s;pointer-events:none}
.ticker:hover .paused{opacity:1}
.ticker .fade{position:absolute;top:0;bottom:0;width:64px;pointer-events:none;z-index:2}
.ticker .fade.l{left:0;background:linear-gradient(90deg,#0b0e12,transparent)}.ticker .fade.r{right:0;background:linear-gradient(270deg,#0b0e12,transparent)}
.selbar{max-width:1240px;margin:0 auto;display:none;padding:12px 22px;background:#11161c;color:#e7ecf1;border-top:1px solid #1c232b;align-items:center;gap:14px}
.selbar.on{display:flex}.selbar .c{font-size:10px;font-weight:800;text-transform:uppercase;letter-spacing:.05em;background:var(--red);padding:3px 9px;border-radius:20px}
.selbar a{color:#fff;font-weight:700}.selbar a:hover{text-decoration:underline}.selbar .x{margin-left:auto;background:none;border:0;color:#8b939b;cursor:pointer;font-size:18px}
.sec{padding:34px 0}
h2.rule{font-size:13px;text-transform:uppercase;letter-spacing:.09em;border-top:2px solid var(--ink);padding-top:10px;margin-bottom:16px;font-weight:800}
.river{display:grid;grid-template-columns:repeat(4,1fr);gap:18px}
.river .im{height:130px;border-radius:5px;background:linear-gradient(135deg,#1f2a37,#c8102e) center/cover;display:block;margin-bottom:8px}
.river .tag{font-size:10px;font-weight:800;text-transform:uppercase}.river h4{font:600 15px/1.25 'Inter';margin-top:3px}
.cta{background:var(--red);color:#fff;border-radius:10px;padding:26px;display:flex;gap:16px;align-items:center;flex-wrap:wrap;justify-content:center;margin:26px 0 40px}
.cta h3{font-family:'Playfair Display',serif;font-size:23px}
.cta input{flex:1;min-width:240px;max-width:340px;border:0;border-radius:6px;padding:12px 14px;font:inherit}
.cta button{background:#0b0e12;color:#fff;border:0;border-radius:6px;padding:12px 22px;font-weight:800;cursor:pointer}
.cta .ok{display:none;font-weight:700}
footer{background:#0b0e12;color:#8b939b;padding:30px 0;font-size:12px}
footer .cols{max-width:1240px;margin:0 auto;padding:0 22px;display:grid;grid-template-columns:2fr 1fr 1fr 1fr 1fr;gap:20px}
footer h5{color:#e7ecf1;font-size:12px;text-transform:uppercase;letter-spacing:.06em;margin-bottom:8px}
footer a{display:block;color:#9aa4ad;padding:3px 0}footer .logo{font-family:'Playfair Display',serif;font-size:22px;font-weight:800;color:#fff}footer .logo span{color:var(--red)}
.fine{max-width:1240px;margin:18px auto 0;padding:14px 22px 0;border-top:1px solid #1c232b;color:#6b747d}
@media(max-width:900px){.hero,.river,footer .cols{grid-template-columns:1fr}.lead{padding-right:0;border-right:0}.river{grid-template-columns:1fr 1fr}}
/* ---------- href-to-deeper-data: category drill chips (facet → ?cat=) ---------- */
/* In-card cat values are drill atoms, not nested <a> (cards are already article links). */
.drill{cursor:pointer;border:0;background:none;font:inherit;color:inherit}
.drill:hover{text-decoration:underline}
/* Ticker cat chip drills the feed; keep it visually a chip but clickable to filter. */
.ticker .it .c.drill:hover{background:var(--red);color:#fff;text-decoration:none}
/* ---------- CRE Market Pulse embed (dark rates terminal on the light homepage) ---------- */
.mp{background:linear-gradient(180deg,#0b0f16,#080b11);border:1px solid #1c2536;border-radius:14px;color:#e8edf5;padding:22px 22px 20px;margin:8px 0 6px;
--mp-mono:"SFMono-Regular",ui-monospace,"JetBrains Mono","Menlo",Consolas,monospace}
.mp .mp-top{display:flex;align-items:flex-end;justify-content:space-between;gap:14px;flex-wrap:wrap;margin-bottom:16px}
.mp .mp-k{color:#f0b429;font-weight:800;font-size:11px;letter-spacing:.16em;text-transform:uppercase}
.mp .mp-h{font-family:'Playfair Display',serif;font-size:clamp(22px,2.4vw,30px);font-weight:700;letter-spacing:-.01em;margin-top:3px}
.mp .mp-sub{color:#8a97ad;font-size:12px;margin-top:2px}
.mp .mp-view{color:#38bdf8;font-weight:800;font-size:12px;letter-spacing:.06em;text-transform:uppercase;white-space:nowrap}
.mp .mp-view:hover{text-decoration:underline}
.mp .mp-kpis{display:grid;grid-template-columns:repeat(6,1fr);gap:10px}
.mp .mp-kpi{background:linear-gradient(180deg,#111826,#0e131c);border:1px solid #1c2536;border-radius:10px;padding:12px 13px;position:relative;overflow:hidden}
.mp .mp-kpi::after{content:"";position:absolute;left:0;top:0;bottom:0;width:3px;background:#38bdf8;opacity:.5}
.mp .mp-kpi.up::after{background:#22c55e}.mp .mp-kpi.down::after{background:#f04141}.mp .mp-kpi.flat::after{background:#8a97ad}
.mp .mp-kpi .l{font-size:10px;letter-spacing:1.1px;text-transform:uppercase;color:#5b6879}
.mp .mp-kpi .v{font-family:var(--mp-mono);font-size:21px;font-weight:600;margin-top:6px;font-variant-numeric:tabular-nums}
.mp .mp-kpi .d{margin-top:6px;display:inline-flex;align-items:center;gap:4px;font-family:var(--mp-mono);font-size:11px;padding:2px 7px;border-radius:6px;font-variant-numeric:tabular-nums}
.mp .mp-kpi.up .d{color:#22c55e;background:rgba(34,197,94,.14)}.mp .mp-kpi.down .d{color:#f04141;background:rgba(240,65,65,.14)}.mp .mp-kpi.flat .d{color:#8a97ad;background:rgba(138,151,173,.12)}
.mp .mp-funds{display:flex;gap:26px;flex-wrap:wrap;margin-top:16px;padding-top:14px;border-top:1px solid #161d2b}
.mp .mp-fund .fl{font-size:10px;letter-spacing:1px;text-transform:uppercase;color:#5b6879}
.mp .mp-fund .fv{font-family:var(--mp-mono);font-size:19px;font-weight:600;color:#f0b429;margin-top:3px}
.mp .mp-fund .fm{font-size:11px;color:#8a97ad;margin-top:1px}
@media(max-width:900px){.mp .mp-kpis{grid-template-columns:repeat(3,1fr)}}
@media(max-width:560px){.mp .mp-kpis{grid-template-columns:repeat(2,1fr)}}
</style></head>
<body>
<header class="top">
<button class="burger" id="burger" aria-label="Explore 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">Explore</span></button>
<a class="brand" href="/">REN<span>TV</span></a>
<div class="rightnav">
<button class="user" id="user" 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>
<button class="burger" id="burgerR" aria-label="Sections, regions & market snapshot menu"><span class="lbl">Sections</span><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></button>
</div>
</header>
<div class="scrim" id="scrim"></div>
<!-- LEFT drawer (upper-left hamburger): Explore — discover, intelligence, company.
Regions + Market Snapshot now live in the RIGHT drawer (see below). -->
<aside class="drawer" id="drawer">
<div class="dh"><span class="w">REN<span>TV</span></span><button id="dclose" aria-label="Close">✕</button></div>
<div class="grp"><div class="gt">Discover</div></div>
<nav><a href="/hub" style="font-weight:700">✨ Intelligence Hub — all tools</a><a href="/search">🔎 Search</a><a href="/market-pulse">📈 Rates & Capital Markets</a><a href="/pulse">📊 Market Pulse</a><a href="/sector">🏙️ Sector Deep-Dives</a><a href="/metros">📍 Deal Flow by Metro</a><a href="/tools">🧮 CRE Toolkit</a><a href="/watchlist">⭐ Deal Watchlist</a><a href="/directory">🏢 CRE Directory</a><a href="/services">🛠️ CRE Services</a><a href="/compare">⚖️ Deal Comparison</a><a href="/brief">📰 The RENTV Brief</a><a href="/syndicate">📡 Syndicate</a></nav>
<div class="grp"><div class="gt">Intelligence</div></div>
<nav><a href="/deals">Deals</a><a href="/markets">Markets</a><a href="/la-commercial">LA Commercial</a><a href="/desk">Broker & Owner Desk</a><a href="/review">The REview</a><a href="/cre-talk">CRE Talk</a><a href="/conferences">Conferences</a><a href="/blog">Blog</a></nav>
<div class="grp"><div class="gt">Company</div></div>
<nav><a href="/about">About</a><a href="/advertise">Advertise</a><a href="/subscribe">Subscribe / Newsletters</a></nav>
<div class="df">RENTV.com — Commercial Real Estate News across the Western U.S. since 1998.</div>
</aside>
<!-- RIGHT drawer (upper-right hamburger): Sections (property-type axis) + Regions
(JS-injected by rentv-nav.js at [data-regions-anchor]) + Market Snapshot. -->
<aside class="drawer right" id="drawerR" data-own-nav data-regions>
<div class="dh"><span class="w">Sections</span><button id="dcloseR" aria-label="Close">✕</button></div>
<div class="grp"><div class="gt">By Property Type</div></div>
<nav><a href="/closings">Closings</a><a href="/sales">Sales</a><a href="/leases">Leases</a><a href="/financing">Financing</a><a href="/development">Development</a><a href="/multifamily">Multifamily</a><a href="/industrial">Industrial</a><a href="/retail">Retail</a></nav>
<!-- Regions group (rt-drawer-regions) is injected here by rentv-nav.js buildDrawerGroup(),
inserted before this [data-regions-anchor] so it lands under Sections, above Market Snapshot. -->
<div data-regions-anchor></div>
<div class="grp"><div class="gt">Market Snapshot</div></div>
<div class="snap" id="snap"></div>
<div class="df">RENTV.com — Commercial Real Estate News across the Western U.S. since 1998.</div>
</aside>
<div class="fold">
<div class="datastrip"><div class="in" id="datastrip"></div></div>
<!-- Featured video — Boomer's CRE Talk, front & center. STATIC block: the wire render
loop only rewrites #lead/#aside/#river/#track, never this, so the video persists. -->
<section class="vhero">
<div class="in">
<div class="eyebrow">▶ RENTV CRE Talk · Featured</div>
<div class="fr"><iframe id="vhero-frame" src="https://player.vimeo.com/video/1200839794?title=0&byline=0&portrait=0&dnt=1" title="CRE Talk Episode #22 with Bill Shopoff" allow="autoplay; fullscreen; picture-in-picture" allowfullscreen loading="lazy"></iframe></div>
<div class="cap"><h2 id="vhero-cap">CRE Talk Episode #22 — Bill Shopoff</h2><span>Hosted by Boomer · RENTV.com</span></div>
</div>
</section>
<main class="wrap"><div class="sechat" id="sechat"></div><section class="hero"><div class="lead" id="lead"></div><div class="aside" id="aside"></div></section></main>
<div class="selbar" id="selbar"></div>
<div class="breaking"><div class="in"><div class="lbl"><span class="dot"></span> Breaking</div>
<div class="ticker" id="ticker"><div class="fade l"></div><div class="fade r"></div><span class="paused">Paused · hover to browse</span><div class="track" id="track"></div></div></div></div>
</div>
<main class="wrap">
<section class="sec" style="padding-bottom:8px">
<style>
.vidrow{display:grid;grid-auto-flow:column;grid-auto-columns:minmax(280px,1fr);gap:16px;overflow-x:auto;scroll-snap-type:x mandatory;padding:4px 2px 14px;-webkit-overflow-scrolling:touch}
.vidrow::-webkit-scrollbar{height:8px}.vidrow::-webkit-scrollbar-thumb{background:#c8102e;border-radius:4px}
.vidcard{scroll-snap-align:start;position:relative;border-radius:12px;overflow:hidden;background:#0b1017;border:1px solid #1c2941}
.vidcard video{width:100%;aspect-ratio:1/1;object-fit:cover;display:block;background:#05080c}
.vc-badge{position:absolute;top:10px;left:10px;background:#c8102e;color:#fff;font-weight:800;font-size:11px;letter-spacing:.14em;padding:4px 9px;border-radius:4px;pointer-events:none}
.vc-title{padding:10px 12px;font-size:14px;font-weight:700;line-height:1.25;color:#0b1017}
.vidhead{display:flex;align-items:baseline;justify-content:space-between;gap:12px}
.vidhead a{font-size:13px;font-weight:600;color:#c8102e;text-decoration:none;white-space:nowrap}
</style>
<div class="vidhead"><h2 class="rule" style="margin-bottom:0">Deal Wire · Video</h2><a href="/press">Full video library →</a></div>
<div class="vidrow">
<div class="vidcard"><span class="vc-badge">RENTV NEWS</span><video preload="none" playsinline controls poster="/vid/news-wire/news12.jpg" src="/vid/news-wire/news12.mp4"></video><div class="vc-title">Santa Monica Multifamily Fetches $928k Per Unit</div></div>
<div class="vidcard"><span class="vc-badge">RENTV NEWS</span><video preload="none" playsinline controls poster="/vid/news-wire/news11.jpg" src="/vid/news-wire/news11.mp4"></video><div class="vc-title">Longpoint Closes $34.6M Arizona Industrial Buy</div></div>
<div class="vidcard"><span class="vc-badge">RENTV NEWS</span><video preload="none" playsinline controls poster="/vid/news-wire/news0.jpg" src="/vid/news-wire/news0.mp4"></video><div class="vc-title">$81M Refi — Denver-Area Multifamily</div></div>
<div class="vidcard"><span class="vc-badge">RENTV NEWS</span><video preload="none" playsinline controls poster="/vid/news-wire/news1.jpg" src="/vid/news-wire/news1.mp4"></video><div class="vc-title">Washington Industrial Sells for $174M</div></div>
<div class="vidcard"><span class="vc-badge">RENTV NEWS</span><video preload="none" playsinline controls poster="/vid/news-wire/news2.jpg" src="/vid/news-wire/news2.mp4"></video><div class="vc-title">Granite Towers Buys 229-Unit Texas Multifamily</div></div>
<div class="vidcard"><span class="vc-badge">RENTV NEWS</span><video preload="none" playsinline controls poster="/vid/news-wire/news3.jpg" src="/vid/news-wire/news3.mp4"></video><div class="vc-title">SFF Realty Buys 104k sf Silicon Valley Office/R&D</div></div>
<div class="vidcard"><span class="vc-badge">RENTV NEWS</span><video preload="none" playsinline controls poster="/vid/news-wire/news4.jpg" src="/vid/news-wire/news4.mp4"></video><div class="vc-title">Swig Company Refis SF Office High-Rise</div></div>
<div class="vidcard"><span class="vc-badge">RENTV NEWS</span><video preload="none" playsinline controls poster="/vid/news-wire/news5.jpg" src="/vid/news-wire/news5.mp4"></video><div class="vc-title">Tailwind Spends $65M on Two Sacramento Properties</div></div>
<div class="vidcard"><span class="vc-badge">RENTV NEWS</span><video preload="none" playsinline controls poster="/vid/news-wire/news6.jpg" src="/vid/news-wire/news6.mp4"></video><div class="vc-title">Oxnard Senior-Living Facility Lands New Owner</div></div>
<div class="vidcard"><span class="vc-badge">RENTV NEWS</span><video preload="none" playsinline controls poster="/vid/news-wire/news7.jpg" src="/vid/news-wire/news7.mp4"></video><div class="vc-title">Velo3D Leases 289k sf Bay Area Manufacturing Space</div></div>
<div class="vidcard"><span class="vc-badge">RENTV NEWS</span><video preload="none" playsinline controls poster="/vid/news-wire/news8.jpg" src="/vid/news-wire/news8.mp4"></video><div class="vc-title">Fresno Multifamily Sale Hits $177k Per Unit</div></div>
<div class="vidcard"><span class="vc-badge">RENTV NEWS</span><video preload="none" playsinline controls poster="/vid/news-wire/news9.jpg" src="/vid/news-wire/news9.mp4"></video><div class="vc-title">Gilbert, AZ Multifamily Refinanced for $51.8M</div></div>
<div class="vidcard"><span class="vc-badge">RENTV NEWS</span><video preload="none" playsinline controls poster="/vid/news-wire/news10.jpg" src="/vid/news-wire/news10.mp4"></video><div class="vc-title">Downtown Long Beach Retail Portfolio Fetches $30M</div></div>
</div>
</section>
<section class="sec" style="padding-bottom:0">
<a class="mp" id="mpEmbed" href="/market-pulse" style="display:block">
<div class="mp-top">
<div>
<div class="mp-k">CRE Market Pulse</div>
<div class="mp-h">Rates & Capital Markets</div>
<div class="mp-sub">U.S. Treasury curve · credit spreads · SoCal fundamentals — live</div>
</div>
<span class="mp-view">View full pulse →</span>
</div>
<div class="mp-kpis" id="mpKpis"></div>
<div class="mp-funds" id="mpFunds"></div>
</a>
</section>
<section class="sec"><h2 class="rule">Across the market</h2><div class="river" id="river"></div></section>
<div class="cta"><h3>The morning CRE briefing, in your inbox.</h3><input id="email" type="email" placeholder="you@company.com"><button id="subbtn">Subscribe</button><span class="ok" id="subok">✓ You're in.</span></div>
</main>
<footer><div class="cols">
<div><div class="logo">REN<span>TV</span>.com</div><p style="max-width:34ch;margin-top:8px">Commercial real estate news, deals & conferences across the Western United States since 1998.</p></div>
<div><h5>Sections</h5><a href="/sales">Sales</a><a href="/leases">Leases</a><a href="/financing">Financing</a><a href="/development">Development</a><a href="/la-commercial">LA Commercial</a></div>
<div><h5>Intelligence</h5><a href="/markets">Markets</a><a href="/review">The REview</a><a href="/cre-talk">CRE Talk</a><a href="/conferences">Conferences</a><a href="/blog">Blog</a></div>
<div><h5>Company</h5><a href="/about">About</a><a href="/advertise">Advertise</a><a href="/subscribe">Subscribe</a></div>
<div><h5>Follow</h5><a href="https://www.instagram.com/therentv.com_/" target="_blank" rel="noopener noreferrer">Instagram</a><a href="https://www.facebook.com/Rentv.comInc/" target="_blank" rel="noopener noreferrer">Facebook</a><a href="https://www.linkedin.com/company/rentv.com-inc" target="_blank" rel="noopener noreferrer">LinkedIn</a></div>
</div><div class="fine">© 2026 RENTV.com · Headlines refreshed live from rentv.com · <span id="src"></span></div></footer>
<script src="/drill.js"></script>
<script src="/rentv-nav.js" defer></script>
<script>
const $=s=>document.getElementById(s), esc=s=>(s||'').replace(/[&<>"]/g,c=>({'&':'&','<':'<','>':'>','"':'"'}[c]));
const bg=o=>(o&&o.image)?(' style="background-image:url(\''+esc(o.image)+'\')"'):'';
const LK=x=>(x.orig?'/post/':'/news/')+encodeURIComponent(x.id);
// ── href-to-deeper-data adapter (drill.js atom contract, adapted to this app's single-facet
// ?cat= server model). The category is already a URL-addressable facet: the drawer links
// /sales, /development … 301 to /?cat=Sales, and load() filters ALL by the ?cat= value.
// So every DISPLAYED cat value becomes a real drill to that filtered feed. Cards are already
// <a href="/news/…"> article links → the in-card cat chip is a <span class="drill"> (delegated,
// stopPropagation), never a nested <a>. drill.js's qstr/readURL/writeURL are file-scope over a
// multi-field FIELDS/FIL model; here the single facet is `cat`, so we keep the atom's
// data-filter="cat"/data-v contract but drive it through the existing ?cat= URL. ──
const catURL=v=>(v?('?cat='+encodeURIComponent(v)):location.pathname);
// catChip(value) → a <span> drill atom (used INSIDE <a> cards); clicking filters the feed to that
// category and reflects into the URL (deep-link/back-button). Falls back to '' for a blank cat.
const catChip=(v,cls,label)=>{ if(v==null||v==='')return esc(label!=null?label:'');
return `<span class="drill ${cls||''}" data-filter="cat" data-v="${esc(v)}" role="link" tabindex="0" title="Filter to ${esc(v)}">${esc(label!=null?label:v)}</span>`; };
// Delegated: a cat drill atom sets the ?cat= facet (full-page nav to the filtered feed). Because
// the atom lives inside an <a> article card, stopPropagation + preventDefault keep the card link
// from also firing. A cmd/ctrl click still opens the filtered view in this tab (no nested <a>).
document.addEventListener('click',e=>{ const d=e.target.closest('.drill[data-filter="cat"]'); if(!d)return;
e.preventDefault(); e.stopPropagation(); const v=d.dataset.v||'';
const cur=(new URLSearchParams(location.search).get('cat')||'');
// toggle: clicking the currently-active category clears back to the full homepage
location.href=(cur.toLowerCase()===v.toLowerCase())?location.pathname:catURL(v);
});
document.addEventListener('keydown',e=>{ if(e.key!=='Enter'&&e.key!==' ')return;
const d=e.target.closest&&e.target.closest('.drill[data-filter="cat"]'); if(!d)return;
e.preventDefault(); e.stopPropagation(); location.href=catURL(d.dataset.v||''); });
const openDrawer=id=>{$('scrim').classList.add('open');$(id).classList.add('open');};
const closeDrawer=()=>{$('scrim').classList.remove('open');$('drawer').classList.remove('open');$('drawerR').classList.remove('open');};
$('burger').onclick=()=>openDrawer('drawer'); // upper-LEFT ham → Explore + Regions
$('burgerR').onclick=()=>openDrawer('drawerR'); // upper-RIGHT ham → Sections
$('dclose').onclick=closeDrawer;$('dcloseR').onclick=closeDrawer;$('scrim').onclick=closeDrawer;
$('user').onclick=()=>openDrawer('drawerR');
document.addEventListener('keydown',e=>{if(e.key==='Escape')closeDrawer();});
// Accordion: every group ("module") in both drawers starts COLLAPSED (CSS default);
// clicking its title toggles it. Delegated on each .drawer so the Regions group that
// rentv-nav.js injects after load is clickable too, with no extra wiring.
['drawer','drawerR'].forEach(id=>{const dr=$(id);if(!dr)return;dr.addEventListener('click',e=>{
const g=e.target.closest('.grp'); if(g&&dr.contains(g)) g.classList.toggle('open');
});});
async function jget(url){try{const r=await fetch(url,{credentials:'same-origin'});if(!r.ok)return null;return await r.json();}catch(e){return null;}}
let ALL=[];
// Canonical section labels — the drawer/footer link to /sales, /development, … which the
// server 301s to /?cat=Sales, /?cat=Development, … Match the exact cat tags in the live feed.
const SECTIONS={sales:'Sales',leases:'Leases',financing:'Financing',development:'Development',multifamily:'Multifamily',industrial:'Industrial',retail:'Retail'};
const rawCat=(new URLSearchParams(location.search).get('cat')||'').trim();
const CAT=rawCat?(SECTIONS[rawCat.toLowerCase()]||rawCat):''; // canonical label, or '' for the full homepage
if(CAT){
document.title=CAT+' — RENTV.com Commercial Real Estate News';
// Highlight the matching section in the drawer so the open section is obvious.
document.querySelectorAll('.drawer nav a').forEach(a=>{
if((a.getAttribute('href')||'').toLowerCase()==='/'+CAT.toLowerCase()) a.classList.add('active');
});
}
async function load(){
const [d,pd]=await Promise.all([jget('/api/news'),jget('/api/posts?status=published')]);
const news=(d&&d.items)||[], orig=((pd&&pd.items)||[]).map(p=>({id:p.id,title:p.title,cat:p.cat,image:p.image,author:p.author,orig:true}));
ALL=orig.concat(news);
// A "section" IS the live feed filtered to one category (server-side model in server.js). When
// ?cat= is set we render only that slice — that's what makes each hamburger link show DIFFERENT info.
const view=CAT?ALL.filter(x=>x&&String(x.cat||'').toLowerCase()===CAT.toLowerCase()):ALL;
const sh=$('sechat');
if(CAT&&sh){
sh.innerHTML=`<h1>${esc(CAT)}</h1><span class="ct">${view.length} ${view.length===1?'story':'stories'}</span><a class="all" href="/">All news ↗</a>`;
sh.classList.add('on');
}
if(!view.length){
// Either the wire is empty (homepage) or this section has no current stories.
$('lead').innerHTML='';$('aside').innerHTML='';$('river').innerHTML='';$('track').innerHTML='';
if(CAT){$('lead').innerHTML=`<div class="empty"><h3>No current ${esc(CAT)} stories</h3><div>This section refreshes from the live wire. <a href="/">Browse all news →</a></div></div>`;}
else{$('lead').innerHTML='<div class="tag">RENTV.com</div><h1>Live commercial real estate news, refreshing…</h1><div class="byl">If this persists, the wire is briefly unavailable — please reload.</div>';}
return;
}
// lead with the top story that HAS an image (never a blank-image placeholder); keep the rest in order
// Always feature the LATEST article in the hero — the newest wire story (news is newest-first),
// not a published Original or the first-item-that-happens-to-have-an-image.
const lead = (!CAT && news[0]) ? news[0] : view[0];
const rest = view.filter(x=>x!==lead);
const l=lead;
$('lead').innerHTML=`<div class="im"${bg(l)}></div><span class="tag">${l.cat?catChip(l.cat):'RENTV'}${l.orig?'<span class="chipo">Original</span>':''}</span><h1>${esc(l.title)}</h1><div class="byl">${l.orig?'By '+esc(l.author||'RENTV Staff')+' · RENTV Original':'By RENTV Staff · live from rentv.com'}</div>`;
$('lead').style.cursor='pointer';$('lead').onclick=()=>location.href=LK(l);
$('aside').innerHTML=rest.slice(0,4).map(n=>`<a href="${LK(n)}"><div class="im"${bg(n)}></div><div><span class="tag" style="color:var(--red)">${catChip(n.cat,'','')||''}</span><h4>${esc(n.title)}</h4></div></a>`).join('');
const items=view.slice(0,16), mk=n=>`<a class="it" data-id="${esc(n.id)}" data-cat="${esc(n.cat||'')}" data-title="${esc(n.title)}" data-href="${LK(n)}"><span class="c ${n.cat?'drill':''}"${n.cat?` data-filter="cat" data-v="${esc(n.cat)}" role="link" tabindex="0" title="Filter to ${esc(n.cat)}"`:''}>${esc(n.cat||'')}</span>${esc(n.title)}</a>`;
$('track').innerHTML=items.map(mk).join('')+items.map(mk).join('');
const selbar=$('selbar');
$('track').querySelectorAll('.it').forEach(a=>a.addEventListener('click',e=>{e.preventDefault();
$('track').querySelectorAll('.it').forEach(x=>x.classList.remove('sel'));$('track').querySelectorAll('.it[data-id="'+a.dataset.id+'"]').forEach(x=>x.classList.add('sel'));
selbar.innerHTML=`<span class="c">${esc(a.dataset.cat)}</span><a href="${a.dataset.href}">${esc(a.dataset.title)}</a><button class="x" aria-label="close">✕</button>`;selbar.classList.add('on');
selbar.querySelector('.x').onclick=ev=>{ev.stopPropagation();selbar.classList.remove('on');$('track').querySelectorAll('.it').forEach(x=>x.classList.remove('sel'));};}));
$('river').innerHTML=rest.slice(4,12).map(n=>`<a href="${LK(n)}"><div class="im"${bg(n)}></div><span class="tag" style="color:var(--red)">${catChip(n.cat,'','')||''}</span><h4>${esc(n.title)}</h4></a>`).join('');
if(d&&d.fetched_at){$('src').textContent='last pull '+new Date(d.fetched_at).toLocaleTimeString();}
}
async function loadTicker(){
const t=await jget('/api/ticker'); if(!t)return;
const metrics=Object.values(t).filter(m=>m&&m.label&&m.value!=null);
const cell=m=>{const cls=m.dir==='up'?'up':m.dir==='down'?'dn':'';const chg=m.change_pct!=null?` ${m.change_pct>0?'+':''}${m.change_pct}%`:'';return `<span class="m">${esc(m.label)}<b class="${cls}">${esc(m.value)}${chg}</b></span>`;};
$('datastrip').innerHTML=metrics.map(cell).join('')+'<a class="mp-link" href="/market-pulse">CRE Market Pulse →</a>';
$('snap').innerHTML=metrics.map(m=>`<div class="m"><div class="k">${esc(m.label)}</div><div class="v ${m.dir==='up'?'up':m.dir==='down'?'dn':''}">${esc(m.value)}</div></div>`).join('');
}
$('subbtn').onclick=function(){var em=($('email').value||'').trim();if(!/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(em)){$('email').focus();return;}
this.setAttribute('disabled','');fetch('/api/subscribe',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({email:em,source:'homepage'})}).then(r=>r.json()).then(j=>{if(j&&j.ok){$('subok').style.display='inline';$('email').setAttribute('disabled','');}else this.removeAttribute('disabled');}).catch(()=>this.removeAttribute('disabled'));};
async function loadPulseEmbed(){
const box=$('mpEmbed'); if(!box)return;
const d=await jget('/api/markets');
if(!d||!Array.isArray(d.curve)||!d.curve.length){box.style.display='none';return;}
const arr=x=>x==='up'?'▲':x==='down'?'▼':'●';
const nb=s=>{const n=parseFloat(String(s).replace(/[^0-9.\-]/g,''));return isNaN(n)?0:n;};
const curve=(d.curve||[]).map(c=>`<div class="mp-kpi ${esc(c.dir)}"><div class="l">${esc(c.label)} UST</div><div class="v">${esc(c.value)}</div><div class="d">${arr(c.dir)} ${Math.abs(c.change_bps||0)} bps</div></div>`);
const spreads=(d.spreads||[]).map(s=>`<div class="mp-kpi ${esc(s.dir)}"><div class="l">${esc(s.label)}</div><div class="v">${esc(String(s.value).replace(/\s*bps/,''))}<span style="font-size:12px;color:#5b6879"> bps</span></div><div class="d">${arr(s.dir)} ${nb(s.value)>=0?'steepening':'inverted'}</div></div>`);
$('mpKpis').innerHTML=curve.concat(spreads).join('');
$('mpFunds').innerHTML=(d.fundamentals||[]).map(f=>`<div class="mp-fund"><div class="fl">${esc(f.label)}</div><div class="fv">${esc(f.value)}</div><div class="fm">As of ${esc(f.as_of)} · ${esc(f.source)}</div></div>`).join('');
box.style.display='block';
}
// Always feature the LATEST CRE Talk episode in the video hero (server-rendered fallback stays if this fails).
async function loadFeaturedVideo(){
const fr=$('vhero-frame'); if(!fr)return;
const v=await jget('/api/cre-talk-latest'); if(!v||!v.id)return;
const cur=(fr.getAttribute('src')||'').match(/video\/(\d+)/);
if(cur&&cur[1]===String(v.id))return; // already latest
fr.setAttribute('src','https://player.vimeo.com/video/'+v.id+'?title=0&byline=0&portrait=0&dnt=1');
if(v.title)fr.setAttribute('title',v.title);
const cap=$('vhero-cap'); if(cap&&v.episode){const guest=(v.title||'').replace(/^.*with\s+/i,'').trim();cap.textContent='CRE Talk Episode #'+v.episode+(guest?' — '+guest:'');}
}
load(); setInterval(load,120000); loadTicker(); setInterval(loadTicker,120000); loadPulseEmbed(); setInterval(loadPulseEmbed,120000); loadFeaturedVideo();
</script>
<script src="/template-toggle.js" defer></script>
</body></html>