← back to Rentv 2026

public/2026/e.html

1413 lines

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>RENTV — CRE Market Dashboard</title>
<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&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
<style>
/* ── TOKENS ── */
:root {
  --bg:         #0b0f16;
  --surface:    #131820;
  --panel:      #181e28;
  --border:     #252d3d;
  --border-hi:  #2f3a50;
  --text:        #e2e8f0;
  --text-muted:  #64748b;
  --text-dim:    #94a3b8;
  --red:         #c8102e;
  --green:       #22c55e;
  --red-dn:      #f04141;
  --gold:        #f0b429;
  --blue:        #3b82f6;
  --mono:        'JetBrains Mono', ui-monospace, 'Cascadia Code', monospace;
  --serif:       'Playfair Display', Georgia, serif;
  --sans:        'Inter', system-ui, sans-serif;
  --radius:      8px;
  --radius-sm:   5px;
  --ticker-h:    40px;
}

/* ── RESET ── */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html { scroll-behavior: smooth; }
body {
  font-family: var(--sans);
  background: var(--bg);
  color: var(--text);
  min-height: 100vh;
  font-size: 14px;
  line-height: 1.5;
  overflow-x: hidden;
}
a { color: inherit; text-decoration: none; }
img { display: block; max-width: 100%; }
button { cursor: pointer; font-family: inherit; }

/* ── UTILITY ── */
.mono { font-family: var(--mono); font-variant-numeric: tabular-nums; }
.up   { color: var(--green); }
.dn   { color: var(--red-dn); }
.flat { color: var(--text-dim); }
.gold { color: var(--gold); }
.red  { color: var(--red); }
.sr-only { position: absolute; width: 1px; height: 1px; clip: rect(0,0,0,0); overflow: hidden; }

/* ── LIVE PULSE ── */
@keyframes pulse-ring {
  0%   { transform: scale(.8); opacity: .9; }
  70%  { transform: scale(1.6); opacity: 0; }
  100% { transform: scale(.8); opacity: 0; }
}
.live-dot {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: .08em;
  color: var(--red);
  text-transform: uppercase;
}
.live-dot::before {
  content: '';
  width: 8px; height: 8px;
  border-radius: 50%;
  background: var(--red);
  display: inline-block;
  flex-shrink: 0;
}
.live-dot::after {
  content: '';
  position: absolute;
  left: 0; top: 50%;
  transform: translateY(-50%);
  width: 8px; height: 8px;
  border-radius: 50%;
  background: var(--red);
  animation: pulse-ring 1.8s ease-out infinite;
}

/* ── TICKER STRIP ── */
#ticker-strip {
  height: var(--ticker-h);
  background: #0d1219;
  border-bottom: 1px solid var(--border);
  display: flex;
  align-items: center;
  overflow: hidden;
  position: relative;
}
#ticker-strip::before,
#ticker-strip::after {
  content: '';
  position: absolute;
  top: 0; bottom: 0;
  width: 60px;
  z-index: 2;
  pointer-events: none;
}
#ticker-strip::before { left: 0;  background: linear-gradient(to right, #0d1219, transparent); }
#ticker-strip::after  { right: 0; background: linear-gradient(to left,  #0d1219, transparent); }

.ticker-label {
  flex-shrink: 0;
  padding: 0 16px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: .1em;
  color: var(--red);
  text-transform: uppercase;
  border-right: 1px solid var(--border);
  height: 100%;
  display: flex;
  align-items: center;
  gap: 8px;
  z-index: 3;
  background: #0d1219;
}

#ticker-track-wrap {
  flex: 1;
  overflow: hidden;
  height: 100%;
  display: flex;
  align-items: center;
}
#ticker-track {
  display: flex;
  gap: 0;
  white-space: nowrap;
  will-change: transform;
}
.ticker-item {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 0 24px;
  border-right: 1px solid var(--border);
  height: var(--ticker-h);
  font-family: var(--mono);
  font-size: 12px;
  font-variant-numeric: tabular-nums;
}
.ticker-item .t-label { color: var(--text-muted); font-size: 10px; text-transform: uppercase; letter-spacing: .06em; }
.ticker-item .t-value { font-weight: 600; }
.ticker-item .t-chg   { font-size: 11px; }

/* ── HEADER ── */
#site-header {
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  position: sticky;
  top: 0;
  z-index: 100;
}
.header-inner {
  max-width: 1600px;
  margin: 0 auto;
  padding: 0 20px;
  height: 56px;
  display: flex;
  align-items: center;
  gap: 16px;
}
.header-left, .header-right {
  display: flex;
  align-items: center;
  gap: 12px;
  flex: 1;
}
.header-right { justify-content: flex-end; }

.wordmark {
  font-family: var(--serif);
  font-size: 22px;
  font-weight: 800;
  letter-spacing: -.02em;
  line-height: 1;
  flex-shrink: 0;
}
.wordmark .tv { color: var(--red); }

.nav-btn {
  background: none;
  border: none;
  color: var(--text-dim);
  font-size: 13px;
  font-weight: 500;
  padding: 6px 10px;
  border-radius: var(--radius-sm);
  transition: color .15s, background .15s;
}
.nav-btn:hover { color: var(--text); background: var(--border); }

.advertise-btn {
  background: var(--red);
  color: #fff;
  border: none;
  border-radius: var(--radius-sm);
  padding: 7px 14px;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
  transition: opacity .15s;
  text-decoration: none;
  display: inline-block;
}
.advertise-btn:hover { opacity: .85; }

.hamburger-btn {
  background: none;
  border: none;
  color: var(--text-dim);
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 6px;
  border-radius: var(--radius-sm);
  transition: color .15s;
}
.hamburger-btn:hover { color: var(--text); }
.hamburger-btn span {
  display: block;
  width: 18px;
  height: 2px;
  background: currentColor;
  border-radius: 1px;
}

/* ── AD SLOT BANNER (LEADERBOARD) ── */
#ad-leaderboard {
  max-width: 1600px;
  margin: 0 auto;
  padding: 12px 20px 0;
}

/* ── MAIN LAYOUT ── */
.dashboard-wrap {
  max-width: 1600px;
  margin: 0 auto;
  padding: 16px 20px 80px;
  display: grid;
  grid-template-columns: 1fr 340px;
  grid-template-rows: auto auto auto;
  gap: 14px;
}

/* ── PANEL BASE ── */
.panel {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
}
.panel-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 10px 16px;
  border-bottom: 1px solid var(--border);
  gap: 10px;
}
.panel-title {
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .1em;
  color: var(--text-muted);
  display: flex;
  align-items: center;
  gap: 8px;
}
.panel-title .icon { font-size: 12px; }
.panel-body { padding: 14px 16px; }

/* ── MARKET PULSE PANEL ── */
#panel-market {
  grid-column: 1;
  grid-row: 2;
}
.market-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(130px, 1fr));
  gap: 10px;
}
.kpi-tile {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 10px 12px;
  display: flex;
  flex-direction: column;
  gap: 4px;
  transition: border-color .15s;
}
.kpi-tile:hover { border-color: var(--border-hi); }
.kpi-label {
  font-size: 9px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .09em;
  color: var(--text-muted);
}
.kpi-value {
  font-family: var(--mono);
  font-size: 18px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  line-height: 1.1;
}
.kpi-delta {
  font-family: var(--mono);
  font-size: 11px;
  font-variant-numeric: tabular-nums;
}
.kpi-source {
  font-size: 9px;
  color: var(--text-muted);
  margin-top: 2px;
}
.section-divider {
  font-size: 9px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .1em;
  color: var(--text-muted);
  padding: 6px 0 4px;
  border-top: 1px solid var(--border);
  margin-top: 6px;
  grid-column: 1 / -1;
}

/* ── LIVE VIDEO PANEL ── */
#panel-video {
  grid-column: 1;
  grid-row: 1;
}
.video-layout {
  display: grid;
  grid-template-columns: 1fr 240px;
  gap: 0;
}
.video-main {
  position: relative;
}
.video-embed-wrap {
  position: relative;
  padding-top: 56.25%;
  background: #000;
}
.video-embed-wrap iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: none;
}
.video-meta {
  padding: 12px 16px;
  border-top: 1px solid var(--border);
}
.video-cat {
  font-size: 9px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .1em;
  color: var(--red);
  margin-bottom: 4px;
}
.video-title {
  font-family: var(--serif);
  font-size: 16px;
  font-weight: 700;
  line-height: 1.3;
  margin-bottom: 4px;
}
.video-desc {
  font-size: 12px;
  color: var(--text-dim);
  line-height: 1.4;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.video-playlist {
  border-left: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  overflow-y: auto;
  max-height: 340px;
}
.playlist-item {
  display: flex;
  gap: 0;
  border-bottom: 1px solid var(--border);
  transition: background .15s;
  cursor: pointer;
}
.playlist-item:hover { background: var(--surface); }
.playlist-item.active { background: rgba(200,16,46,.08); border-left: 2px solid var(--red); }
.playlist-thumb {
  width: 80px;
  height: 52px;
  object-fit: cover;
  flex-shrink: 0;
  background: var(--bg);
}
.playlist-thumb-placeholder {
  width: 80px;
  height: 52px;
  flex-shrink: 0;
  background: var(--bg);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 18px;
  color: var(--text-muted);
}
.playlist-info {
  padding: 6px 10px;
  flex: 1;
  min-width: 0;
}
.playlist-cat {
  font-size: 9px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .07em;
  color: var(--text-muted);
}
.playlist-title {
  font-size: 11px;
  font-weight: 600;
  line-height: 1.3;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  margin-top: 2px;
}

/* ── DEAL WIRE PANEL ── */
#panel-news {
  grid-column: 2;
  grid-row: 1 / 3;
}
.news-list { display: flex; flex-direction: column; }
.news-item {
  display: flex;
  gap: 10px;
  padding: 10px 16px;
  border-bottom: 1px solid var(--border);
  transition: background .15s;
  text-decoration: none;
  color: inherit;
}
.news-item:hover { background: var(--surface); }
.news-item:last-child { border-bottom: none; }
.news-thumb {
  width: 52px;
  height: 38px;
  object-fit: cover;
  border-radius: 4px;
  flex-shrink: 0;
  background: var(--bg);
}
.news-thumb-placeholder {
  width: 52px;
  height: 38px;
  border-radius: 4px;
  flex-shrink: 0;
  background: var(--border);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 14px;
}
.news-content { flex: 1; min-width: 0; }
.news-cat {
  font-size: 9px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .07em;
  margin-bottom: 2px;
}
.news-headline {
  font-size: 12px;
  font-weight: 600;
  line-height: 1.35;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* Category color coding */
.cat-Multifamily { color: #60a5fa; }
.cat-Financing   { color: var(--gold); }
.cat-Industrial  { color: #a78bfa; }
.cat-Sales       { color: var(--green); }
.cat-Office      { color: #fb923c; }
.cat-Retail      { color: #34d399; }
.cat-Featured    { color: var(--red); }
.cat-default     { color: var(--text-muted); }

/* ── AD SIDEBAR PANEL ── */
#panel-ad-sidebar {
  grid-column: 2;
  grid-row: 3;
}

/* ── VIDEO LIBRARY PANEL ── */
#panel-library {
  grid-column: 1 / -1;
  grid-row: 4;
}

/* Library controls */
.library-controls {
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
}
.ctrl-label {
  font-size: 10px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .07em;
  color: var(--text-muted);
  flex-shrink: 0;
}
.ctrl-select {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text);
  font-family: var(--sans);
  font-size: 12px;
  font-weight: 500;
  padding: 5px 10px;
  cursor: pointer;
  outline: none;
  transition: border-color .15s;
  appearance: none;
  -webkit-appearance: none;
  padding-right: 26px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' fill='none'%3E%3Cpath d='M1 1l4 4 4-4' stroke='%2364748b' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 8px center;
}
.ctrl-select:hover, .ctrl-select:focus { border-color: var(--border-hi); }

.density-wrap {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-left: auto;
}
.density-icon { font-size: 13px; color: var(--text-muted); }
input[type=range].density-slider {
  -webkit-appearance: none;
  appearance: none;
  width: 100px;
  height: 4px;
  background: var(--border);
  border-radius: 2px;
  outline: none;
  cursor: pointer;
}
input[type=range].density-slider::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 14px; height: 14px;
  border-radius: 50%;
  background: var(--red);
  cursor: pointer;
  transition: transform .1s;
}
input[type=range].density-slider::-webkit-slider-thumb:hover { transform: scale(1.2); }
input[type=range].density-slider::-moz-range-thumb {
  width: 14px; height: 14px;
  border-radius: 50%;
  background: var(--red);
  border: none;
  cursor: pointer;
}

/* Video grid */
#video-grid {
  display: grid;
  gap: 12px;
  margin-top: 14px;
  --cols: 4;
  grid-template-columns: repeat(var(--cols), 1fr);
}
.vid-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  overflow: hidden;
  transition: border-color .15s, transform .15s;
  display: flex;
  flex-direction: column;
  position: relative;
}
.vid-card:hover { border-color: var(--border-hi); transform: translateY(-2px); }

.vid-card-thumb-wrap {
  position: relative;
  padding-top: 56.25%;
  background: var(--bg);
  flex-shrink: 0;
}
.vid-card-thumb {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.vid-card-thumb-placeholder {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 28px;
  color: var(--text-muted);
}
.vid-play-icon {
  position: absolute;
  top: 50%; left: 50%;
  transform: translate(-50%, -50%);
  width: 36px; height: 36px;
  border-radius: 50%;
  background: rgba(0,0,0,.6);
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  transition: opacity .15s;
}
.vid-play-icon::after {
  content: '';
  border-style: solid;
  border-width: 7px 0 7px 13px;
  border-color: transparent transparent transparent #fff;
  margin-left: 2px;
}
.vid-card:hover .vid-play-icon { opacity: 1; }

.sponsored-flag {
  position: absolute;
  top: 8px; left: 8px;
  background: var(--gold);
  color: #000;
  font-size: 9px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: .07em;
  padding: 2px 6px;
  border-radius: 3px;
  z-index: 2;
}
.sponsor-hover-link {
  position: absolute;
  top: 8px; right: 8px;
  background: rgba(0,0,0,.75);
  color: var(--gold);
  font-size: 9px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .06em;
  padding: 3px 7px;
  border-radius: 3px;
  z-index: 2;
  opacity: 0;
  transition: opacity .15s;
  text-decoration: none;
  border: 1px solid rgba(240,180,41,.3);
}
.vid-card:hover .sponsor-hover-link { opacity: 1; }

.vid-card-body {
  padding: 10px 12px;
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.vid-card-cat {
  font-size: 9px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .08em;
}
.vid-card-title {
  font-size: 12px;
  font-weight: 600;
  line-height: 1.35;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.vid-card-link {
  margin-top: auto;
  font-size: 10px;
  color: var(--text-muted);
  text-decoration: none;
  padding-top: 4px;
}
.vid-card-link:hover { color: var(--red); }

/* ── AD SLOTS (styled as panels) ── */
[data-ad-slot] {
  background: var(--panel);
  border: 1px dashed var(--border-hi);
  border-radius: var(--radius);
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 80px;
  color: var(--text-muted);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: .04em;
  text-align: center;
}
[data-ad-slot="home-leaderboard"] { min-height: 90px; width: 100%; }
[data-ad-slot="section-sidebar"]  { min-height: 250px; }
[data-ad-slot="footer-band"]      { min-height: 90px; }

/* ── FOOTER ── */
footer {
  background: var(--surface);
  border-top: 1px solid var(--border);
  padding: 40px 20px 24px;
  margin-top: 20px;
}
.footer-inner {
  max-width: 1600px;
  margin: 0 auto;
  display: grid;
  grid-template-columns: 1fr auto auto auto;
  gap: 40px;
  align-items: start;
}
.footer-brand .wordmark { font-size: 20px; margin-bottom: 8px; }
.footer-tagline { font-size: 12px; color: var(--text-muted); max-width: 240px; line-height: 1.5; }
.footer-col h4 {
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .1em;
  color: var(--text-muted);
  margin-bottom: 12px;
}
.footer-col a {
  display: block;
  font-size: 13px;
  color: var(--text-dim);
  margin-bottom: 8px;
  transition: color .15s;
}
.footer-col a:hover { color: var(--text); }
.footer-bottom {
  max-width: 1600px;
  margin: 24px auto 0;
  padding-top: 16px;
  border-top: 1px solid var(--border);
  font-size: 11px;
  color: var(--text-muted);
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
}

/* ── SWITCH CHIP ── */
.switch-chip {
  position: fixed;
  bottom: 20px;
  right: 20px;
  background: var(--surface);
  border: 1px solid var(--border-hi);
  border-radius: 20px;
  padding: 8px 14px;
  font-size: 11px;
  font-weight: 600;
  color: var(--text-dim);
  text-decoration: none;
  z-index: 200;
  transition: all .15s;
  display: flex;
  align-items: center;
  gap: 6px;
  backdrop-filter: blur(8px);
  box-shadow: 0 4px 20px rgba(0,0,0,.4);
}
.switch-chip:hover { color: var(--text); border-color: var(--red); }
.switch-chip .chip-icon { color: var(--red); }

/* ── LOADING STATE ── */
.skeleton {
  background: linear-gradient(90deg, var(--surface) 25%, var(--border) 50%, var(--surface) 75%);
  background-size: 200% 100%;
  animation: shimmer 1.5s infinite;
  border-radius: var(--radius-sm);
}
@keyframes shimmer {
  0%   { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}

/* ── ERROR STATE ── */
.empty-state {
  color: var(--text-muted);
  font-size: 12px;
  text-align: center;
  padding: 20px;
}

/* ── RESPONSIVE ── */
@media (max-width: 1100px) {
  .dashboard-wrap {
    grid-template-columns: 1fr;
    grid-template-rows: auto;
  }
  #panel-video { grid-column: 1; grid-row: 1; }
  #panel-news  { grid-column: 1; grid-row: 2; }
  #panel-market { grid-column: 1; grid-row: 3; }
  #panel-ad-sidebar { grid-column: 1; grid-row: 4; }
  #panel-library { grid-column: 1; grid-row: 5; }

  .video-layout { grid-template-columns: 1fr; }
  .video-playlist { flex-direction: row; overflow-x: auto; overflow-y: hidden; max-height: none; border-left: none; border-top: 1px solid var(--border); }
  .playlist-item { flex-direction: column; min-width: 140px; border-bottom: none; border-right: 1px solid var(--border); }
  .playlist-thumb, .playlist-thumb-placeholder { width: 100%; height: 80px; }

  #video-grid { --cols: 2; }
}
@media (max-width: 680px) {
  .header-inner { padding: 0 12px; gap: 8px; }
  .wordmark { font-size: 18px; }
  .nav-btn { display: none; }
  .dashboard-wrap { padding: 10px 12px 80px; gap: 10px; }
  #video-grid { --cols: 1; }
  .market-grid { grid-template-columns: repeat(2, 1fr); }
  .footer-inner { grid-template-columns: 1fr 1fr; gap: 24px; }
  .footer-brand { grid-column: 1 / -1; }
}
</style>
</head>
<body>

<!-- ── TICKER STRIP ── -->
<div id="ticker-strip" role="marquee" aria-label="CRE Market Ticker">
  <div class="ticker-label">
    <span class="live-dot">Live</span>
    <span style="display:none" id="ticker-time"></span>
  </div>
  <div id="ticker-track-wrap">
    <div id="ticker-track">
      <div class="ticker-item">
        <span class="t-label">Loading…</span>
      </div>
    </div>
  </div>
</div>

<!-- ── HEADER ── -->
<header id="site-header">
  <div class="header-inner">
    <div class="header-left">
      <button class="hamburger-btn" aria-label="Explore menu">
        <span></span><span></span><span></span>
      </button>
      <button class="nav-btn" onclick="location='/'">Explore</button>
    </div>

    <a href="/" class="wordmark" aria-label="RENTV Home">REN<span class="tv">TV</span></a>

    <div class="header-right">
      <button class="nav-btn">Sections</button>
      <a href="/advertise" class="advertise-btn" aria-label="Advertise on RENTV">Advertise →</a>
    </div>
  </div>
</header>

<!-- ── LEADERBOARD AD ── -->
<div id="ad-leaderboard">
  <div data-ad-slot="home-leaderboard" aria-label="Advertisement"></div>
</div>

<!-- ── DASHBOARD MAIN ── -->
<main class="dashboard-wrap">

  <!-- 1. LIVE VIDEO PANEL -->
  <section class="panel" id="panel-video" aria-label="Featured Video">
    <div class="panel-header">
      <h2 class="panel-title">
        <span class="icon">▶</span>
        Live Video — The REview
        <span class="live-dot" style="margin-left:8px">Live</span>
      </h2>
      <a href="https://www.rentvreview.com" target="_blank" rel="noopener" style="font-size:11px;color:var(--text-muted);transition:color .15s" onmouseover="this.style.color='var(--red)'" onmouseout="this.style.color='var(--text-muted)'">rentvreview.com →</a>
    </div>
    <div class="video-layout">
      <div class="video-main">
        <div class="video-embed-wrap" id="featured-embed-wrap">
          <div style="position:absolute;inset:0;display:flex;align-items:center;justify-content:center;color:var(--text-muted);font-size:13px;">Loading…</div>
        </div>
        <div class="video-meta" id="featured-meta">
          <div class="video-cat" id="featured-cat"></div>
          <div class="video-title" id="featured-title"></div>
          <div class="video-desc" id="featured-desc"></div>
        </div>
      </div>
      <div class="video-playlist" id="video-playlist"></div>
    </div>
  </section>

  <!-- 2. DEAL WIRE PANEL -->
  <section class="panel" id="panel-news" aria-label="Deal Wire">
    <div class="panel-header">
      <h2 class="panel-title"><span class="icon">◈</span>Deal Wire</h2>
      <a href="/news" style="font-size:11px;color:var(--text-muted);transition:color .15s" onmouseover="this.style.color='var(--red)'" onmouseout="this.style.color='var(--text-muted)'">All deals →</a>
    </div>
    <div id="news-list" class="news-list" role="list">
      <div class="empty-state">Loading…</div>
    </div>
  </section>

  <!-- 3. MARKET PULSE PANEL -->
  <section class="panel" id="panel-market" aria-label="Market Pulse">
    <div class="panel-header">
      <h2 class="panel-title"><span class="icon">◎</span>Market Pulse</h2>
      <span class="mono" style="font-size:10px;color:var(--text-muted)" id="market-time"></span>
    </div>
    <div class="panel-body">
      <div class="market-grid" id="market-grid">
        <div class="empty-state" style="grid-column:1/-1">Loading…</div>
      </div>
    </div>
  </section>

  <!-- 4. AD SIDEBAR PANEL -->
  <section class="panel" id="panel-ad-sidebar" style="padding:14px" aria-label="Advertisement">
    <div data-ad-slot="section-sidebar" aria-label="Advertisement"></div>
  </section>

  <!-- 5. VIDEO LIBRARY PANEL -->
  <section class="panel" id="panel-library" aria-label="Video Library">
    <div class="panel-header">
      <h2 class="panel-title"><span class="icon">⊞</span>Video Library — 155 Videos</h2>
      <div class="library-controls" role="group" aria-label="Library display controls">
        <label class="ctrl-label" for="lib-sort">Sort</label>
        <select id="lib-sort" class="ctrl-select" aria-label="Sort videos">
          <option value="newest">Newest</option>
          <option value="views">Most Viewed</option>
          <option value="cat">Category</option>
          <option value="title">Title A→Z</option>
        </select>
        <div class="density-wrap">
          <span class="density-icon" aria-hidden="true">⊟</span>
          <input type="range" id="lib-density" class="density-slider" min="2" max="6" step="1" value="4" aria-label="Grid column density">
          <span class="density-icon" aria-hidden="true">⊞</span>
        </div>
      </div>
    </div>
    <div class="panel-body" style="padding-top:0">
      <div id="video-grid" role="list" aria-label="Video cards"></div>
      <div id="lib-loading" class="empty-state">Loading library…</div>
    </div>
  </section>

</main>

<!-- ── FOOTER AD BAND ── -->
<div style="max-width:1600px;margin:0 auto;padding:0 20px">
  <div data-ad-slot="footer-band" aria-label="Advertisement"></div>
</div>

<!-- ── FOOTER ── -->
<footer>
  <div class="footer-inner">
    <div class="footer-brand">
      <div class="wordmark">REN<span class="tv">TV</span></div>
      <p class="footer-tagline">Western U.S. commercial real estate news. The deals break here first — since 1998.</p>
    </div>
    <div class="footer-col">
      <h4>Sections</h4>
      <a href="/news">News</a>
      <a href="/deals">Deals</a>
      <a href="/financing">Financing</a>
      <a href="/industrial">Industrial</a>
      <a href="/multifamily">Multifamily</a>
    </div>
    <div class="footer-col">
      <h4>Intelligence</h4>
      <a href="/markets">Market Data</a>
      <a href="/cre-talk">CRE Talk</a>
      <a href="https://www.rentvreview.com" target="_blank" rel="noopener">The REview</a>
      <a href="/conferences">Conferences</a>
    </div>
    <div class="footer-col">
      <h4>Company</h4>
      <a href="/about">About</a>
      <a href="/advertise">Advertise</a>
      <a href="/audience">Audience</a>
      <a href="/contact">Contact</a>
    </div>
  </div>
  <div class="footer-bottom">
    <span>© 2026 <strong style="color:var(--text-dim)">RENTV</strong> — Western CRE Authority</span>
    <a href="/advertise" style="color:var(--red);font-weight:600">Advertise on RENTV →</a>
  </div>
</footer>

<!-- ── SWITCH CHIP ── -->
<a href="/2026/" class="switch-chip" aria-label="Switch to another 2026 template">
  <span class="chip-icon">◆</span>
  2026 Templates — switch
</a>

<!-- ── AD SYSTEM ── -->
<script src="/ad-slot.js" defer></script>

<!-- ── DASHBOARD SCRIPTS ── -->
<script>
(function () {
  'use strict';

  /* ─── HELPERS ─── */
  function esc(s) {
    return String(s ?? '').replace(/[&<>"']/g, c => ({
      '&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'
    })[c]);
  }
  function dirClass(dir) {
    if (dir === 'up')   return 'up';
    if (dir === 'down') return 'dn';
    return 'flat';
  }
  function dirArrow(dir) {
    if (dir === 'up')   return '▲';
    if (dir === 'down') return '▼';
    return '—';
  }
  function catClass(cat) {
    const map = {
      Multifamily:'cat-Multifamily', Financing:'cat-Financing',
      Industrial:'cat-Industrial',   Sales:'cat-Sales',
      Office:'cat-Office',           Retail:'cat-Retail',
      Featured:'cat-Featured'
    };
    return map[cat] || 'cat-default';
  }
  async function fetchJSON(url) {
    try {
      const r = await fetch(url);
      if (!r.ok) throw new Error(r.status);
      return await r.json();
    } catch(e) {
      console.warn('fetchJSON failed', url, e);
      return null;
    }
  }

  /* ─── TICKER ─── */
  async function initTicker() {
    const data = await fetchJSON('/api/ticker');
    if (!data) return;
    const items = Object.values(data).filter(v => v && v.label && v.value);
    const track = document.getElementById('ticker-track');
    track.innerHTML = '';

    // Build items × 3 for seamless loop
    const buildSet = () => items.map(item => {
      const dc = dirClass(item.dir);
      const arrow = dirArrow(item.dir);
      const chg = item.change_pct != null
        ? `${item.change_pct > 0 ? '+' : ''}${item.change_pct}%`
        : item.change_bps != null
          ? `${item.change_bps > 0 ? '+' : ''}${item.change_bps} bps`
          : '';
      return `<div class="ticker-item">
        <span class="t-label">${esc(item.label)}</span>
        <span class="t-value mono ${dc}">${esc(item.value)}</span>
        ${chg ? `<span class="t-chg mono ${dc}">${arrow} ${esc(chg)}</span>` : ''}
      </div>`;
    }).join('');

    track.innerHTML = buildSet() + buildSet() + buildSet();

    // Animate
    let x = 0;
    const singleW = track.scrollWidth / 3;
    let last = null;
    function tick(ts) {
      if (last !== null) {
        const dt = ts - last;
        x += dt * 0.04; // px/ms speed
        if (x >= singleW) x -= singleW;
        track.style.transform = `translateX(-${x.toFixed(2)}px)`;
      }
      last = ts;
      requestAnimationFrame(tick);
    }
    requestAnimationFrame(tick);
  }

  /* ─── MARKET PULSE ─── */
  async function initMarket() {
    const data = await fetchJSON('/api/markets');
    const grid = document.getElementById('market-grid');
    if (!data) { grid.innerHTML = '<div class="empty-state">Market data unavailable</div>'; return; }

    const timeEl = document.getElementById('market-time');
    if (data.fetched_at) {
      const d = new Date(data.fetched_at);
      timeEl.textContent = d.toLocaleTimeString([], {hour:'2-digit',minute:'2-digit'}) + ' · ' + (data.source || '');
    }

    let html = '';

    // Treasury Curve
    if (data.curve?.length) {
      html += `<div class="section-divider">Treasury Curve</div>`;
      data.curve.forEach(item => {
        const dc = dirClass(item.dir);
        const chg = item.change_bps != null ? `${item.change_bps > 0 ? '+' : ''}${item.change_bps} bps` : '';
        html += `<div class="kpi-tile">
          <div class="kpi-label">${esc(item.label)}</div>
          <div class="kpi-value mono ${dc}">${esc(item.value)}</div>
          ${chg ? `<div class="kpi-delta mono ${dc}">${dirArrow(item.dir)} ${esc(chg)}</div>` : ''}
        </div>`;
      });
    }

    // Spreads
    if (data.spreads?.length) {
      html += `<div class="section-divider">Spreads</div>`;
      data.spreads.forEach(item => {
        const dc = dirClass(item.dir);
        html += `<div class="kpi-tile">
          <div class="kpi-label">${esc(item.label)}</div>
          <div class="kpi-value mono ${dc}">${esc(item.value)}</div>
        </div>`;
      });
    }

    // REITs
    if (data.reits?.length) {
      html += `<div class="section-divider">REIT Indices</div>`;
      data.reits.forEach(item => {
        const dc = dirClass(item.dir);
        const chg = item.change_pct != null ? `${item.change_pct > 0 ? '+' : ''}${item.change_pct}%` : '';
        html += `<div class="kpi-tile">
          <div class="kpi-label">${esc(item.label)}</div>
          <div class="kpi-value mono ${dc}">${esc(item.value)}</div>
          ${chg ? `<div class="kpi-delta mono ${dc}">${dirArrow(item.dir)} ${esc(chg)}</div>` : ''}
        </div>`;
      });
    }

    // Fundamentals
    if (data.fundamentals?.length) {
      html += `<div class="section-divider">Fundamentals</div>`;
      data.fundamentals.forEach(item => {
        html += `<div class="kpi-tile">
          <div class="kpi-label">${esc(item.label)}</div>
          <div class="kpi-value mono">${esc(item.value)}</div>
          ${item.as_of ? `<div class="kpi-source">${esc(item.as_of)} · ${esc(item.source || '')}</div>` : ''}
        </div>`;
      });
    }

    grid.innerHTML = html || '<div class="empty-state">No data</div>';
  }

  /* ─── LIVE VIDEO ─── */
  let allVideos = [];

  async function initVideo() {
    const data = await fetchJSON('/api/videos');
    if (!data?.items) return;

    allVideos = data.items.filter(v => v.embed || v.mp4);
    // Move pinned to front
    const pinned = allVideos.find(v => v.pinned);
    if (pinned) {
      allVideos = [pinned, ...allVideos.filter(v => v !== pinned)];
    }

    if (!allVideos.length) return;

    // Render playlist (skip first 1 = featured)
    renderPlaylist(allVideos);
    setFeatured(allVideos[0], 0);
  }

  function renderPlaylist(videos) {
    const container = document.getElementById('video-playlist');
    container.innerHTML = videos.slice(0, 10).map((v, i) => {
      const hasThumb = v.thumb && !v.thumb.includes('/media/originals/');
      return `<div class="playlist-item ${i === 0 ? 'active' : ''}" data-idx="${i}" role="button" tabindex="0" aria-label="${esc(v.title)}">
        ${hasThumb
          ? `<img class="playlist-thumb" src="${esc(v.thumb)}" alt="" loading="lazy">`
          : `<div class="playlist-thumb-placeholder">▶</div>`
        }
        <div class="playlist-info">
          <div class="playlist-cat ${catClass(v.cat)}">${esc(v.cat || '')}</div>
          <div class="playlist-title">${esc(v.title)}</div>
        </div>
      </div>`;
    }).join('');

    container.addEventListener('click', e => {
      const item = e.target.closest('.playlist-item');
      if (!item) return;
      const idx = parseInt(item.dataset.idx, 10);
      container.querySelectorAll('.playlist-item').forEach(el => el.classList.remove('active'));
      item.classList.add('active');
      setFeatured(videos[idx], idx);
    });
    container.addEventListener('keydown', e => {
      if (e.key === 'Enter' || e.key === ' ') {
        e.preventDefault();
        e.target.closest('.playlist-item')?.click();
      }
    });
  }

  function setFeatured(video, idx) {
    const wrap = document.getElementById('featured-embed-wrap');
    const catEl  = document.getElementById('featured-cat');
    const titleEl = document.getElementById('featured-title');
    const descEl  = document.getElementById('featured-desc');

    catEl.textContent  = video.cat || '';
    catEl.className    = 'video-cat ' + catClass(video.cat);
    titleEl.textContent = video.title || '';
    descEl.textContent  = video.desc || '';

    if (video.embed) {
      wrap.innerHTML = `<iframe
        src="${esc(video.embed)}?rel=0&modestbranding=1"
        title="${esc(video.title)}"
        allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
        allowfullscreen
        loading="lazy"
      ></iframe>`;
    } else if (video.mp4) {
      wrap.innerHTML = `<video controls style="position:absolute;inset:0;width:100%;height:100%;object-fit:cover" preload="metadata">
        <source src="${esc(video.mp4)}" type="video/mp4">
      </video>`;
    }
  }

  /* ─── DEAL WIRE ─── */
  async function initNews() {
    const data = await fetchJSON('/api/news');
    const list = document.getElementById('news-list');
    if (!data?.items?.length) { list.innerHTML = '<div class="empty-state">No deals found</div>'; return; }

    const items = data.items.slice(0, 20);
    list.innerHTML = items.map(item => {
      const hasImg = item.image && !item.image.includes('/img/news/news_');
      const localImg = item.image ? `https://www.rentv.com${item.image.startsWith('/') ? '' : '/'}${item.image}` : '';
      // Use rentv.com origin for images since we don't have them locally
      return `<a href="${esc(item.url || `/news/${item.id}`)}" class="news-item" target="_blank" rel="noopener" role="listitem" aria-label="${esc(item.title)}">
        ${item.image
          ? `<img class="news-thumb" src="${esc('https://www.rentv.com' + item.image)}" alt="" loading="lazy" onerror="this.style.display='none'">`
          : `<div class="news-thumb-placeholder">◈</div>`
        }
        <div class="news-content">
          <div class="news-cat ${catClass(item.cat)}">${esc(item.cat || '')}</div>
          <div class="news-headline">${esc(item.title)}</div>
        </div>
      </a>`;
    }).join('');
  }

  /* ─── VIDEO LIBRARY ─── */
  let libItems = [];
  let libSortBy  = localStorage.getItem('lib-sort')  || 'newest';
  let libDensity = parseInt(localStorage.getItem('lib-density') || '4', 10);

  async function initLibrary() {
    const loadEl = document.getElementById('lib-loading');
    const data = await fetchJSON('/api/ad/inventory');
    if (!data?.items) { loadEl.textContent = 'Library unavailable.'; return; }
    loadEl.style.display = 'none';

    libItems = data.items;

    // Restore controls
    const sortEl    = document.getElementById('lib-sort');
    const densityEl = document.getElementById('lib-density');
    sortEl.value    = libSortBy;
    densityEl.value = libDensity;
    applyDensity(libDensity);

    sortEl.addEventListener('change', () => {
      libSortBy = sortEl.value;
      localStorage.setItem('lib-sort', libSortBy);
      renderLibrary();
    });
    densityEl.addEventListener('input', () => {
      libDensity = parseInt(densityEl.value, 10);
      localStorage.setItem('lib-density', libDensity);
      applyDensity(libDensity);
    });

    renderLibrary();
  }

  function applyDensity(cols) {
    document.getElementById('video-grid').style.setProperty('--cols', cols);
  }

  function sortedItems() {
    const items = [...libItems];
    switch (libSortBy) {
      case 'views':
        return items.sort((a, b) => (b.plays || 0) - (a.plays || 0));
      case 'cat':
        return items.sort((a, b) => (a.cat || '').localeCompare(b.cat || ''));
      case 'title':
        return items.sort((a, b) => (a.title || '').localeCompare(b.title || ''));
      case 'newest':
      default:
        return items.sort((a, b) => {
          const da = a.date ? new Date(a.date) : 0;
          const db = b.date ? new Date(b.date) : 0;
          return db - da;
        });
    }
  }

  function renderLibrary() {
    const grid = document.getElementById('video-grid');
    const items = sortedItems();
    if (!items.length) { grid.innerHTML = '<div class="empty-state">No videos found.</div>'; return; }

    grid.innerHTML = items.map(item => {
      const sp = item.sponsorship || {};
      const isSold = sp.status === 'sold';
      const advertiser = sp.advertiser || '';
      const hasThumb = item.thumb && !item.thumb.startsWith('/media');
      const linkUrl = item.view_url || (item.embed ? item.embed : '#');

      return `<article class="vid-card" role="listitem">
        <a href="${esc(linkUrl)}" target="_blank" rel="noopener" aria-label="${esc(item.title)}" tabindex="0">
          <div class="vid-card-thumb-wrap">
            ${hasThumb
              ? `<img class="vid-card-thumb" src="${esc(item.thumb)}" alt="" loading="lazy" onerror="this.style.display='none'">`
              : `<div class="vid-card-thumb-placeholder">▶</div>`
            }
            <div class="vid-play-icon" aria-hidden="true"></div>
            ${isSold
              ? `<div class="sponsored-flag">Sponsored · ${esc(advertiser)}</div>`
              : `<a class="sponsor-hover-link" href="/advertise?video=${esc(item.uid)}" aria-label="Sponsor this video" tabindex="-1">Sponsor this →</a>`
            }
          </div>
          <div class="vid-card-body">
            <div class="vid-card-cat ${catClass(item.cat)}">${esc(item.cat || '')}</div>
            <div class="vid-card-title">${esc(item.title)}</div>
          </div>
        </a>
        ${item.view_url && item.view_url !== linkUrl
          ? `<div style="padding:0 12px 10px"><a class="vid-card-link" href="${esc(item.view_url)}" target="_blank" rel="noopener">Watch on REview →</a></div>`
          : ''
        }
      </article>`;
    }).join('');
  }

  /* ─── BOOTSTRAP ─── */
  document.addEventListener('DOMContentLoaded', () => {
    initTicker();
    initMarket();
    initVideo();
    initNews();
    initLibrary();
  });

})();
</script>
<script src="/2026/elements.js" defer></script>
<script src="/template-toggle.js" defer></script>
</body>
</html>