← back to Rentv 2026
public/2026/a.html
1353 lines
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>RENTV · The Video Wall — 2026</title>
<meta name="description" content="Western U.S. commercial real estate video — The Video Wall. News, deals, interviews and market intelligence from RENTV.">
<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:wght@700;800&family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
<style>
/* ─── TOKENS ─────────────────────────────────────────────────────────── */
:root {
--ink: #14171a;
--sub: #5b636b;
--hairline: #e4e7ea;
--red: #c8102e;
--gold: #c9a227;
--bg: #f7f8fa;
--card-bg: #ffffff;
--header-h: 56px;
--col-min: 260px;
--radius: 6px;
--shadow: 0 2px 12px rgba(0,0,0,.08);
--shadow-lg: 0 8px 32px rgba(0,0,0,.18);
}
/* ─── RESET ──────────────────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html { font-size: 16px; scroll-behavior: smooth; }
body {
font-family: 'Inter', system-ui, sans-serif;
color: var(--ink);
background: var(--bg);
min-height: 100vh;
}
a { color: inherit; text-decoration: none; }
button { font-family: inherit; cursor: pointer; border: none; background: none; }
img { display: block; }
/* ─── HEADER ─────────────────────────────────────────────────────────── */
.site-header {
position: sticky;
top: 0;
z-index: 200;
background: #fff;
border-bottom: 1px solid var(--hairline);
height: var(--header-h);
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 20px;
gap: 12px;
}
.hdr-left, .hdr-right {
display: flex;
align-items: center;
gap: 12px;
min-width: 120px;
}
.hdr-right { justify-content: flex-end; }
.hdr-btn {
display: flex;
align-items: center;
gap: 6px;
font-size: 13px;
font-weight: 600;
color: var(--sub);
padding: 6px 10px;
border-radius: 4px;
transition: background .15s, color .15s;
white-space: nowrap;
}
.hdr-btn:hover { background: var(--bg); color: var(--ink); }
.hdr-btn svg { flex-shrink: 0; }
.wordmark {
font-family: 'Playfair Display', serif;
font-size: 26px;
font-weight: 800;
letter-spacing: -0.5px;
line-height: 1;
color: var(--ink);
}
.wordmark span { color: var(--red); }
.advertise-btn {
display: inline-flex;
align-items: center;
gap: 5px;
font-size: 12px;
font-weight: 700;
color: var(--red);
border: 1.5px solid var(--red);
padding: 5px 12px;
border-radius: 4px;
letter-spacing: .3px;
transition: background .15s, color .15s;
}
.advertise-btn:hover { background: var(--red); color: #fff; }
/* ─── AD SLOTS ───────────────────────────────────────────────────────── */
.ad-strip {
background: #fff;
border-bottom: 1px solid var(--hairline);
display: flex;
justify-content: center;
align-items: center;
padding: 10px 20px;
min-height: 52px;
}
/* ─── HERO / FEATURED VIDEO ──────────────────────────────────────────── */
.hero-section {
background: var(--ink);
position: relative;
overflow: hidden;
}
.hero-inner {
max-width: 1280px;
margin: 0 auto;
display: grid;
grid-template-columns: 1fr 340px;
gap: 0;
min-height: 420px;
}
.hero-video-wrap {
position: relative;
background: #000;
cursor: pointer;
overflow: hidden;
}
.hero-video-wrap img.hero-thumb {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
opacity: .85;
transition: opacity .3s;
}
.hero-video-wrap:hover img.hero-thumb { opacity: .7; }
.hero-play {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
pointer-events: none;
}
.hero-play-circle {
width: 72px;
height: 72px;
background: rgba(200,16,46,.9);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 4px 24px rgba(200,16,46,.4);
transition: transform .2s, background .2s;
}
.hero-video-wrap:hover .hero-play-circle {
transform: scale(1.1);
background: var(--red);
}
.hero-play-circle svg { margin-left: 4px; }
.hero-badge {
position: absolute;
top: 16px;
left: 16px;
background: var(--red);
color: #fff;
font-size: 10px;
font-weight: 700;
letter-spacing: 1.2px;
text-transform: uppercase;
padding: 4px 10px;
border-radius: 3px;
}
.hero-meta {
padding: 28px 28px 28px 28px;
display: flex;
flex-direction: column;
justify-content: flex-end;
color: #fff;
}
.hero-cat {
font-size: 11px;
font-weight: 700;
letter-spacing: 1.5px;
text-transform: uppercase;
color: var(--red);
margin-bottom: 10px;
}
.hero-title {
font-family: 'Playfair Display', serif;
font-size: 22px;
font-weight: 700;
line-height: 1.3;
color: #fff;
margin-bottom: 12px;
}
.hero-desc {
font-size: 13px;
color: rgba(255,255,255,.65);
line-height: 1.6;
flex: 1;
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: 5;
-webkit-box-orient: vertical;
}
.hero-cta {
margin-top: 20px;
display: inline-flex;
align-items: center;
gap: 6px;
font-size: 13px;
font-weight: 700;
color: #fff;
background: var(--red);
padding: 9px 18px;
border-radius: 4px;
align-self: flex-start;
transition: background .15s, transform .15s;
}
.hero-cta:hover { background: #a60d26; transform: translateY(-1px); }
.hero-sponsor-strip {
position: absolute;
bottom: 0;
left: 0;
right: 0;
background: rgba(0,0,0,.55);
padding: 8px 16px;
font-size: 11px;
color: rgba(255,255,255,.6);
display: flex;
align-items: center;
justify-content: space-between;
}
/* ─── WALL MASTHEAD AD ───────────────────────────────────────────────── */
.masthead-ad {
display: flex;
justify-content: center;
align-items: center;
background: #fff;
border-top: 1px solid var(--hairline);
border-bottom: 1px solid var(--hairline);
padding: 10px 20px;
min-height: 52px;
}
/* ─── CONTROLS BAR ───────────────────────────────────────────────────── */
.controls-bar {
background: #fff;
border-bottom: 1px solid var(--hairline);
padding: 0 20px;
position: sticky;
top: var(--header-h);
z-index: 100;
}
.controls-inner {
max-width: 1280px;
margin: 0 auto;
display: flex;
align-items: center;
gap: 12px;
padding: 10px 0;
flex-wrap: wrap;
}
/* Category chips */
.cat-chips {
display: flex;
gap: 6px;
overflow-x: auto;
flex: 1;
scrollbar-width: none;
-webkit-overflow-scrolling: touch;
}
.cat-chips::-webkit-scrollbar { display: none; }
.chip {
flex-shrink: 0;
font-size: 12px;
font-weight: 600;
padding: 5px 13px;
border-radius: 20px;
border: 1.5px solid var(--hairline);
color: var(--sub);
cursor: pointer;
transition: all .15s;
white-space: nowrap;
background: transparent;
}
.chip:hover { border-color: var(--ink); color: var(--ink); }
.chip.active { background: var(--ink); color: #fff; border-color: var(--ink); }
/* Sort + density */
.wall-controls {
display: flex;
align-items: center;
gap: 10px;
flex-shrink: 0;
}
.sort-select {
font-family: 'Inter', sans-serif;
font-size: 12px;
font-weight: 600;
color: var(--ink);
border: 1.5px solid var(--hairline);
border-radius: 4px;
padding: 5px 28px 5px 10px;
background: #fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%235b636b'/%3E%3C/svg%3E") no-repeat right 8px center;
appearance: none;
cursor: pointer;
transition: border-color .15s;
}
.sort-select:hover, .sort-select:focus { border-color: var(--ink); outline: none; }
.density-group {
display: flex;
align-items: center;
gap: 6px;
font-size: 11px;
font-weight: 600;
color: var(--sub);
}
.density-group svg { flex-shrink: 0; }
#density-slider {
width: 80px;
height: 4px;
accent-color: var(--ink);
cursor: pointer;
}
.result-count {
font-size: 12px;
color: var(--sub);
white-space: nowrap;
flex-shrink: 0;
}
/* ─── VIDEO WALL ─────────────────────────────────────────────────────── */
.wall-section {
max-width: 1280px;
margin: 0 auto;
padding: 24px 20px 48px;
}
.video-wall {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(var(--col-min), 1fr));
gap: 20px;
}
/* ─── VIDEO CARD ─────────────────────────────────────────────────────── */
.video-card {
background: var(--card-bg);
border-radius: var(--radius);
box-shadow: var(--shadow);
overflow: hidden;
cursor: pointer;
transition: transform .2s, box-shadow .2s;
display: flex;
flex-direction: column;
position: relative;
}
.video-card:hover { transform: translateY(-3px); box-shadow: var(--shadow-lg); }
.video-card:hover .card-play-btn { opacity: 1; transform: translate(-50%,-50%) scale(1); }
.card-thumb-wrap {
position: relative;
padding-top: 56.25%;
background: linear-gradient(135deg, #1a2332 0%, #2d3e55 60%, var(--ink) 100%);
overflow: hidden;
}
.card-thumb {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
object-fit: cover;
}
.card-thumb-fallback {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
padding: 16px;
text-align: center;
}
.card-thumb-fallback span {
font-family: 'Playfair Display', serif;
font-size: 14px;
font-weight: 700;
color: rgba(255,255,255,.5);
line-height: 1.4;
}
.card-play-btn {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%,-50%) scale(.9);
width: 44px;
height: 44px;
background: rgba(200,16,46,.85);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
opacity: 0;
transition: opacity .2s, transform .2s;
}
.card-play-btn svg { margin-left: 3px; }
.card-source-badge {
position: absolute;
top: 8px;
right: 8px;
font-size: 9px;
font-weight: 700;
letter-spacing: .8px;
text-transform: uppercase;
padding: 3px 7px;
border-radius: 3px;
background: rgba(0,0,0,.65);
color: rgba(255,255,255,.9);
backdrop-filter: blur(2px);
}
.source-youtube { background: rgba(255,0,0,.75); }
.source-vimeo { background: rgba(26,183,234,.75); }
.source-original { background: rgba(200,16,46,.85); }
.source-newswire { background: rgba(50,50,50,.75); }
.sponsored-ribbon {
position: absolute;
bottom: 0;
left: 0;
right: 0;
background: var(--gold);
color: #fff;
font-size: 9px;
font-weight: 700;
letter-spacing: 1px;
text-transform: uppercase;
padding: 3px 8px;
}
.card-body {
padding: 13px 14px 14px;
flex: 1;
display: flex;
flex-direction: column;
gap: 6px;
}
.card-cat-chip {
display: inline-flex;
align-items: center;
align-self: flex-start;
font-size: 10px;
font-weight: 700;
letter-spacing: .8px;
text-transform: uppercase;
color: var(--red);
padding: 2px 8px;
border: 1px solid rgba(200,16,46,.25);
border-radius: 3px;
cursor: pointer;
transition: background .15s;
}
.card-cat-chip:hover { background: rgba(200,16,46,.07); }
.card-title {
font-size: 14px;
font-weight: 600;
line-height: 1.4;
color: var(--ink);
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
}
.card-footer {
display: flex;
align-items: center;
justify-content: space-between;
margin-top: auto;
padding-top: 6px;
}
.card-plays {
font-size: 11px;
color: var(--sub);
display: flex;
align-items: center;
gap: 4px;
}
.card-sponsor-cta {
font-size: 10px;
font-weight: 600;
color: var(--sub);
opacity: 0;
transition: opacity .2s;
text-decoration: none;
border-bottom: 1px dashed var(--sub);
}
.video-card:hover .card-sponsor-cta { opacity: 1; }
/* ─── SKELETON LOADER ────────────────────────────────────────────────── */
.skeleton {
background: linear-gradient(90deg, #e8eaed 25%, #f3f4f6 50%, #e8eaed 75%);
background-size: 200% 100%;
animation: shimmer 1.4s infinite;
border-radius: var(--radius);
}
@keyframes shimmer { to { background-position: -200% 0; } }
.skel-card {
background: var(--card-bg);
border-radius: var(--radius);
overflow: hidden;
box-shadow: var(--shadow);
}
.skel-thumb { padding-top: 56.25%; position: relative; }
.skel-thumb-inner { position: absolute; inset: 0; }
.skel-body { padding: 14px; display: flex; flex-direction: column; gap: 8px; }
.skel-chip { height: 16px; width: 60px; border-radius: 3px; }
.skel-line { height: 12px; border-radius: 3px; }
/* ─── MODAL ──────────────────────────────────────────────────────────── */
.modal-overlay {
display: none;
position: fixed;
inset: 0;
background: rgba(0,0,0,.88);
z-index: 500;
align-items: center;
justify-content: center;
padding: 20px;
}
.modal-overlay.open { display: flex; }
.modal-box {
width: 100%;
max-width: 960px;
background: #000;
border-radius: 8px;
overflow: hidden;
position: relative;
box-shadow: 0 24px 80px rgba(0,0,0,.8);
}
.modal-close {
position: absolute;
top: -40px;
right: 0;
color: rgba(255,255,255,.8);
font-size: 28px;
line-height: 1;
z-index: 10;
width: 36px;
height: 36px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
transition: color .15s, background .15s;
cursor: pointer;
}
.modal-close:hover { color: #fff; background: rgba(255,255,255,.15); }
.modal-video-wrap {
position: relative;
padding-top: 56.25%;
background: #000;
}
.modal-video-wrap iframe,
.modal-video-wrap video {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
border: none;
}
.modal-info {
padding: 18px 20px;
background: var(--ink);
color: #fff;
}
.modal-cat {
font-size: 10px;
font-weight: 700;
letter-spacing: 1.2px;
text-transform: uppercase;
color: var(--red);
margin-bottom: 6px;
}
.modal-title {
font-family: 'Playfair Display', serif;
font-size: 18px;
font-weight: 700;
line-height: 1.3;
margin-bottom: 6px;
}
.modal-desc {
font-size: 13px;
color: rgba(255,255,255,.6);
line-height: 1.6;
}
.modal-actions {
display: flex;
gap: 12px;
margin-top: 14px;
flex-wrap: wrap;
}
.modal-action-btn {
font-size: 12px;
font-weight: 600;
padding: 7px 14px;
border-radius: 4px;
transition: background .15s;
cursor: pointer;
text-decoration: none;
display: inline-flex;
align-items: center;
gap: 5px;
}
.modal-action-btn.primary { background: var(--red); color: #fff; }
.modal-action-btn.primary:hover { background: #a60d26; }
.modal-action-btn.secondary { background: rgba(255,255,255,.12); color: rgba(255,255,255,.85); }
.modal-action-btn.secondary:hover { background: rgba(255,255,255,.2); }
/* ─── FOOTER ─────────────────────────────────────────────────────────── */
.site-footer {
background: var(--ink);
color: rgba(255,255,255,.6);
padding: 48px 20px 28px;
}
.footer-inner {
max-width: 1280px;
margin: 0 auto;
}
.footer-top {
display: grid;
grid-template-columns: 200px repeat(3, 1fr);
gap: 40px;
padding-bottom: 36px;
border-bottom: 1px solid rgba(255,255,255,.1);
margin-bottom: 24px;
}
.footer-brand .wordmark { font-size: 28px; color: #fff; margin-bottom: 12px; }
.footer-brand .wordmark span { color: var(--red); }
.footer-tagline {
font-size: 12px;
line-height: 1.6;
color: rgba(255,255,255,.45);
margin-bottom: 16px;
}
.footer-advertise {
display: inline-flex;
align-items: center;
gap: 5px;
font-size: 12px;
font-weight: 700;
color: var(--red);
border: 1.5px solid var(--red);
padding: 6px 14px;
border-radius: 4px;
transition: background .15s, color .15s;
}
.footer-advertise:hover { background: var(--red); color: #fff; }
.footer-col h4 {
font-size: 11px;
font-weight: 700;
letter-spacing: 1.2px;
text-transform: uppercase;
color: rgba(255,255,255,.9);
margin-bottom: 14px;
}
.footer-col ul { list-style: none; display: flex; flex-direction: column; gap: 9px; }
.footer-col a {
font-size: 13px;
color: rgba(255,255,255,.5);
transition: color .15s;
}
.footer-col a:hover { color: rgba(255,255,255,.9); }
.footer-bottom {
display: flex;
align-items: center;
justify-content: space-between;
font-size: 12px;
color: rgba(255,255,255,.3);
flex-wrap: wrap;
gap: 8px;
}
.footer-bottom a { color: rgba(255,255,255,.4); transition: color .15s; }
.footer-bottom a:hover { color: rgba(255,255,255,.8); }
/* ─── TEMPLATE SWITCHER CHIP ─────────────────────────────────────────── */
.tpl-switcher {
position: fixed;
bottom: 16px;
right: 16px;
z-index: 300;
background: var(--ink);
color: rgba(255,255,255,.85);
font-size: 11px;
font-weight: 700;
padding: 7px 13px;
border-radius: 20px;
box-shadow: 0 4px 16px rgba(0,0,0,.3);
transition: background .15s, transform .15s;
letter-spacing: .3px;
}
.tpl-switcher:hover { background: var(--red); transform: translateY(-1px); }
/* ─── EMPTY STATE ────────────────────────────────────────────────────── */
.empty-state {
grid-column: 1 / -1;
text-align: center;
padding: 60px 20px;
color: var(--sub);
}
.empty-state h3 { font-size: 18px; margin-bottom: 8px; color: var(--ink); }
.empty-state p { font-size: 14px; }
/* ─── RESPONSIVE ─────────────────────────────────────────────────────── */
@media (max-width: 900px) {
.hero-inner {
grid-template-columns: 1fr;
min-height: auto;
}
.hero-video-wrap { padding-top: 56.25%; position: relative; min-height: auto; }
.hero-video-wrap img.hero-thumb { position: absolute; inset: 0; width: 100%; height: 100%; }
.hero-meta { padding: 20px; }
.hero-title { font-size: 18px; }
.footer-top { grid-template-columns: 1fr 1fr; gap: 28px; }
.footer-brand { grid-column: 1 / -1; }
}
@media (max-width: 640px) {
.hdr-btn span { display: none; }
.advertise-btn span { display: none; }
.controls-inner { gap: 8px; }
.wall-controls { flex-wrap: wrap; }
.footer-top { grid-template-columns: 1fr; }
.result-count { display: none; }
}
@media (max-width: 480px) {
:root { --col-min: 160px; }
.wordmark { font-size: 22px; }
}
/* ─── UTILS ──────────────────────────────────────────────────────────── */
.visually-hidden {
position: absolute; width: 1px; height: 1px;
overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap;
}
</style>
</head>
<body>
<!-- ─── HEADER ──────────────────────────────────────────────────────── -->
<header class="site-header" role="banner">
<div class="hdr-left">
<button class="hdr-btn" id="menu-btn" aria-label="Open navigation menu" aria-expanded="false" aria-controls="nav-drawer">
<svg width="18" height="14" viewBox="0 0 18 14" fill="none" aria-hidden="true">
<path d="M0 1h18M0 7h18M0 13h18" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/>
</svg>
<span>Explore</span>
</button>
</div>
<a href="/" class="wordmark" aria-label="RENTV — Home">
REN<span>TV</span>
</a>
<div class="hdr-right">
<a href="/advertise" class="advertise-btn" aria-label="Advertise on RENTV">
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" aria-hidden="true">
<path d="M1 6h10M7 2l4 4-4 4" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
<span>Advertise</span>
</a>
<button class="hdr-btn" aria-label="Browse sections">
<span>Sections</span>
<svg width="10" height="6" viewBox="0 0 10 6" fill="none" aria-hidden="true">
<path d="M1 1l4 4 4-4" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</button>
</div>
</header>
<!-- ─── LEADERBOARD AD ───────────────────────────────────────────────── -->
<div class="ad-strip">
<div data-ad-slot="home-leaderboard"></div>
</div>
<!-- ─── HERO / FEATURED VIDEO ────────────────────────────────────────── -->
<section class="hero-section" id="hero" aria-label="Featured video">
<div class="hero-inner">
<div class="hero-video-wrap" id="hero-video-wrap" role="button" tabindex="0" aria-label="Play featured video">
<img id="hero-thumb" class="hero-thumb" src="" alt="" loading="eager">
<div class="hero-badge">Featured</div>
<div class="hero-play" aria-hidden="true">
<div class="hero-play-circle">
<svg width="22" height="22" viewBox="0 0 22 22" fill="none">
<path d="M7 4l13 7-13 7V4z" fill="#fff"/>
</svg>
</div>
</div>
<div class="hero-sponsor-strip" id="hero-sponsor"></div>
</div>
<div class="hero-meta">
<div class="hero-cat" id="hero-cat"></div>
<h1 class="hero-title" id="hero-title"></h1>
<p class="hero-desc" id="hero-desc"></p>
<a class="hero-cta" id="hero-cta" href="#">
Watch Now
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" aria-hidden="true">
<path d="M2 7h10M8 3l4 4-4 4" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</a>
</div>
</div>
</section>
<!-- ─── WALL MASTHEAD AD + CONTROLS ─────────────────────────────────── -->
<div class="masthead-ad">
<div data-ad-slot="review-masthead"></div>
</div>
<div class="controls-bar" role="navigation" aria-label="Video filter and sort controls">
<div class="controls-inner">
<!-- Category chips -->
<div class="cat-chips" role="tablist" aria-label="Filter by category" id="cat-chips">
<!-- injected by JS -->
</div>
<!-- Sort + density -->
<div class="wall-controls">
<label class="visually-hidden" for="sort-select">Sort videos</label>
<select class="sort-select" id="sort-select" aria-label="Sort videos">
<option value="newest">Newest</option>
<option value="most-viewed">Most Viewed</option>
<option value="title-az">Title A→Z</option>
<option value="category">Category</option>
</select>
<div class="density-grid" aria-label="Grid density" role="group">
<div class="density-group">
<svg width="13" height="13" viewBox="0 0 13 13" aria-hidden="true" fill="none">
<rect x="0" y="0" width="5" height="5" rx="1" fill="currentColor" opacity=".5"/>
<rect x="8" y="0" width="5" height="5" rx="1" fill="currentColor" opacity=".5"/>
<rect x="0" y="8" width="5" height="5" rx="1" fill="currentColor" opacity=".5"/>
<rect x="8" y="8" width="5" height="5" rx="1" fill="currentColor" opacity=".5"/>
</svg>
<label for="density-slider" class="visually-hidden">Card size</label>
<input type="range" id="density-slider" min="160" max="480" step="20" value="260"
aria-label="Grid density — card minimum width">
<svg width="18" height="13" viewBox="0 0 18 13" aria-hidden="true" fill="none">
<rect x="0" y="0" width="7" height="7" rx="1" fill="currentColor" opacity=".5"/>
<rect x="11" y="0" width="7" height="7" rx="1" fill="currentColor" opacity=".5"/>
<rect x="0" y="10" width="7" height="3" rx="1" fill="currentColor" opacity=".2"/>
<rect x="11" y="10" width="7" height="3" rx="1" fill="currentColor" opacity=".2"/>
</svg>
</div>
</div>
<span class="result-count" id="result-count" aria-live="polite"></span>
</div>
</div>
</div>
<!-- ─── VIDEO WALL ────────────────────────────────────────────────────── -->
<main class="wall-section" role="main" aria-label="Video wall">
<div class="video-wall" id="video-wall" role="list">
<!-- Skeleton placeholders while loading -->
<div class="skel-card" aria-hidden="true">
<div class="skel-thumb skeleton"><div class="skel-thumb-inner"></div></div>
<div class="skel-body"><div class="skel-chip skeleton"></div><div class="skel-line skeleton" style="width:90%"></div><div class="skel-line skeleton" style="width:70%"></div></div>
</div>
<div class="skel-card" aria-hidden="true">
<div class="skel-thumb skeleton"><div class="skel-thumb-inner"></div></div>
<div class="skel-body"><div class="skel-chip skeleton"></div><div class="skel-line skeleton" style="width:85%"></div><div class="skel-line skeleton" style="width:60%"></div></div>
</div>
<div class="skel-card" aria-hidden="true">
<div class="skel-thumb skeleton"><div class="skel-thumb-inner"></div></div>
<div class="skel-body"><div class="skel-chip skeleton"></div><div class="skel-line skeleton" style="width:88%"></div><div class="skel-line skeleton" style="width:50%"></div></div>
</div>
<div class="skel-card" aria-hidden="true">
<div class="skel-thumb skeleton"><div class="skel-thumb-inner"></div></div>
<div class="skel-body"><div class="skel-chip skeleton"></div><div class="skel-line skeleton" style="width:75%"></div><div class="skel-line skeleton" style="width:65%"></div></div>
</div>
<div class="skel-card" aria-hidden="true">
<div class="skel-thumb skeleton"><div class="skel-thumb-inner"></div></div>
<div class="skel-body"><div class="skel-chip skeleton"></div><div class="skel-line skeleton" style="width:92%"></div><div class="skel-line skeleton" style="width:55%"></div></div>
</div>
<div class="skel-card" aria-hidden="true">
<div class="skel-thumb skeleton"><div class="skel-thumb-inner"></div></div>
<div class="skel-body"><div class="skel-chip skeleton"></div><div class="skel-line skeleton" style="width:80%"></div><div class="skel-line skeleton" style="width:70%"></div></div>
</div>
</div>
</main>
<!-- ─── FOOTER ────────────────────────────────────────────────────────── -->
<footer class="site-footer" role="contentinfo">
<div class="footer-inner">
<div class="footer-top">
<div class="footer-brand">
<a href="/" class="wordmark" aria-label="RENTV — Home">REN<span>TV</span></a>
<p class="footer-tagline">Western U.S. commercial real estate news,<br>video and intelligence since 1998.</p>
<a href="/advertise" class="footer-advertise">Advertise on RENTV →</a>
</div>
<div class="footer-col">
<h4>Sections</h4>
<ul>
<li><a href="/deals">Sales</a></li>
<li><a href="/deals">Leases</a></li>
<li><a href="/deals">Financing</a></li>
<li><a href="/deals">Development</a></li>
</ul>
</div>
<div class="footer-col">
<h4>Intelligence</h4>
<ul>
<li><a href="/markets">Markets</a></li>
<li><a href="/review">The REview</a></li>
<li><a href="/cre-talk">CRE Talk</a></li>
<li><a href="/conferences">Conferences</a></li>
</ul>
</div>
<div class="footer-col">
<h4>Company</h4>
<ul>
<li><a href="/about">About RENTV</a></li>
<li><a href="/advertise">Advertise</a></li>
<li><a href="/subscribe">Subscribe</a></li>
<li><a href="https://www.rentvreview.com" target="_blank" rel="noopener">The REview</a></li>
</ul>
</div>
</div>
<div class="footer-bottom">
<span>© 2026 RENTV.com · All rights reserved.</span>
<span>
<a href="/privacy">Privacy</a> ·
<a href="/terms">Terms</a> ·
<a href="/advertise">Advertise</a>
</span>
</div>
</div>
</footer>
<!-- ─── MODAL ─────────────────────────────────────────────────────────── -->
<div class="modal-overlay" id="modal-overlay" role="dialog" aria-modal="true" aria-label="Video player">
<div class="modal-box">
<button class="modal-close" id="modal-close" aria-label="Close video">×</button>
<div class="modal-video-wrap" id="modal-video-wrap"></div>
<div class="modal-info">
<div class="modal-cat" id="modal-cat"></div>
<h2 class="modal-title" id="modal-title"></h2>
<p class="modal-desc" id="modal-desc"></p>
<div class="modal-actions">
<a class="modal-action-btn primary" id="modal-watch-link" href="#" target="_blank" rel="noopener">
Open Full Page
</a>
<a class="modal-action-btn secondary" id="modal-sponsor-link" href="#">
Sponsor This Video
</a>
</div>
</div>
</div>
</div>
<!-- ─── TEMPLATE SWITCHER ─────────────────────────────────────────────── -->
<a href="/2026/" class="tpl-switcher" aria-label="Switch 2026 template">◆ 2026 Templates — switch</a>
<!-- ─── AD SLOT SCRIPT ────────────────────────────────────────────────── -->
<script src="/ad-slot.js" defer></script>
<!-- ─── MAIN SCRIPT ───────────────────────────────────────────────────── -->
<script>
(function () {
'use strict';
/* ── Helpers ─────────────────────────────────────────────────────── */
function esc(str) {
if (str == null) return '';
return String(str)
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
function safe(str) {
// Returns text safe for innerHTML by escaping HTML
return esc(str);
}
/* ── LocalStorage helpers ─────────────────────────────────────────── */
const LS_SORT = 'rentv_wall_sort';
const LS_DENSITY = 'rentv_wall_density';
const LS_CAT = 'rentv_wall_cat';
function lsGet(key, fallback) {
try { return localStorage.getItem(key) || fallback; } catch(_) { return fallback; }
}
function lsSet(key, val) {
try { localStorage.setItem(key, String(val)); } catch(_) {}
}
/* ── State ───────────────────────────────────────────────────────── */
let ALL_ITEMS = []; // full inventory
let featuredItem = null;
let currentCat = lsGet(LS_CAT, 'All');
let currentSort = lsGet(LS_SORT, 'newest');
let currentDensity = parseInt(lsGet(LS_DENSITY, '260'), 10);
/* ── DOM refs ────────────────────────────────────────────────────── */
const wall = document.getElementById('video-wall');
const catChips = document.getElementById('cat-chips');
const sortSel = document.getElementById('sort-select');
const densitySl = document.getElementById('density-slider');
const resultCount = document.getElementById('result-count');
/* ── Restore persisted UI values ─────────────────────────────────── */
sortSel.value = currentSort;
densitySl.value = currentDensity;
document.documentElement.style.setProperty('--col-min', currentDensity + 'px');
/* ── Source label helper ──────────────────────────────────────────── */
function sourceLabel(source) {
const map = { youtube: 'YouTube', vimeo: 'Vimeo', original: 'RENTV Original', 'news-wire': 'Deal Wire' };
return map[source] || source;
}
function sourceCls(source) {
const map = { youtube: 'source-youtube', vimeo: 'source-vimeo', original: 'source-original', 'news-wire': 'source-newswire' };
return map[source] || '';
}
/* ── Sort ────────────────────────────────────────────────────────── */
function sortItems(items, mode) {
const copy = items.slice();
switch (mode) {
case 'most-viewed':
return copy.sort((a, b) => (b.plays || 0) - (a.plays || 0));
case 'title-az':
return copy.sort((a, b) => (a.title || '').localeCompare(b.title || ''));
case 'category':
return copy.sort((a, b) => (a.cat || '').localeCompare(b.cat || '') || (a.title || '').localeCompare(b.title || ''));
case 'newest':
default:
return copy; // API order = newest
}
}
/* ── Filter ──────────────────────────────────────────────────────── */
function filterItems(items, cat) {
if (cat === 'All') return items;
return items.filter(i => i.cat === cat);
}
/* ── Render wall ─────────────────────────────────────────────────── */
function renderWall() {
let items = filterItems(ALL_ITEMS, currentCat);
items = sortItems(items, currentSort);
resultCount.textContent = items.length + ' video' + (items.length !== 1 ? 's' : '');
if (items.length === 0) {
wall.innerHTML = '<div class="empty-state"><h3>No videos in this category</h3><p>Try a different filter or select All.</p></div>';
return;
}
wall.innerHTML = items.map(item => renderCard(item)).join('');
// Attach click handlers
wall.querySelectorAll('.video-card').forEach(card => {
card.addEventListener('click', function(e) {
// If clicking the category chip, filter instead of open modal
if (e.target.closest('.card-cat-chip')) return;
const uid = card.dataset.uid;
const item = ALL_ITEMS.find(i => i.uid === uid);
if (item) openModal(item);
});
card.addEventListener('keydown', function(e) {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
card.click();
}
});
});
// Category chip clicks on cards
wall.querySelectorAll('.card-cat-chip').forEach(chip => {
chip.addEventListener('click', function(e) {
e.stopPropagation();
const cat = chip.dataset.cat;
if (cat) setCategory(cat);
});
});
}
function renderCard(item) {
const hasSponsor = item.sponsorship && item.sponsorship.status === 'sold';
const thumbHtml = buildThumbHtml(item);
const playsHtml = item.plays > 0
? `<span class="card-plays" aria-label="${item.plays} plays">
<svg width="11" height="11" viewBox="0 0 11 11" fill="none" aria-hidden="true"><path d="M2 1.5l7.5 4-7.5 4V1.5z" fill="currentColor" opacity=".5"/></svg>
${item.plays.toLocaleString()}
</span>`
: '';
return `
<article class="video-card"
role="listitem"
data-uid="${esc(item.uid)}"
tabindex="0"
aria-label="${esc(item.title)} — ${esc(item.cat)}">
<div class="card-thumb-wrap">
${thumbHtml}
<div class="card-play-btn" aria-hidden="true">
<svg width="16" height="16" viewBox="0 0 16 16" fill="none">
<path d="M3 2l11 6-11 6V2z" fill="#fff"/>
</svg>
</div>
<span class="card-source-badge ${sourceCls(item.source)}">${safe(sourceLabel(item.source))}</span>
${hasSponsor ? `<div class="sponsored-ribbon">SPONSORED · ${safe(item.sponsorship.advertiser)}</div>` : ''}
</div>
<div class="card-body">
<a class="card-cat-chip" href="#" data-cat="${esc(item.cat)}" aria-label="Filter by ${esc(item.cat)}">${safe(item.cat)}</a>
<h3 class="card-title">${safe(item.title)}</h3>
<div class="card-footer">
${playsHtml}
${!hasSponsor ? `<a class="card-sponsor-cta" href="/advertise?video=${esc(item.uid)}" tabindex="-1" aria-hidden="true">Sponsor this video</a>` : ''}
</div>
</div>
</article>`;
}
function buildThumbHtml(item) {
if (item.thumb && item.thumb !== '') {
// Skip bad vimeo-undefined items
if (item.uid === 'vimeo-undefined') {
return fallbackThumb(item);
}
return `<img class="card-thumb" src="${esc(item.thumb)}" alt="${esc(item.title)}" loading="lazy"
onerror="this.style.display='none';this.nextElementSibling.style.display='flex'">
<div class="card-thumb-fallback" style="display:none"><span>${safe(item.title)}</span></div>`;
}
return fallbackThumb(item);
}
function fallbackThumb(item) {
return `<div class="card-thumb-fallback"><span>${safe(item.title)}</span></div>`;
}
/* ── Hero setup ──────────────────────────────────────────────────── */
function setupHero(item) {
featuredItem = item;
const heroThumb = document.getElementById('hero-thumb');
const heroTitle = document.getElementById('hero-title');
const heroCat = document.getElementById('hero-cat');
const heroDesc = document.getElementById('hero-desc');
const heroCta = document.getElementById('hero-cta');
const heroSponsor = document.getElementById('hero-sponsor');
heroTitle.textContent = item.title || 'Western U.S. Commercial Real Estate';
heroCat.textContent = item.cat || 'Featured';
heroDesc.textContent = item.desc || '';
if (item.thumb && item.thumb !== '' && item.uid !== 'vimeo-undefined') {
heroThumb.src = item.thumb;
heroThumb.alt = item.title || '';
} else {
heroThumb.style.display = 'none';
}
heroCta.href = item.view_url || '#';
heroCta.setAttribute('aria-label', 'Watch: ' + (item.title || 'Featured video'));
const heroWrap = document.getElementById('hero-video-wrap');
heroWrap.setAttribute('aria-label', 'Play: ' + (item.title || 'Featured video'));
heroWrap.addEventListener('click', () => openModal(item));
heroWrap.addEventListener('keydown', e => {
if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); openModal(item); }
});
if (item.sponsorship && item.sponsorship.status === 'sold') {
heroSponsor.textContent = 'Sponsored by ' + item.sponsorship.advertiser;
} else {
const price = item.sponsorship && item.sponsorship.price_monthly
? item.sponsorship.price_monthly
: 600;
heroSponsor.innerHTML = `<span>Sponsor this feature slot — from $${price.toLocaleString()}/mo</span><a href="/advertise?video=${esc(item.uid)}" style="color:#c8102e;font-weight:700;margin-left:8px">Advertise →</a>`;
}
}
/* ── Category chips ──────────────────────────────────────────────── */
function buildCategoryChips(items) {
const cats = ['All', ...Array.from(new Set(items.map(i => i.cat).filter(Boolean))).sort()];
catChips.innerHTML = cats.map(cat =>
`<button class="chip${cat === currentCat ? ' active' : ''}"
role="tab"
aria-selected="${cat === currentCat}"
data-cat="${esc(cat)}">${safe(cat)}</button>`
).join('');
catChips.querySelectorAll('.chip').forEach(btn => {
btn.addEventListener('click', () => setCategory(btn.dataset.cat));
});
}
function setCategory(cat) {
currentCat = cat;
lsSet(LS_CAT, cat);
catChips.querySelectorAll('.chip').forEach(btn => {
const active = btn.dataset.cat === cat;
btn.classList.toggle('active', active);
btn.setAttribute('aria-selected', active);
});
renderWall();
}
/* ── Sort + density controls ──────────────────────────────────────── */
sortSel.addEventListener('change', function() {
currentSort = this.value;
lsSet(LS_SORT, currentSort);
renderWall();
});
densitySl.addEventListener('input', function() {
currentDensity = parseInt(this.value, 10);
lsSet(LS_DENSITY, currentDensity);
document.documentElement.style.setProperty('--col-min', currentDensity + 'px');
});
/* ── Modal ───────────────────────────────────────────────────────── */
const overlay = document.getElementById('modal-overlay');
const modalClose = document.getElementById('modal-close');
const modalVideo = document.getElementById('modal-video-wrap');
const modalCat = document.getElementById('modal-cat');
const modalTitle = document.getElementById('modal-title');
const modalDesc = document.getElementById('modal-desc');
const modalWatchLink = document.getElementById('modal-watch-link');
const modalSponsorLink = document.getElementById('modal-sponsor-link');
function openModal(item) {
modalCat.textContent = item.cat || '';
modalTitle.textContent = item.title || '';
modalDesc.textContent = item.desc || '';
modalWatchLink.href = item.view_url || '#';
modalSponsorLink.href = '/advertise?video=' + encodeURIComponent(item.uid || '');
// Inject media
if (item.embed) {
// YouTube or Vimeo iframe
const src = item.embed + (item.embed.includes('?') ? '&' : '?') + 'autoplay=1&rel=0';
modalVideo.innerHTML = `<iframe src="${esc(src)}" allow="autoplay; fullscreen; picture-in-picture" allowfullscreen title="${esc(item.title)}"></iframe>`;
} else if (item.mp4) {
modalVideo.innerHTML = `<video controls autoplay playsinline preload="metadata" poster="${esc(item.thumb || '')}">
<source src="${esc(item.mp4)}" type="video/mp4">
<p>Your browser doesn't support HTML5 video.</p>
</video>`;
} else if (item.view_url) {
// Fallback: open external link
window.open(item.view_url, '_blank', 'noopener');
return;
} else {
modalVideo.innerHTML = `<div style="display:flex;align-items:center;justify-content:center;height:200px;color:#fff;font-size:14px;">Video unavailable</div>`;
}
overlay.classList.add('open');
document.body.style.overflow = 'hidden';
modalClose.focus();
}
function closeModal() {
overlay.classList.remove('open');
document.body.style.overflow = '';
// Stop iframe/video playback
modalVideo.innerHTML = '';
}
modalClose.addEventListener('click', closeModal);
overlay.addEventListener('click', function(e) {
if (e.target === overlay) closeModal();
});
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape' && overlay.classList.contains('open')) closeModal();
});
/* ── Fetch data and bootstrap ─────────────────────────────────────── */
async function init() {
try {
// Fetch both endpoints in parallel
const [invRes, vidRes] = await Promise.allSettled([
fetch('/api/ad/inventory'),
fetch('/api/videos')
]);
let inventoryItems = [];
let pinnedItem = null;
if (invRes.status === 'fulfilled' && invRes.value.ok) {
const invData = await invRes.value.json();
inventoryItems = (invData.items || []).filter(i =>
// Filter out the bad vimeo-undefined record for the wall but keep all others
i.uid !== 'vimeo-undefined'
);
}
if (vidRes.status === 'fulfilled' && vidRes.value.ok) {
const vidData = await vidRes.value.json();
pinnedItem = (vidData.items || []).find(i => i.pinned) || (vidData.items || [])[0] || null;
}
// Set up hero: prefer pinned from /api/videos, fall back to Featured from inventory
const heroItem = pinnedItem
? {
uid: 'yt-' + (pinnedItem.id || pinnedItem.yt || 'featured'),
cat: pinnedItem.cat || 'Featured',
title: pinnedItem.title || '',
desc: pinnedItem.desc || '',
thumb: pinnedItem.thumb || '',
embed: pinnedItem.embed || (pinnedItem.yt ? 'https://www.youtube.com/embed/' + pinnedItem.yt : ''),
mp4: pinnedItem.mp4 || '',
view_url: pinnedItem.view_url || '',
plays: pinnedItem.plays || 0,
sponsorship: { status: 'available', price_monthly: 600 }
}
: inventoryItems.find(i => i.cat === 'Featured') || inventoryItems[0] || null;
if (heroItem) setupHero(heroItem);
ALL_ITEMS = inventoryItems;
if (ALL_ITEMS.length === 0) {
// Graceful empty state
wall.innerHTML = `<div class="empty-state">
<h3>Loading videos…</h3>
<p>Check back shortly or <a href="/review" style="color:var(--red)">visit The REview</a>.</p>
</div>`;
return;
}
buildCategoryChips(ALL_ITEMS);
renderWall();
} catch (err) {
console.error('RENTV Wall: init error', err);
wall.innerHTML = `<div class="empty-state">
<h3>Unable to load videos</h3>
<p>Please refresh the page. If the problem persists, <a href="/about" style="color:var(--red)">contact us</a>.</p>
</div>`;
}
}
init();
})();
</script>
<script src="/2026/elements.js" defer></script>
<script src="/template-toggle.js" defer></script>
</body>
</html>