[object Object]

← back to Rentv 2026

rentv-v1: 10 ADMIN dashboard versions (distinct layouts over real subscriber/feed/video/market data) + /admin picker

c7117dee6770043a0f66699d7502b1a461477d64 · 2026-07-21 15:22:00 -0700 · Steve Abrams

Files touched

Diff

commit c7117dee6770043a0f66699d7502b1a461477d64
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Tue Jul 21 15:22:00 2026 -0700

    rentv-v1: 10 ADMIN dashboard versions (distinct layouts over real subscriber/feed/video/market data) + /admin picker
---
 admin-versions.mjs          | 58 +++++++++++++++++++++++++++++
 public/admin/analytics.html | 14 +++++++
 public/admin/cards.html     | 14 +++++++
 public/admin/classic.html   | 14 +++++++
 public/admin/command.html   | 14 +++++++
 public/admin/console.html   | 14 +++++++
 public/admin/index.html     | 18 +++++++++
 public/admin/kanban.html    | 14 +++++++
 public/admin/minimal.html   | 14 +++++++
 public/admin/render.js      | 90 +++++++++++++++++++++++++++++++++++++++++++++
 public/admin/sidebar.html   | 14 +++++++
 public/admin/split.html     | 14 +++++++
 public/admin/table.html     | 14 +++++++
 server.js                   |  2 +
 14 files changed, 308 insertions(+)

diff --git a/admin-versions.mjs b/admin-versions.mjs
new file mode 100644
index 00000000..eba11129
--- /dev/null
+++ b/admin-versions.mjs
@@ -0,0 +1,58 @@
+#!/usr/bin/env node
+/* rentv-v1 — generate 10 ADMIN dashboard versions (distinct layouts over the real
+   gated data) + an /admin picker. Boomer picks the admin direction he likes. $0. */
+import { writeFileSync, mkdirSync } from 'node:fs';
+import { dirname, join } from 'node:path';
+import { fileURLToPath } from 'node:url';
+const OUT = join(dirname(fileURLToPath(import.meta.url)), 'public', 'admin');
+mkdirSync(OUT, { recursive: true });
+
+const V = [
+  ['classic','Classic Dashboard','KPI cards up top, two data tables below. The safe, familiar admin.'],
+  ['sidebar','Sidebar Console','Left nav rail + main panel — the SaaS-standard admin shell.'],
+  ['console','Ops Console','Monospace metrics row + a live activity log. For the data-desk operator.'],
+  ['cards','Module Grid','Independent widget cards you can scan at a glance.'],
+  ['table','Data Table','Dense spreadsheet-style rows — power-user, keyboard-first feel.'],
+  ['analytics','Analytics First','One giant headline metric, then supporting stats and a table.'],
+  ['split','Master–Detail','Scrollable list on the left, KPI rail on the right.'],
+  ['kanban','Kanban Board','Stories grouped into columns by sector — editorial pipeline view.'],
+  ['minimal','Minimal','Quiet single-column stat sheet. No chrome, just the numbers.'],
+  ['command','Command Center','Multi-panel grid — every surface (subs, feed, video, market) at once.'],
+];
+
+const GF = "https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700;800&family=Playfair+Display:wght@700&display=swap";
+// one professional dark admin palette (versions differ by LAYOUT, not color)
+const SHELL = `
+:root{--a:#c8102e;--bg:#0e1116;--pan:#161b22;--ln:#242b34;--sub:#8b949e;--ink:#e6edf3}
+*{box-sizing:border-box;margin:0;padding:0}body{font-family:'Inter',system-ui,sans-serif;background:var(--bg);color:var(--ink);line-height:1.5}
+a{color:inherit;text-decoration:none}.wrap{max-width:1240px;margin:0 auto;padding:0 22px}
+.abar{background:#0a0d11;border-bottom:1px solid var(--ln)}.abar .wrap{display:flex;justify-content:space-between;align-items:center;height:52px}
+.abrand{font-family:'Playfair Display',serif;font-size:20px}.abrand span{color:var(--a)}.abrand small{font-family:'Inter';font-size:10px;letter-spacing:.2em;text-transform:uppercase;color:var(--sub);margin-left:8px}
+.abar a{font-size:12px;color:var(--sub)}.abar a:hover{color:var(--ink)}
+.ah{padding:20px 0 4px}.ah h1{font-size:20px}.ah p{color:var(--sub);font-size:13px}
+#app{padding:18px 0 60px;min-height:60vh}`;
+
+function page(key, title, desc) {
+  return `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
+<title>RENTV Admin — ${title}</title><link href="${GF}" rel="stylesheet"><style>${SHELL}</style></head>
+<body data-ver="${key}" class="ver-${key}">
+<div class="abar"><div class="wrap"><div class="abrand">REN<span>TV</span> Admin<small>${title}</small></div><a href="/admin">← all 10 admin versions</a></div></div>
+<div class="wrap"><div class="ah"><h1>${title}</h1><p>${desc} · live gated data</p></div><main id="app"><p style="color:var(--sub);padding:30px">Loading admin data…</p></main></div>
+<script src="/admin/render.js"></script></body></html>`;
+}
+
+const cards = V.map(([k,t,d])=>`<a class="vc" href="/admin/${k}"><div class="tag">Admin</div><h3>${t}</h3><p>${d}</p><span class="go">Open →</span></a>`).join('');
+const picker = `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
+<title>RENTV — 10 Admin Versions</title><link href="${GF}" rel="stylesheet"><style>${SHELL}
+.hd{padding:44px 0 6px;text-align:center}.hd h1{font-family:'Playfair Display',serif;font-size:clamp(28px,5vw,48px)}.hd h1 span{color:var(--a)}.hd p{color:var(--sub);margin-top:8px}
+.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:16px;padding:28px 0 60px}
+.vc{background:var(--pan);border:1px solid var(--ln);border-radius:10px;padding:20px;transition:.2s}
+.vc:hover{transform:translateY(-4px);border-color:var(--a);box-shadow:0 16px 40px rgba(0,0,0,.5)}
+.tag{font-size:10px;text-transform:uppercase;letter-spacing:.14em;color:var(--a);font-weight:800}.vc h3{font-size:20px;margin:6px 0}.vc p{color:var(--sub);font-size:14px;min-height:40px}.vc .go{color:var(--a);font-weight:800;font-size:12px;text-transform:uppercase;letter-spacing:.06em}</style></head>
+<body><div class="abar"><div class="wrap"><div class="abrand">REN<span>TV</span> Admin</div><a href="/">← main site</a></div></div>
+<header class="hd"><div class="wrap"><h1>REN<span>TV</span> · 10 Admin Versions</h1><p>Ten complete back-office directions over the real subscriber / feed / video / market data. Open each and pick the one to run.</p></div></header>
+<div class="wrap"><div class="grid">${cards}</div></div></body></html>`;
+
+for (const [k,t,d] of V) writeFileSync(join(OUT, k + '.html'), page(k,t,d));
+writeFileSync(join(OUT, 'index.html'), picker);
+console.log('Wrote 10 admin versions + picker: ' + V.map(v=>v[0]).join(', '));
diff --git a/public/admin/analytics.html b/public/admin/analytics.html
new file mode 100644
index 00000000..c175764b
--- /dev/null
+++ b/public/admin/analytics.html
@@ -0,0 +1,14 @@
+<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
+<title>RENTV Admin — Analytics First</title><link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700;800&family=Playfair+Display:wght@700&display=swap" rel="stylesheet"><style>
+:root{--a:#c8102e;--bg:#0e1116;--pan:#161b22;--ln:#242b34;--sub:#8b949e;--ink:#e6edf3}
+*{box-sizing:border-box;margin:0;padding:0}body{font-family:'Inter',system-ui,sans-serif;background:var(--bg);color:var(--ink);line-height:1.5}
+a{color:inherit;text-decoration:none}.wrap{max-width:1240px;margin:0 auto;padding:0 22px}
+.abar{background:#0a0d11;border-bottom:1px solid var(--ln)}.abar .wrap{display:flex;justify-content:space-between;align-items:center;height:52px}
+.abrand{font-family:'Playfair Display',serif;font-size:20px}.abrand span{color:var(--a)}.abrand small{font-family:'Inter';font-size:10px;letter-spacing:.2em;text-transform:uppercase;color:var(--sub);margin-left:8px}
+.abar a{font-size:12px;color:var(--sub)}.abar a:hover{color:var(--ink)}
+.ah{padding:20px 0 4px}.ah h1{font-size:20px}.ah p{color:var(--sub);font-size:13px}
+#app{padding:18px 0 60px;min-height:60vh}</style></head>
+<body data-ver="analytics" class="ver-analytics">
+<div class="abar"><div class="wrap"><div class="abrand">REN<span>TV</span> Admin<small>Analytics First</small></div><a href="/admin">← all 10 admin versions</a></div></div>
+<div class="wrap"><div class="ah"><h1>Analytics First</h1><p>One giant headline metric, then supporting stats and a table. · live gated data</p></div><main id="app"><p style="color:var(--sub);padding:30px">Loading admin data…</p></main></div>
+<script src="/admin/render.js"></script></body></html>
\ No newline at end of file
diff --git a/public/admin/cards.html b/public/admin/cards.html
new file mode 100644
index 00000000..9cf60866
--- /dev/null
+++ b/public/admin/cards.html
@@ -0,0 +1,14 @@
+<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
+<title>RENTV Admin — Module Grid</title><link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700;800&family=Playfair+Display:wght@700&display=swap" rel="stylesheet"><style>
+:root{--a:#c8102e;--bg:#0e1116;--pan:#161b22;--ln:#242b34;--sub:#8b949e;--ink:#e6edf3}
+*{box-sizing:border-box;margin:0;padding:0}body{font-family:'Inter',system-ui,sans-serif;background:var(--bg);color:var(--ink);line-height:1.5}
+a{color:inherit;text-decoration:none}.wrap{max-width:1240px;margin:0 auto;padding:0 22px}
+.abar{background:#0a0d11;border-bottom:1px solid var(--ln)}.abar .wrap{display:flex;justify-content:space-between;align-items:center;height:52px}
+.abrand{font-family:'Playfair Display',serif;font-size:20px}.abrand span{color:var(--a)}.abrand small{font-family:'Inter';font-size:10px;letter-spacing:.2em;text-transform:uppercase;color:var(--sub);margin-left:8px}
+.abar a{font-size:12px;color:var(--sub)}.abar a:hover{color:var(--ink)}
+.ah{padding:20px 0 4px}.ah h1{font-size:20px}.ah p{color:var(--sub);font-size:13px}
+#app{padding:18px 0 60px;min-height:60vh}</style></head>
+<body data-ver="cards" class="ver-cards">
+<div class="abar"><div class="wrap"><div class="abrand">REN<span>TV</span> Admin<small>Module Grid</small></div><a href="/admin">← all 10 admin versions</a></div></div>
+<div class="wrap"><div class="ah"><h1>Module Grid</h1><p>Independent widget cards you can scan at a glance. · live gated data</p></div><main id="app"><p style="color:var(--sub);padding:30px">Loading admin data…</p></main></div>
+<script src="/admin/render.js"></script></body></html>
\ No newline at end of file
diff --git a/public/admin/classic.html b/public/admin/classic.html
new file mode 100644
index 00000000..13c64851
--- /dev/null
+++ b/public/admin/classic.html
@@ -0,0 +1,14 @@
+<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
+<title>RENTV Admin — Classic Dashboard</title><link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700;800&family=Playfair+Display:wght@700&display=swap" rel="stylesheet"><style>
+:root{--a:#c8102e;--bg:#0e1116;--pan:#161b22;--ln:#242b34;--sub:#8b949e;--ink:#e6edf3}
+*{box-sizing:border-box;margin:0;padding:0}body{font-family:'Inter',system-ui,sans-serif;background:var(--bg);color:var(--ink);line-height:1.5}
+a{color:inherit;text-decoration:none}.wrap{max-width:1240px;margin:0 auto;padding:0 22px}
+.abar{background:#0a0d11;border-bottom:1px solid var(--ln)}.abar .wrap{display:flex;justify-content:space-between;align-items:center;height:52px}
+.abrand{font-family:'Playfair Display',serif;font-size:20px}.abrand span{color:var(--a)}.abrand small{font-family:'Inter';font-size:10px;letter-spacing:.2em;text-transform:uppercase;color:var(--sub);margin-left:8px}
+.abar a{font-size:12px;color:var(--sub)}.abar a:hover{color:var(--ink)}
+.ah{padding:20px 0 4px}.ah h1{font-size:20px}.ah p{color:var(--sub);font-size:13px}
+#app{padding:18px 0 60px;min-height:60vh}</style></head>
+<body data-ver="classic" class="ver-classic">
+<div class="abar"><div class="wrap"><div class="abrand">REN<span>TV</span> Admin<small>Classic Dashboard</small></div><a href="/admin">← all 10 admin versions</a></div></div>
+<div class="wrap"><div class="ah"><h1>Classic Dashboard</h1><p>KPI cards up top, two data tables below. The safe, familiar admin. · live gated data</p></div><main id="app"><p style="color:var(--sub);padding:30px">Loading admin data…</p></main></div>
+<script src="/admin/render.js"></script></body></html>
\ No newline at end of file
diff --git a/public/admin/command.html b/public/admin/command.html
new file mode 100644
index 00000000..301d21c0
--- /dev/null
+++ b/public/admin/command.html
@@ -0,0 +1,14 @@
+<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
+<title>RENTV Admin — Command Center</title><link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700;800&family=Playfair+Display:wght@700&display=swap" rel="stylesheet"><style>
+:root{--a:#c8102e;--bg:#0e1116;--pan:#161b22;--ln:#242b34;--sub:#8b949e;--ink:#e6edf3}
+*{box-sizing:border-box;margin:0;padding:0}body{font-family:'Inter',system-ui,sans-serif;background:var(--bg);color:var(--ink);line-height:1.5}
+a{color:inherit;text-decoration:none}.wrap{max-width:1240px;margin:0 auto;padding:0 22px}
+.abar{background:#0a0d11;border-bottom:1px solid var(--ln)}.abar .wrap{display:flex;justify-content:space-between;align-items:center;height:52px}
+.abrand{font-family:'Playfair Display',serif;font-size:20px}.abrand span{color:var(--a)}.abrand small{font-family:'Inter';font-size:10px;letter-spacing:.2em;text-transform:uppercase;color:var(--sub);margin-left:8px}
+.abar a{font-size:12px;color:var(--sub)}.abar a:hover{color:var(--ink)}
+.ah{padding:20px 0 4px}.ah h1{font-size:20px}.ah p{color:var(--sub);font-size:13px}
+#app{padding:18px 0 60px;min-height:60vh}</style></head>
+<body data-ver="command" class="ver-command">
+<div class="abar"><div class="wrap"><div class="abrand">REN<span>TV</span> Admin<small>Command Center</small></div><a href="/admin">← all 10 admin versions</a></div></div>
+<div class="wrap"><div class="ah"><h1>Command Center</h1><p>Multi-panel grid — every surface (subs, feed, video, market) at once. · live gated data</p></div><main id="app"><p style="color:var(--sub);padding:30px">Loading admin data…</p></main></div>
+<script src="/admin/render.js"></script></body></html>
\ No newline at end of file
diff --git a/public/admin/console.html b/public/admin/console.html
new file mode 100644
index 00000000..407521e3
--- /dev/null
+++ b/public/admin/console.html
@@ -0,0 +1,14 @@
+<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
+<title>RENTV Admin — Ops Console</title><link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700;800&family=Playfair+Display:wght@700&display=swap" rel="stylesheet"><style>
+:root{--a:#c8102e;--bg:#0e1116;--pan:#161b22;--ln:#242b34;--sub:#8b949e;--ink:#e6edf3}
+*{box-sizing:border-box;margin:0;padding:0}body{font-family:'Inter',system-ui,sans-serif;background:var(--bg);color:var(--ink);line-height:1.5}
+a{color:inherit;text-decoration:none}.wrap{max-width:1240px;margin:0 auto;padding:0 22px}
+.abar{background:#0a0d11;border-bottom:1px solid var(--ln)}.abar .wrap{display:flex;justify-content:space-between;align-items:center;height:52px}
+.abrand{font-family:'Playfair Display',serif;font-size:20px}.abrand span{color:var(--a)}.abrand small{font-family:'Inter';font-size:10px;letter-spacing:.2em;text-transform:uppercase;color:var(--sub);margin-left:8px}
+.abar a{font-size:12px;color:var(--sub)}.abar a:hover{color:var(--ink)}
+.ah{padding:20px 0 4px}.ah h1{font-size:20px}.ah p{color:var(--sub);font-size:13px}
+#app{padding:18px 0 60px;min-height:60vh}</style></head>
+<body data-ver="console" class="ver-console">
+<div class="abar"><div class="wrap"><div class="abrand">REN<span>TV</span> Admin<small>Ops Console</small></div><a href="/admin">← all 10 admin versions</a></div></div>
+<div class="wrap"><div class="ah"><h1>Ops Console</h1><p>Monospace metrics row + a live activity log. For the data-desk operator. · live gated data</p></div><main id="app"><p style="color:var(--sub);padding:30px">Loading admin data…</p></main></div>
+<script src="/admin/render.js"></script></body></html>
\ No newline at end of file
diff --git a/public/admin/index.html b/public/admin/index.html
new file mode 100644
index 00000000..8337b7ad
--- /dev/null
+++ b/public/admin/index.html
@@ -0,0 +1,18 @@
+<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
+<title>RENTV — 10 Admin Versions</title><link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700;800&family=Playfair+Display:wght@700&display=swap" rel="stylesheet"><style>
+:root{--a:#c8102e;--bg:#0e1116;--pan:#161b22;--ln:#242b34;--sub:#8b949e;--ink:#e6edf3}
+*{box-sizing:border-box;margin:0;padding:0}body{font-family:'Inter',system-ui,sans-serif;background:var(--bg);color:var(--ink);line-height:1.5}
+a{color:inherit;text-decoration:none}.wrap{max-width:1240px;margin:0 auto;padding:0 22px}
+.abar{background:#0a0d11;border-bottom:1px solid var(--ln)}.abar .wrap{display:flex;justify-content:space-between;align-items:center;height:52px}
+.abrand{font-family:'Playfair Display',serif;font-size:20px}.abrand span{color:var(--a)}.abrand small{font-family:'Inter';font-size:10px;letter-spacing:.2em;text-transform:uppercase;color:var(--sub);margin-left:8px}
+.abar a{font-size:12px;color:var(--sub)}.abar a:hover{color:var(--ink)}
+.ah{padding:20px 0 4px}.ah h1{font-size:20px}.ah p{color:var(--sub);font-size:13px}
+#app{padding:18px 0 60px;min-height:60vh}
+.hd{padding:44px 0 6px;text-align:center}.hd h1{font-family:'Playfair Display',serif;font-size:clamp(28px,5vw,48px)}.hd h1 span{color:var(--a)}.hd p{color:var(--sub);margin-top:8px}
+.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:16px;padding:28px 0 60px}
+.vc{background:var(--pan);border:1px solid var(--ln);border-radius:10px;padding:20px;transition:.2s}
+.vc:hover{transform:translateY(-4px);border-color:var(--a);box-shadow:0 16px 40px rgba(0,0,0,.5)}
+.tag{font-size:10px;text-transform:uppercase;letter-spacing:.14em;color:var(--a);font-weight:800}.vc h3{font-size:20px;margin:6px 0}.vc p{color:var(--sub);font-size:14px;min-height:40px}.vc .go{color:var(--a);font-weight:800;font-size:12px;text-transform:uppercase;letter-spacing:.06em}</style></head>
+<body><div class="abar"><div class="wrap"><div class="abrand">REN<span>TV</span> Admin</div><a href="/">← main site</a></div></div>
+<header class="hd"><div class="wrap"><h1>REN<span>TV</span> · 10 Admin Versions</h1><p>Ten complete back-office directions over the real subscriber / feed / video / market data. Open each and pick the one to run.</p></div></header>
+<div class="wrap"><div class="grid"><a class="vc" href="/admin/classic"><div class="tag">Admin</div><h3>Classic Dashboard</h3><p>KPI cards up top, two data tables below. The safe, familiar admin.</p><span class="go">Open →</span></a><a class="vc" href="/admin/sidebar"><div class="tag">Admin</div><h3>Sidebar Console</h3><p>Left nav rail + main panel — the SaaS-standard admin shell.</p><span class="go">Open →</span></a><a class="vc" href="/admin/console"><div class="tag">Admin</div><h3>Ops Console</h3><p>Monospace metrics row + a live activity log. For the data-desk operator.</p><span class="go">Open →</span></a><a class="vc" href="/admin/cards"><div class="tag">Admin</div><h3>Module Grid</h3><p>Independent widget cards you can scan at a glance.</p><span class="go">Open →</span></a><a class="vc" href="/admin/table"><div class="tag">Admin</div><h3>Data Table</h3><p>Dense spreadsheet-style rows — power-user, keyboard-first feel.</p><span class="go">Open →</span></a><a class="vc" href="/admin/analytics"><div class="tag">Admin</div><h3>Analytics First</h3><p>One giant headline metric, then supporting stats and a table.</p><span class="go">Open →</span></a><a class="vc" href="/admin/split"><div class="tag">Admin</div><h3>Master–Detail</h3><p>Scrollable list on the left, KPI rail on the right.</p><span class="go">Open →</span></a><a class="vc" href="/admin/kanban"><div class="tag">Admin</div><h3>Kanban Board</h3><p>Stories grouped into columns by sector — editorial pipeline view.</p><span class="go">Open →</span></a><a class="vc" href="/admin/minimal"><div class="tag">Admin</div><h3>Minimal</h3><p>Quiet single-column stat sheet. No chrome, just the numbers.</p><span class="go">Open →</span></a><a class="vc" href="/admin/command"><div class="tag">Admin</div><h3>Command Center</h3><p>Multi-panel grid — every surface (subs, feed, video, market) at once.</p><span class="go">Open →</span></a></div></div></body></html>
\ No newline at end of file
diff --git a/public/admin/kanban.html b/public/admin/kanban.html
new file mode 100644
index 00000000..5c294e8d
--- /dev/null
+++ b/public/admin/kanban.html
@@ -0,0 +1,14 @@
+<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
+<title>RENTV Admin — Kanban Board</title><link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700;800&family=Playfair+Display:wght@700&display=swap" rel="stylesheet"><style>
+:root{--a:#c8102e;--bg:#0e1116;--pan:#161b22;--ln:#242b34;--sub:#8b949e;--ink:#e6edf3}
+*{box-sizing:border-box;margin:0;padding:0}body{font-family:'Inter',system-ui,sans-serif;background:var(--bg);color:var(--ink);line-height:1.5}
+a{color:inherit;text-decoration:none}.wrap{max-width:1240px;margin:0 auto;padding:0 22px}
+.abar{background:#0a0d11;border-bottom:1px solid var(--ln)}.abar .wrap{display:flex;justify-content:space-between;align-items:center;height:52px}
+.abrand{font-family:'Playfair Display',serif;font-size:20px}.abrand span{color:var(--a)}.abrand small{font-family:'Inter';font-size:10px;letter-spacing:.2em;text-transform:uppercase;color:var(--sub);margin-left:8px}
+.abar a{font-size:12px;color:var(--sub)}.abar a:hover{color:var(--ink)}
+.ah{padding:20px 0 4px}.ah h1{font-size:20px}.ah p{color:var(--sub);font-size:13px}
+#app{padding:18px 0 60px;min-height:60vh}</style></head>
+<body data-ver="kanban" class="ver-kanban">
+<div class="abar"><div class="wrap"><div class="abrand">REN<span>TV</span> Admin<small>Kanban Board</small></div><a href="/admin">← all 10 admin versions</a></div></div>
+<div class="wrap"><div class="ah"><h1>Kanban Board</h1><p>Stories grouped into columns by sector — editorial pipeline view. · live gated data</p></div><main id="app"><p style="color:var(--sub);padding:30px">Loading admin data…</p></main></div>
+<script src="/admin/render.js"></script></body></html>
\ No newline at end of file
diff --git a/public/admin/minimal.html b/public/admin/minimal.html
new file mode 100644
index 00000000..ce4d76c8
--- /dev/null
+++ b/public/admin/minimal.html
@@ -0,0 +1,14 @@
+<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
+<title>RENTV Admin — Minimal</title><link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700;800&family=Playfair+Display:wght@700&display=swap" rel="stylesheet"><style>
+:root{--a:#c8102e;--bg:#0e1116;--pan:#161b22;--ln:#242b34;--sub:#8b949e;--ink:#e6edf3}
+*{box-sizing:border-box;margin:0;padding:0}body{font-family:'Inter',system-ui,sans-serif;background:var(--bg);color:var(--ink);line-height:1.5}
+a{color:inherit;text-decoration:none}.wrap{max-width:1240px;margin:0 auto;padding:0 22px}
+.abar{background:#0a0d11;border-bottom:1px solid var(--ln)}.abar .wrap{display:flex;justify-content:space-between;align-items:center;height:52px}
+.abrand{font-family:'Playfair Display',serif;font-size:20px}.abrand span{color:var(--a)}.abrand small{font-family:'Inter';font-size:10px;letter-spacing:.2em;text-transform:uppercase;color:var(--sub);margin-left:8px}
+.abar a{font-size:12px;color:var(--sub)}.abar a:hover{color:var(--ink)}
+.ah{padding:20px 0 4px}.ah h1{font-size:20px}.ah p{color:var(--sub);font-size:13px}
+#app{padding:18px 0 60px;min-height:60vh}</style></head>
+<body data-ver="minimal" class="ver-minimal">
+<div class="abar"><div class="wrap"><div class="abrand">REN<span>TV</span> Admin<small>Minimal</small></div><a href="/admin">← all 10 admin versions</a></div></div>
+<div class="wrap"><div class="ah"><h1>Minimal</h1><p>Quiet single-column stat sheet. No chrome, just the numbers. · live gated data</p></div><main id="app"><p style="color:var(--sub);padding:30px">Loading admin data…</p></main></div>
+<script src="/admin/render.js"></script></body></html>
\ No newline at end of file
diff --git a/public/admin/render.js b/public/admin/render.js
new file mode 100644
index 00000000..bf206e2e
--- /dev/null
+++ b/public/admin/render.js
@@ -0,0 +1,90 @@
+/* rentv-v1 ADMIN — one file, 10 distinct admin-dashboard layouts over the real
+   gated data (subscribers, news feed, videos, markets). Reads body.dataset.ver. */
+(function () {
+  var ver = document.body.dataset.ver || 'classic';
+  var esc = function (s) { return (s || '').replace(/[&<>"]/g, function (c) { return { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;' }[c]; }); };
+  var css = function (t) { var s = document.createElement('style'); s.textContent = t; document.head.appendChild(s); };
+  var when = function (iso) { try { var m = Math.round((Date.now() - new Date(iso)) / 60000); return m < 2 ? 'just now' : m + ' min ago'; } catch (e) { return '—'; } };
+
+  function build(D) {
+    var subs = D.subs || {}, news = D.news || {}, vids = D.vids || {}, mk = D.mk || {}, tk = D.tk || {};
+    var subN = subs.count || 0, subU = subs.unique || 0, subList = (subs.items || []).slice(0, 12);
+    var nItems = news.items || [], vItems = vids.items || [], curve = mk.curve || [];
+    var KPI = [
+      ['Subscribers', subU, subN + ' total signups'],
+      ['Live stories', nItems.length, 'feed ' + when(news.fetched_at)],
+      ['REview videos', vItems.length, (vids.cats || []).length + ' categories'],
+      ['10-YR', (tk.ten_yr && tk.ten_yr.value) || (curve.find && (curve.find(function (c) { return /10/.test(c.label); }) || {}).value) || '—', 'treasury'],
+    ];
+    var kpiCards = function (cls) { return KPI.map(function (k) { return '<div class="' + (cls || 'kpi') + '"><div class="v">' + esc('' + k[1]) + '</div><div class="l">' + esc(k[0]) + '</div><div class="s">' + esc(k[2]) + '</div></div>'; }).join(''); };
+    var subRows = function () { return subList.length ? subList.map(function (s) { return '<tr><td>' + esc(s.email) + '</td><td>' + esc(s.source || '') + '</td><td>' + esc((s.at || '').slice(0, 16).replace('T', ' ')) + '</td></tr>'; }).join('') : '<tr><td colspan="3" class="mut">No signups yet.</td></tr>'; };
+    var newsRows = function (n) { return nItems.slice(0, n || 10).map(function (x) { return '<tr><td class="c">' + esc(x.cat) + '</td><td>' + esc(x.title) + '</td><td class="mut">#' + esc(x.id) + '</td></tr>'; }).join(''); };
+    var vidList = function (n) { return vItems.slice(0, n || 8).map(function (v) { return '<li>' + esc(v.title) + ' <span class="mut">· ' + esc(v.cat || '') + '</span></li>'; }).join(''); };
+
+    var T = {
+      // 1 CLASSIC — KPI cards + two tables
+      classic: function () {
+        css('.kpis{display:grid;grid-template-columns:repeat(4,1fr);gap:16px}.kpi{background:var(--pan);border:1px solid var(--ln);border-radius:10px;padding:18px}.kpi .v{font-size:32px;font-weight:800;color:var(--a)}.kpi .l{font-size:12px;text-transform:uppercase;letter-spacing:.06em;color:var(--sub);margin-top:4px}.kpi .s{font-size:12px;color:var(--sub);margin-top:2px}.tw{display:grid;grid-template-columns:1fr 1fr;gap:22px;margin-top:24px}.card{background:var(--pan);border:1px solid var(--ln);border-radius:10px;padding:16px 18px}.card h3{font-size:14px;text-transform:uppercase;letter-spacing:.06em;margin-bottom:10px}table{width:100%;border-collapse:collapse;font-size:13px}td,th{text-align:left;padding:7px 6px;border-bottom:1px solid var(--ln)}.c{color:var(--a);font-weight:700;font-size:11px;text-transform:uppercase}.mut{color:var(--sub)}@media(max-width:820px){.kpis,.tw{grid-template-columns:1fr}}');
+        return '<div class="kpis">' + kpiCards() + '</div><div class="tw"><div class="card"><h3>Recent subscribers</h3><table><tr><th>Email</th><th>Source</th><th>When</th></tr>' + subRows() + '</table></div><div class="card"><h3>Latest stories</h3><table>' + newsRows(10) + '</table></div></div>';
+      },
+      // 2 SIDEBAR
+      sidebar: function () {
+        css('.lay{display:grid;grid-template-columns:200px 1fr;gap:24px}.side{background:var(--pan);border:1px solid var(--ln);border-radius:10px;padding:14px}.side a{display:block;padding:9px 10px;border-radius:6px;color:var(--sub);font-size:13px;font-weight:600}.side a.on{background:var(--a);color:#fff}.kpis{display:grid;grid-template-columns:1fr 1fr;gap:14px}.kpi{background:var(--pan);border:1px solid var(--ln);border-radius:10px;padding:16px}.kpi .v{font-size:28px;font-weight:800;color:var(--a)}.kpi .l{font-size:11px;text-transform:uppercase;color:var(--sub)}.kpi .s{font-size:11px;color:var(--sub)}.card{background:var(--pan);border:1px solid var(--ln);border-radius:10px;padding:16px;margin-top:16px}table{width:100%;border-collapse:collapse;font-size:13px}td,th{text-align:left;padding:6px;border-bottom:1px solid var(--ln)}.c{color:var(--a);font-size:11px;font-weight:700;text-transform:uppercase}.mut{color:var(--sub)}@media(max-width:760px){.lay{grid-template-columns:1fr}}');
+        return '<div class="lay"><nav class="side"><a class="on">Overview</a><a>Subscribers</a><a>News feed</a><a>The REview</a><a>Markets</a><a>Settings</a></nav><div><div class="kpis">' + kpiCards() + '</div><div class="card"><h3 style="font-size:14px;text-transform:uppercase;margin-bottom:8px">Latest stories</h3><table>' + newsRows(8) + '</table></div></div></div>';
+      },
+      // 3 CONSOLE (dark ops)
+      console: function () {
+        css('body.ver-console{}.con{font-family:ui-monospace,monospace}.row{display:flex;gap:26px;flex-wrap:wrap;padding:14px 0;border-bottom:1px solid var(--ln)}.stat .v{font-size:26px;color:var(--a)}.stat .l{font-size:10px;text-transform:uppercase;color:var(--sub);letter-spacing:.1em}.log{margin-top:14px;font-size:12px}.log div{padding:5px 0;border-bottom:1px solid var(--ln)}.c{color:var(--a)}.mut{color:var(--sub)}');
+        return '<div class="con"><div class="row">' + KPI.map(function (k) { return '<div class="stat"><div class="v">' + esc('' + k[1]) + '</div><div class="l">' + esc(k[0]) + '</div></div>'; }).join('') + '</div><div class="log">' + nItems.slice(0, 16).map(function (x) { return '<div><span class="c">[' + esc(x.cat) + ']</span> ' + esc(x.title) + ' <span class="mut">#' + esc(x.id) + '</span></div>'; }).join('') + '</div></div>';
+      },
+      // 4 CARDS (module grid)
+      cards: function () {
+        css('.mods{display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:16px}.mod{background:var(--pan);border:1px solid var(--ln);border-radius:10px;padding:18px}.mod h3{font-size:13px;text-transform:uppercase;letter-spacing:.06em;color:var(--sub);margin-bottom:8px}.mod .big{font-size:34px;font-weight:800;color:var(--a)}.mod ul{list-style:none;font-size:13px}.mod li{padding:5px 0;border-bottom:1px solid var(--ln)}.c{color:var(--a);font-weight:700}.mut{color:var(--sub)}');
+        return '<div class="mods"><div class="mod"><h3>Subscribers</h3><div class="big">' + subU + '</div><p class="mut">' + subN + ' total signups</p></div><div class="mod"><h3>Live feed</h3><div class="big">' + nItems.length + '</div><p class="mut">updated ' + when(news.fetched_at) + '</p></div><div class="mod"><h3>REview</h3><div class="big">' + vItems.length + '</div><p class="mut">videos</p></div><div class="mod"><h3>Latest videos</h3><ul>' + vidList(6) + '</ul></div><div class="mod" style="grid-column:span 2"><h3>Latest stories</h3><ul>' + nItems.slice(0, 8).map(function (x) { return '<li><span class="c">' + esc(x.cat) + '</span> · ' + esc(x.title) + '</li>'; }).join('') + '</ul></div></div>';
+      },
+      // 5 TABLE (dense spreadsheet)
+      table: function () {
+        css('.bar{display:flex;gap:24px;padding:10px 0 16px}.bar b{color:var(--a)}table{width:100%;border-collapse:collapse;font-size:13px}th{text-align:left;text-transform:uppercase;font-size:11px;letter-spacing:.06em;color:var(--sub);border-bottom:2px solid var(--a);padding:8px}td{padding:8px;border-bottom:1px solid var(--ln)}.c{color:var(--a);font-weight:700;font-size:11px;text-transform:uppercase}.mut{color:var(--sub)}');
+        return '<div class="bar"><span>Subscribers <b>' + subU + '</b></span><span>Stories <b>' + nItems.length + '</b></span><span>Videos <b>' + vItems.length + '</b></span><span>Feed <b>' + when(news.fetched_at) + '</b></span></div><table><tr><th>Sector</th><th>Headline</th><th>Ref</th></tr>' + newsRows(20) + '</table>';
+      },
+      // 6 ANALYTICS (big numbers first)
+      analytics: function () {
+        css('.hero{text-align:center;padding:24px 0 8px}.hero .v{font-size:64px;font-weight:800;color:var(--a);line-height:1}.hero .l{font-size:13px;text-transform:uppercase;letter-spacing:.1em;color:var(--sub)}.strip{display:grid;grid-template-columns:repeat(3,1fr);gap:16px;margin:20px 0}.s{background:var(--pan);border:1px solid var(--ln);border-radius:10px;padding:16px;text-align:center}.s .v{font-size:28px;font-weight:800;color:var(--a)}.s .l{font-size:11px;text-transform:uppercase;color:var(--sub)}.card{background:var(--pan);border:1px solid var(--ln);border-radius:10px;padding:16px}table{width:100%;border-collapse:collapse;font-size:13px}td{padding:6px;border-bottom:1px solid var(--ln)}.c{color:var(--a);font-size:11px;font-weight:700;text-transform:uppercase}');
+        return '<div class="hero"><div class="v">' + subU + '</div><div class="l">Newsletter subscribers</div></div><div class="strip"><div class="s"><div class="v">' + nItems.length + '</div><div class="l">Live stories</div></div><div class="s"><div class="v">' + vItems.length + '</div><div class="l">Videos</div></div><div class="s"><div class="v">' + subN + '</div><div class="l">Total signups</div></div></div><div class="card"><table>' + newsRows(10) + '</table></div>';
+      },
+      // 7 SPLIT (master-detail)
+      split: function () {
+        css('.sp{display:grid;grid-template-columns:1fr 300px;gap:24px}.list{background:var(--pan);border:1px solid var(--ln);border-radius:10px;padding:8px}.list .it{padding:10px 12px;border-bottom:1px solid var(--ln);font-size:14px}.list .c{color:var(--a);font-size:11px;font-weight:700;text-transform:uppercase}.side .kpi{background:var(--pan);border:1px solid var(--ln);border-radius:10px;padding:14px;margin-bottom:12px}.side .v{font-size:26px;font-weight:800;color:var(--a)}.side .l{font-size:11px;text-transform:uppercase;color:var(--sub)}@media(max-width:820px){.sp{grid-template-columns:1fr}}');
+        return '<div class="sp"><div class="list">' + nItems.slice(0, 14).map(function (x) { return '<div class="it"><span class="c">' + esc(x.cat) + '</span> ' + esc(x.title) + '</div>'; }).join('') + '</div><aside class="side">' + KPI.map(function (k) { return '<div class="kpi"><div class="v">' + esc('' + k[1]) + '</div><div class="l">' + esc(k[0]) + '</div></div>'; }).join('') + '</aside></div>';
+      },
+      // 8 KANBAN (by category)
+      kanban: function () {
+        var cats = {}; nItems.forEach(function (x) { (cats[x.cat] = cats[x.cat] || []).push(x); });
+        css('.kb{display:flex;gap:14px;overflow-x:auto;padding-bottom:8px}.col{min-width:220px;flex:1;background:var(--pan);border:1px solid var(--ln);border-radius:10px;padding:10px}.col h3{font-size:12px;text-transform:uppercase;color:var(--a);padding:4px 6px}.ct{background:var(--bg);border:1px solid var(--ln);border-radius:6px;padding:8px;margin:6px 0;font-size:12px}');
+        return '<div class="kb">' + Object.keys(cats).map(function (c) { return '<div class="col"><h3>' + esc(c) + ' · ' + cats[c].length + '</h3>' + cats[c].slice(0, 8).map(function (x) { return '<div class="ct">' + esc(x.title) + '</div>'; }).join('') + '</div>'; }).join('') + '</div>';
+      },
+      // 9 MINIMAL
+      minimal: function () {
+        css('.mn{max-width:640px;margin:0 auto}.mn .row{display:flex;justify-content:space-between;padding:14px 0;border-bottom:1px solid var(--ln)}.mn .row .v{font-weight:800;color:var(--a)}.mn h3{font-size:13px;text-transform:uppercase;color:var(--sub);margin:24px 0 6px}.mn .it{padding:8px 0;border-bottom:1px solid var(--ln);font-size:14px}');
+        return '<div class="mn"><div class="row"><span>Subscribers</span><span class="v">' + subU + '</span></div><div class="row"><span>Live stories</span><span class="v">' + nItems.length + '</span></div><div class="row"><span>Videos</span><span class="v">' + vItems.length + '</span></div><div class="row"><span>Feed updated</span><span class="v">' + when(news.fetched_at) + '</span></div><h3>Latest</h3>' + nItems.slice(0, 8).map(function (x) { return '<div class="it">' + esc(x.title) + '</div>'; }).join('') + '</div>';
+      },
+      // 10 COMMAND CENTER (multi-panel)
+      command: function () {
+        css('.cc{display:grid;grid-template-columns:repeat(6,1fr);grid-auto-rows:minmax(90px,auto);gap:12px}.p{background:var(--pan);border:1px solid var(--ln);border-radius:10px;padding:14px}.p h3{font-size:11px;text-transform:uppercase;color:var(--sub)}.p .v{font-size:30px;font-weight:800;color:var(--a)}.p.wide{grid-column:span 4}.p.tall{grid-row:span 3}.p ul,.p table{list-style:none;font-size:12px;width:100%;border-collapse:collapse}.p li,.p td{padding:5px 0;border-bottom:1px solid var(--ln)}.c{color:var(--a);font-weight:700}@media(max-width:820px){.cc{grid-template-columns:1fr 1fr}.p.wide{grid-column:span 2}}');
+        return '<div class="cc"><div class="p"><h3>Subs</h3><div class="v">' + subU + '</div></div><div class="p"><h3>Stories</h3><div class="v">' + nItems.length + '</div></div><div class="p wide"><h3>Latest stories</h3><table>' + newsRows(6) + '</table></div><div class="p"><h3>Videos</h3><div class="v">' + vItems.length + '</div></div><div class="p"><h3>Feed</h3><div class="v" style="font-size:16px">' + when(news.fetched_at) + '</div></div><div class="p wide tall"><h3>Recent subscribers</h3><table><tr><td><b>Email</b></td><td><b>Source</b></td></tr>' + subList.slice(0, 8).map(function (s) { return '<tr><td>' + esc(s.email) + '</td><td>' + esc(s.source || '') + '</td></tr>'; }).join('') + '</table></div><div class="p tall"><h3>REview</h3><ul>' + vidList(7) + '</ul></div></div>';
+      },
+    };
+    document.getElementById('app').innerHTML = (T[ver] || T.classic)();
+  }
+
+  Promise.allSettled([
+    fetch('/api/subscribers').then(function (r) { return r.json(); }),
+    fetch('/api/news').then(function (r) { return r.json(); }),
+    fetch('/api/videos').then(function (r) { return r.json(); }),
+    fetch('/api/markets').then(function (r) { return r.json(); }),
+    fetch('/api/ticker').then(function (r) { return r.json(); }),
+  ]).then(function (res) {
+    var g = function (i) { return res[i].status === 'fulfilled' ? res[i].value : {}; };
+    build({ subs: g(0), news: g(1), vids: g(2), mk: g(3), tk: g(4) });
+  }).catch(function () { document.getElementById('app').innerHTML = '<p style="color:var(--sub);padding:40px">Admin data unavailable — retry.</p>'; });
+})();
diff --git a/public/admin/sidebar.html b/public/admin/sidebar.html
new file mode 100644
index 00000000..538d35d1
--- /dev/null
+++ b/public/admin/sidebar.html
@@ -0,0 +1,14 @@
+<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
+<title>RENTV Admin — Sidebar Console</title><link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700;800&family=Playfair+Display:wght@700&display=swap" rel="stylesheet"><style>
+:root{--a:#c8102e;--bg:#0e1116;--pan:#161b22;--ln:#242b34;--sub:#8b949e;--ink:#e6edf3}
+*{box-sizing:border-box;margin:0;padding:0}body{font-family:'Inter',system-ui,sans-serif;background:var(--bg);color:var(--ink);line-height:1.5}
+a{color:inherit;text-decoration:none}.wrap{max-width:1240px;margin:0 auto;padding:0 22px}
+.abar{background:#0a0d11;border-bottom:1px solid var(--ln)}.abar .wrap{display:flex;justify-content:space-between;align-items:center;height:52px}
+.abrand{font-family:'Playfair Display',serif;font-size:20px}.abrand span{color:var(--a)}.abrand small{font-family:'Inter';font-size:10px;letter-spacing:.2em;text-transform:uppercase;color:var(--sub);margin-left:8px}
+.abar a{font-size:12px;color:var(--sub)}.abar a:hover{color:var(--ink)}
+.ah{padding:20px 0 4px}.ah h1{font-size:20px}.ah p{color:var(--sub);font-size:13px}
+#app{padding:18px 0 60px;min-height:60vh}</style></head>
+<body data-ver="sidebar" class="ver-sidebar">
+<div class="abar"><div class="wrap"><div class="abrand">REN<span>TV</span> Admin<small>Sidebar Console</small></div><a href="/admin">← all 10 admin versions</a></div></div>
+<div class="wrap"><div class="ah"><h1>Sidebar Console</h1><p>Left nav rail + main panel — the SaaS-standard admin shell. · live gated data</p></div><main id="app"><p style="color:var(--sub);padding:30px">Loading admin data…</p></main></div>
+<script src="/admin/render.js"></script></body></html>
\ No newline at end of file
diff --git a/public/admin/split.html b/public/admin/split.html
new file mode 100644
index 00000000..c6bda6b1
--- /dev/null
+++ b/public/admin/split.html
@@ -0,0 +1,14 @@
+<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
+<title>RENTV Admin — Master–Detail</title><link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700;800&family=Playfair+Display:wght@700&display=swap" rel="stylesheet"><style>
+:root{--a:#c8102e;--bg:#0e1116;--pan:#161b22;--ln:#242b34;--sub:#8b949e;--ink:#e6edf3}
+*{box-sizing:border-box;margin:0;padding:0}body{font-family:'Inter',system-ui,sans-serif;background:var(--bg);color:var(--ink);line-height:1.5}
+a{color:inherit;text-decoration:none}.wrap{max-width:1240px;margin:0 auto;padding:0 22px}
+.abar{background:#0a0d11;border-bottom:1px solid var(--ln)}.abar .wrap{display:flex;justify-content:space-between;align-items:center;height:52px}
+.abrand{font-family:'Playfair Display',serif;font-size:20px}.abrand span{color:var(--a)}.abrand small{font-family:'Inter';font-size:10px;letter-spacing:.2em;text-transform:uppercase;color:var(--sub);margin-left:8px}
+.abar a{font-size:12px;color:var(--sub)}.abar a:hover{color:var(--ink)}
+.ah{padding:20px 0 4px}.ah h1{font-size:20px}.ah p{color:var(--sub);font-size:13px}
+#app{padding:18px 0 60px;min-height:60vh}</style></head>
+<body data-ver="split" class="ver-split">
+<div class="abar"><div class="wrap"><div class="abrand">REN<span>TV</span> Admin<small>Master–Detail</small></div><a href="/admin">← all 10 admin versions</a></div></div>
+<div class="wrap"><div class="ah"><h1>Master–Detail</h1><p>Scrollable list on the left, KPI rail on the right. · live gated data</p></div><main id="app"><p style="color:var(--sub);padding:30px">Loading admin data…</p></main></div>
+<script src="/admin/render.js"></script></body></html>
\ No newline at end of file
diff --git a/public/admin/table.html b/public/admin/table.html
new file mode 100644
index 00000000..3845a7a1
--- /dev/null
+++ b/public/admin/table.html
@@ -0,0 +1,14 @@
+<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
+<title>RENTV Admin — Data Table</title><link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700;800&family=Playfair+Display:wght@700&display=swap" rel="stylesheet"><style>
+:root{--a:#c8102e;--bg:#0e1116;--pan:#161b22;--ln:#242b34;--sub:#8b949e;--ink:#e6edf3}
+*{box-sizing:border-box;margin:0;padding:0}body{font-family:'Inter',system-ui,sans-serif;background:var(--bg);color:var(--ink);line-height:1.5}
+a{color:inherit;text-decoration:none}.wrap{max-width:1240px;margin:0 auto;padding:0 22px}
+.abar{background:#0a0d11;border-bottom:1px solid var(--ln)}.abar .wrap{display:flex;justify-content:space-between;align-items:center;height:52px}
+.abrand{font-family:'Playfair Display',serif;font-size:20px}.abrand span{color:var(--a)}.abrand small{font-family:'Inter';font-size:10px;letter-spacing:.2em;text-transform:uppercase;color:var(--sub);margin-left:8px}
+.abar a{font-size:12px;color:var(--sub)}.abar a:hover{color:var(--ink)}
+.ah{padding:20px 0 4px}.ah h1{font-size:20px}.ah p{color:var(--sub);font-size:13px}
+#app{padding:18px 0 60px;min-height:60vh}</style></head>
+<body data-ver="table" class="ver-table">
+<div class="abar"><div class="wrap"><div class="abrand">REN<span>TV</span> Admin<small>Data Table</small></div><a href="/admin">← all 10 admin versions</a></div></div>
+<div class="wrap"><div class="ah"><h1>Data Table</h1><p>Dense spreadsheet-style rows — power-user, keyboard-first feel. · live gated data</p></div><main id="app"><p style="color:var(--sub);padding:30px">Loading admin data…</p></main></div>
+<script src="/admin/render.js"></script></body></html>
\ No newline at end of file
diff --git a/server.js b/server.js
index 4788f933..ef979ad4 100644
--- a/server.js
+++ b/server.js
@@ -152,6 +152,8 @@ app.get('/news/:id', (_q, r) => r.sendFile(path.join(PUB, 'article.html')));
 app.get('/review', (_q, r) => r.sendFile(path.join(PUB, 'review.html')));
 // 10 site VERSIONS — picker + per-version homepage layouts (all on live data)
 app.get('/versions', (_q, r) => r.sendFile(path.join(PUB, 'versions', 'index.html')));
+// 10 ADMIN versions — picker + per-version dashboards over the real gated data
+app.get('/admin', (_q, r) => r.sendFile(path.join(PUB, 'admin', 'index.html')));
 
 app.use(express.static(PUB, { extensions: ['html'] }));
 app.listen(PORT, () => console.log('rentv-v1 (full site, gated, live data) on ' + PORT));

← 3683dd11 rentv-v1: add Versions to index nav (first-occurrence, nav n  ·  back to Rentv 2026  ·  rentv-v1: BLOG BACKEND — file-backed original articles (post 026bc4ee →