[object Object]

← back to Rentv Tour

auto-save: 2026-07-28T15:01:16 (7 files) — videos/pitch/index.html desk-cap.mjs desk-probe.mjs fix-caps.mjs probe.mjs

6cceb3faf4a7a6b4486bc1ed506f1e040b2b4e2f · 2026-07-28 15:01:19 -0700 · Steve Abrams

Files touched

Diff

commit 6cceb3faf4a7a6b4486bc1ed506f1e040b2b4e2f
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Tue Jul 28 15:01:19 2026 -0700

    auto-save: 2026-07-28T15:01:16 (7 files) — videos/pitch/index.html desk-cap.mjs desk-probe.mjs fix-caps.mjs probe.mjs
---
 desk-cap.mjs            |  23 +++++++
 desk-probe.mjs          |  25 +++++++
 fix-caps.mjs            |  59 ++++++++++++++++
 probe.mjs               |  24 +++++++
 recapture-results.json  | 107 +++++++++++++++++++++++++++++
 rentv-recapture.mjs     | 175 ++++++++++++++++++++++++++++++++++++++++++++++++
 videos/pitch/index.html |  41 ++++++------
 7 files changed, 435 insertions(+), 19 deletions(-)

diff --git a/desk-cap.mjs b/desk-cap.mjs
new file mode 100644
index 0000000..b9d5dab
--- /dev/null
+++ b/desk-cap.mjs
@@ -0,0 +1,23 @@
+import { chromium } from 'playwright';
+const OUT='/Users/macstudio3/Projects/rentv-master-reel/media/captures';
+const BASE='https://rentv.agentabrams.com';
+const CLEAN_CSS=`#cap-header{position:sticky;top:0;z-index:99999;display:flex;align-items:center;justify-content:space-between;height:64px;padding:0 24px;background:#fff;border-bottom:1px solid #e6eaef;font-family:-apple-system,sans-serif}#cap-header .cap-brand{font-size:23px;font-weight:800;color:#0d1b2a;text-decoration:none}#cap-header .cap-brand span{color:#c0392b}#cap-header button{background:none;border:0;color:#0d1b2a;display:inline-flex;padding:9px}`;
+const b=await chromium.launch();
+const ctx=await b.newContext({viewport:{width:1440,height:810},deviceScaleFactor:2,httpCredentials:{username:'admin',password:'DW2024!'}});
+const p=await ctx.newPage();
+const resp=await p.goto(BASE+'/desk',{waitUntil:'networkidle',timeout:45000});
+await p.waitForTimeout(2500);
+// close any open drawer, then inject clean header hiding only the top header bar
+await p.addStyleTag({content:CLEAN_CSS});
+await p.evaluate(()=>{
+  // close drawer if open
+  document.querySelectorAll('.drawer,.scrim').forEach(e=>{e.classList.remove('open','active','show');e.style.display='none';});
+  // hide top header only (not the shell content)
+  document.querySelectorAll('header.top,.top').forEach(e=>{e.style.display='none';});
+  if(!document.getElementById('cap-header')){const bar=document.createElement('div');bar.id='cap-header';bar.innerHTML=`<button><svg width=24 height=24 viewBox="0 0 24 24" fill=none stroke=currentColor stroke-width=2.2 stroke-linecap=round><path d="M3 6h18M3 12h18M3 18h18"/></svg></button><a class=cap-brand href="/">REN<span>TV</span></a><button><svg width=21 height=21 viewBox="0 0 24 24" fill=none stroke=currentColor stroke-width=2><circle cx=12 cy=8 r=3.6/><path d="M4.5 20a7.5 7.5 0 0 1 15 0" stroke-linecap=round/></svg></button>`;document.body.insertBefore(bar,document.body.firstChild)}
+  window.scrollTo(0,0);
+});
+await p.waitForTimeout(500);
+await p.screenshot({path:`${OUT}/desk.png`});
+console.log('OK desk.png ['+resp.status()+']');
+await b.close();
diff --git a/desk-probe.mjs b/desk-probe.mjs
new file mode 100644
index 0000000..c26d4d3
--- /dev/null
+++ b/desk-probe.mjs
@@ -0,0 +1,25 @@
+import { chromium } from 'playwright';
+const BASE='https://rentv.agentabrams.com';
+const b=await chromium.launch();
+const ctx=await b.newContext({viewport:{width:1440,height:810},httpCredentials:{username:'admin',password:'DW2024!'}});
+const p=await ctx.newPage();
+const errs=[];
+p.on('console',m=>{if(m.type()==='error')errs.push(m.text().slice(0,120))});
+p.on('pageerror',e=>errs.push('PAGEERR: '+e.message.slice(0,120)));
+await p.goto(BASE+'/desk',{waitUntil:'networkidle',timeout:45000});
+await p.waitForTimeout(3000);
+const info=await p.evaluate(()=>{
+  const bodyText=document.body.innerText.trim().slice(0,200);
+  const main=document.querySelector('main');
+  return {
+    bodyLen:document.body.innerText.trim().length,
+    bodyPreview:bodyText,
+    hasMain:!!main,
+    mainLen:main?main.innerText.trim().length:0,
+    childCount:document.body.children.length,
+    tags:[...document.body.children].map(e=>e.tagName+'.'+(e.className||e.id||'').toString().slice(0,20))
+  };
+});
+console.log('DESK errs:',errs.slice(0,8));
+console.log('DESK info:',JSON.stringify(info,null,1));
+await b.close();
diff --git a/fix-caps.mjs b/fix-caps.mjs
new file mode 100644
index 0000000..f5250bc
--- /dev/null
+++ b/fix-caps.mjs
@@ -0,0 +1,59 @@
+import { chromium } from 'playwright';
+const OUT='/Users/macstudio3/Projects/rentv-master-reel/media/captures';
+const BASE='https://rentv.agentabrams.com';
+const CLEAN_CSS=`#cap-header{position:sticky;top:0;z-index:99999;display:flex;align-items:center;justify-content:space-between;height:64px;padding:0 24px;background:#fff;border-bottom:1px solid #e6eaef;font-family:-apple-system,sans-serif}#cap-header .cap-brand{font-size:23px;font-weight:800;color:#0d1b2a;text-decoration:none}#cap-header .cap-brand span{color:#c0392b}#cap-header button{background:none;border:0;color:#0d1b2a;display:inline-flex;padding:9px}`;
+
+// Theme palette from theme.js
+const THEMES={
+  midnight:{ dark:true, accent:"#c9a84c", ink:"#f0f2f5", sub:"#8fa3b8", line:"#1e2d40", bg:"#0d1b2a", wash:"#132336", serif:"'DM Serif Display', Georgia, serif", sans:"'Archivo', system-ui, sans-serif" },
+  salmon:{ dark:false, accent:"#00538b", ink:"#1a1a1a", sub:"#5c5c5c", line:"#d4c9b8", bg:"#fff1e5", wash:"#fde8d1", serif:"'Source Serif 4', Georgia, serif", sans:"'Inter', system-ui, sans-serif" }
+};
+
+async function normalize(page,darkBg){
+  await page.addStyleTag({content:CLEAN_CSS + (darkBg?`\n#cap-header{background:${darkBg.bg}!important;border-bottom:1px solid ${darkBg.line}!important}#cap-header .cap-brand{color:${darkBg.ink}!important}#cap-header button{color:${darkBg.ink}!important}`:'')});
+  await page.evaluate(()=>{const k=e=>e.closest('.drawer,#drawer,aside');document.querySelectorAll('header,nav,.util,.mast,.topnav').forEach(e=>{if(!k(e))e.style.display='none'});if(!document.getElementById('cap-header')){const bar=document.createElement('div');bar.id='cap-header';bar.innerHTML=`<button><svg width=24 height=24 viewBox="0 0 24 24" fill=none stroke=currentColor stroke-width=2.2 stroke-linecap=round><path d="M3 6h18M3 12h18M3 18h18"/></svg></button><a class=cap-brand href="/">REN<span>TV</span></a><button><svg width=21 height=21 viewBox="0 0 24 24" fill=none stroke=currentColor stroke-width=2><circle cx=12 cy=8 r=3.6/><path d="M4.5 20a7.5 7.5 0 0 1 15 0" stroke-linecap=round/></svg></button>`;document.body.insertBefore(bar,document.body.firstChild)}window.scrollTo(0,0)});
+  await page.waitForTimeout(300);
+}
+
+const b=await chromium.launch();
+const ctx=await b.newContext({viewport:{width:1440,height:810},deviceScaleFactor:2,httpCredentials:{username:'user',password:'RentvUser2026!'}});
+
+// ---- article.png : real article page ----
+{
+  const p=await ctx.newPage();
+  const resp=await p.goto(BASE+'/news/34251',{waitUntil:'domcontentloaded',timeout:45000});
+  await p.waitForTimeout(1400);
+  await normalize(p);
+  await p.screenshot({path:`${OUT}/article.png`});
+  console.log('OK article.png <- /news/34251 ['+resp.status()+']');
+  await p.close();
+}
+
+// ---- theme captures ----
+for (const [name,key] of [['theme-midnight','midnight'],['theme-salmon','salmon']]){
+  const p=await ctx.newPage();
+  const resp=await p.goto(BASE+'/',{waitUntil:'domcontentloaded',timeout:45000});
+  await p.waitForTimeout(1400);
+  const t=THEMES[key];
+  await p.evaluate((t)=>{
+    const r=document.documentElement;
+    r.style.setProperty('--red',t.accent);r.style.setProperty('--ink',t.ink);
+    r.style.setProperty('--sub',t.sub);r.style.setProperty('--line',t.line);
+    r.style.setProperty('--bg',t.bg);r.style.setProperty('--wash',t.wash);
+    r.style.setProperty('--serif',t.serif);r.style.setProperty('--sans',t.sans);
+    document.body.classList.toggle('theme-dark',!!t.dark);
+    // dark surface overrides (mirror theme.js)
+    if(t.dark){
+      const st=document.createElement('style');st.id='dark-ovr';
+      st.textContent="body.theme-dark .util,body.theme-dark footer,body.theme-dark .ns,body.theme-dark .mast{background:var(--wash)!important}body.theme-dark .util,body.theme-dark .util a,body.theme-dark footer,body.theme-dark footer a,body.theme-dark footer p{color:var(--sub)!important}body.theme-dark footer .logo,body.theme-dark footer h5{color:var(--ink)!important}body.theme-dark .ns{color:var(--ink)!important}body.theme-dark .ns p{color:var(--sub)!important}";
+      document.head.appendChild(st);
+    }
+    document.body.style.background=t.bg;
+  },t);
+  await p.waitForTimeout(700);
+  await normalize(p, t.dark?t:null);
+  await p.screenshot({path:`${OUT}/${name}.png`});
+  console.log('OK '+name+'.png theme='+key+' ['+resp.status()+']');
+  await p.close();
+}
+await b.close();
diff --git a/probe.mjs b/probe.mjs
new file mode 100644
index 0000000..1d69566
--- /dev/null
+++ b/probe.mjs
@@ -0,0 +1,24 @@
+import { chromium } from 'playwright';
+const BASE='https://rentv.agentabrams.com';
+const b=await chromium.launch();
+const ctx=await b.newContext({viewport:{width:1440,height:810},httpCredentials:{username:'user',password:'RentvUser2026!'}});
+const p=await ctx.newPage();
+await p.goto(BASE+'/',{waitUntil:'domcontentloaded'});
+await p.waitForTimeout(1500);
+// theme switcher
+const themeInfo=await p.evaluate(()=>{
+  const out={selects:[],themeEls:[]};
+  document.querySelectorAll('select').forEach(s=>out.selects.push({id:s.id,cls:s.className,opts:[...s.options].map(o=>o.value+'|'+o.textContent.trim()).slice(0,12)}));
+  document.querySelectorAll('[data-theme],[id*="theme" i],[class*="theme" i]').forEach(e=>out.themeEls.push({tag:e.tagName,id:e.id,cls:e.className,dt:e.getAttribute('data-theme'),txt:e.textContent.trim().slice(0,40)}));
+  return out;
+});
+console.log('THEME:',JSON.stringify(themeInfo,null,1));
+// article links
+const links=await p.evaluate(()=>{
+  const set=new Set();
+  document.querySelectorAll('a[href]').forEach(a=>{const h=a.getAttribute('href');if(/news|article|story/i.test(h))set.add(h);});
+  return [...set].slice(0,20);
+});
+console.log('ARTICLE LINKS:',links);
+// check theme.js exists
+await b.close();
diff --git a/recapture-results.json b/recapture-results.json
new file mode 100644
index 0000000..1608e20
--- /dev/null
+++ b/recapture-results.json
@@ -0,0 +1,107 @@
+[
+  {
+    "name": "home",
+    "route": "/",
+    "status": 200
+  },
+  {
+    "name": "article",
+    "route": "/news/",
+    "status": 404
+  },
+  {
+    "name": "review",
+    "route": "/review",
+    "status": 200
+  },
+  {
+    "name": "markets",
+    "route": "/markets",
+    "status": 200
+  },
+  {
+    "name": "pulse",
+    "route": "/pulse",
+    "status": 200
+  },
+  {
+    "name": "cretalk",
+    "route": "/cre-talk",
+    "status": 200
+  },
+  {
+    "name": "blog",
+    "route": "/blog",
+    "status": 200
+  },
+  {
+    "name": "versions",
+    "route": "/versions",
+    "status": 200
+  },
+  {
+    "name": "v-wire",
+    "route": "/versions/wire.html",
+    "status": 200
+  },
+  {
+    "name": "v-broadsheet",
+    "route": "/versions/broadsheet.html",
+    "status": 200
+  },
+  {
+    "name": "v-terminal",
+    "route": "/versions/terminal.html",
+    "status": 200
+  },
+  {
+    "name": "v-magazine",
+    "route": "/versions/magazine.html",
+    "status": 200
+  },
+  {
+    "name": "v-dashboard",
+    "route": "/versions/dashboard.html",
+    "status": 200
+  },
+  {
+    "name": "admin-index",
+    "route": "/admin",
+    "status": 200
+  },
+  {
+    "name": "admin-command",
+    "route": "/admin/command.html",
+    "status": 200
+  },
+  {
+    "name": "admin-kanban",
+    "route": "/admin/kanban.html",
+    "status": 200
+  },
+  {
+    "name": "publish",
+    "route": "/admin",
+    "status": 200
+  },
+  {
+    "name": "desk",
+    "route": "/desk",
+    "status": 200
+  },
+  {
+    "name": "social",
+    "route": "/social",
+    "status": 200
+  },
+  {
+    "name": "theme-midnight",
+    "route": "/ (theme)",
+    "status": 200
+  },
+  {
+    "name": "theme-salmon",
+    "route": "/ (theme)",
+    "status": 200
+  }
+]
\ No newline at end of file
diff --git a/rentv-recapture.mjs b/rentv-recapture.mjs
new file mode 100644
index 0000000..45da171
--- /dev/null
+++ b/rentv-recapture.mjs
@@ -0,0 +1,175 @@
+import { chromium } from 'playwright';
+import fs from 'fs';
+
+const OUT = '/Users/macstudio3/Projects/rentv-master-reel/media/captures';
+const BASE = 'https://rentv.agentabrams.com';
+const ADMIN = { username: 'admin', password: 'DW2024!' };
+const USER = { username: 'user', password: 'RentvUser2026!' };
+
+const CLEAN_CSS = `#cap-header{position:sticky;top:0;z-index:99999;display:flex;align-items:center;justify-content:space-between;height:64px;padding:0 24px;background:#fff;border-bottom:1px solid #e6eaef;font-family:-apple-system,sans-serif}#cap-header .cap-brand{font-size:23px;font-weight:800;color:#0d1b2a;text-decoration:none}#cap-header .cap-brand span{color:#c0392b}#cap-header button{background:none;border:0;color:#0d1b2a;display:inline-flex;padding:9px}`;
+
+async function normalize(page) {
+  try {
+    await page.addStyleTag({ content: CLEAN_CSS });
+    await page.evaluate(() => {
+      const k = e => e.closest('.drawer,#drawer,aside');
+      document.querySelectorAll('header,nav,.util,.mast,.topnav').forEach(e => { if (!k(e)) e.style.display = 'none'; });
+      if (!document.getElementById('cap-header')) {
+        const bar = document.createElement('div');
+        bar.id = 'cap-header';
+        bar.innerHTML = `<button><svg width=24 height=24 viewBox="0 0 24 24" fill=none stroke=currentColor stroke-width=2.2 stroke-linecap=round><path d="M3 6h18M3 12h18M3 18h18"/></svg></button><a class=cap-brand href="/">REN<span>TV</span></a><button><svg width=21 height=21 viewBox="0 0 24 24" fill=none stroke=currentColor stroke-width=2><circle cx=12 cy=8 r=3.6/><path d="M4.5 20a7.5 7.5 0 0 1 15 0" stroke-linecap=round/></svg></button>`;
+        document.body.insertBefore(bar, document.body.firstChild);
+      }
+      window.scrollTo(0, 0);
+    });
+    await page.waitForTimeout(300);
+  } catch (e) { /* clean page, skip */ }
+}
+
+async function shoot(context, name, route, { doNormalize = true, before = null } = {}) {
+  const page = await context.newPage();
+  let status = 'ERR';
+  try {
+    const resp = await page.goto(BASE + route, { waitUntil: 'domcontentloaded', timeout: 45000 });
+    status = resp ? resp.status() : 'no-resp';
+    await page.waitForTimeout(1200);
+    if (before) await before(page);
+    if (doNormalize) await normalize(page);
+    await page.screenshot({ path: `${OUT}/${name}.png`, fullPage: false });
+    console.log(`OK  ${name}.png  <- ${route}  [${status}]`);
+  } catch (e) {
+    console.log(`FAIL ${name}.png <- ${route}  [${status}] ${e.message.split('\n')[0]}`);
+    status = 'FAIL:' + status;
+  } finally {
+    await page.close();
+  }
+  return { name, route, status };
+}
+
+const results = [];
+
+(async () => {
+  const browser = await chromium.launch();
+  const vp = { viewport: { width: 1440, height: 810 }, deviceScaleFactor: 2 };
+
+  const userCtx = await browser.newContext({ ...vp, httpCredentials: USER });
+  const adminCtx = await browser.newContext({ ...vp, httpCredentials: ADMIN });
+
+  // ---- USER pages ----
+  results.push(await shoot(userCtx, 'home', '/'));
+
+  // article: grab first headline href from /news/
+  {
+    const page = await userCtx.newPage();
+    let articleHref = null, status = 'ERR';
+    try {
+      const resp = await page.goto(BASE + '/news/', { waitUntil: 'domcontentloaded', timeout: 45000 });
+      status = resp ? resp.status() : 'no-resp';
+      await page.waitForTimeout(1500);
+      articleHref = await page.evaluate(() => {
+        const sel = ['a[href*="/news/"]', 'article a', '.headline a', 'h2 a', 'h3 a', '.story a', 'a[href*="/article"]'];
+        for (const s of sel) {
+          for (const a of document.querySelectorAll(s)) {
+            const h = a.getAttribute('href') || '';
+            if (h && !h.endsWith('/news/') && !h.endsWith('/news') && /news|article|story|\/\d/.test(h)) return h;
+          }
+        }
+        const a = document.querySelector('main a[href], a[href]');
+        return a ? a.getAttribute('href') : null;
+      });
+      console.log('article href ->', articleHref, '(news status ' + status + ')');
+    } catch (e) { console.log('news list err', e.message); }
+    await page.close();
+    if (articleHref) {
+      const full = articleHref.startsWith('http') ? articleHref.replace(BASE, '') : articleHref;
+      results.push(await shoot(userCtx, 'article', full));
+    } else {
+      results.push(await shoot(userCtx, 'article', '/news/'));
+    }
+  }
+
+  results.push(await shoot(userCtx, 'review', '/review'));
+  results.push(await shoot(userCtx, 'markets', '/markets'));
+  {
+    const r = await shoot(userCtx, 'pulse', '/pulse');
+    if (String(r.status).startsWith('4')) {
+      const r2 = await shoot(userCtx, 'pulse', '/market-pulse.html');
+      results.push(r2.status && !String(r2.status).startsWith('4') ? r2 : r);
+    } else results.push(r);
+  }
+  results.push(await shoot(userCtx, 'cretalk', '/cre-talk'));
+  results.push(await shoot(userCtx, 'blog', '/blog'));
+
+  // ---- ADMIN pages ----
+  results.push(await shoot(adminCtx, 'versions', '/versions'));
+  results.push(await shoot(adminCtx, 'v-wire', '/versions/wire.html'));
+  results.push(await shoot(adminCtx, 'v-broadsheet', '/versions/broadsheet.html'));
+  results.push(await shoot(adminCtx, 'v-terminal', '/versions/terminal.html'));
+  results.push(await shoot(adminCtx, 'v-magazine', '/versions/magazine.html'));
+  results.push(await shoot(adminCtx, 'v-dashboard', '/versions/dashboard.html'));
+  results.push(await shoot(adminCtx, 'admin-index', '/admin'));
+
+  {
+    const r = await shoot(adminCtx, 'admin-command', '/admin/command.html');
+    if (String(r.status).startsWith('4')) { results.push(await shoot(adminCtx, 'admin-command', '/admin')); }
+    else results.push(r);
+  }
+  {
+    const r = await shoot(adminCtx, 'admin-kanban', '/admin/kanban.html');
+    results.push(r);
+  }
+  results.push(await shoot(adminCtx, 'publish', '/admin'));
+  results.push(await shoot(adminCtx, 'desk', '/desk'));
+  results.push(await shoot(adminCtx, 'social', '/social'));
+
+  // ---- THEME captures (user ctx, home page) ----
+  async function themeShot(name, matchers) {
+    const page = await userCtx.newPage();
+    let status = 'ERR', picked = null;
+    try {
+      const resp = await page.goto(BASE + '/', { waitUntil: 'domcontentloaded', timeout: 45000 });
+      status = resp ? resp.status() : 'no-resp';
+      await page.waitForTimeout(1500);
+      // try select#rentv-theme
+      picked = await page.evaluate((matchers) => {
+        const sel = document.querySelector('#rentv-theme') || document.querySelector('select[id*="theme" i]') || document.querySelector('select');
+        if (sel && sel.options) {
+          for (const opt of sel.options) {
+            const t = (opt.textContent + ' ' + opt.value).toLowerCase();
+            if (matchers.some(m => t.includes(m))) {
+              sel.value = opt.value;
+              sel.dispatchEvent(new Event('change', { bubbles: true }));
+              return 'select:' + opt.value;
+            }
+          }
+        }
+        return null;
+      }, matchers);
+      if (!picked) {
+        // try clickable theme chips/buttons
+        picked = await page.evaluate((matchers) => {
+          const els = [...document.querySelectorAll('[data-theme],.theme-chip,.theme-option,button,li,a')];
+          for (const el of els) {
+            const t = ((el.getAttribute('data-theme') || '') + ' ' + el.textContent).toLowerCase();
+            if (matchers.some(m => t.includes(m))) { el.click(); return 'click:' + t.trim().slice(0, 30); }
+          }
+          return null;
+        }, matchers);
+      }
+      await page.waitForTimeout(700);
+      await normalize(page);
+      await page.screenshot({ path: `${OUT}/${name}.png`, fullPage: false });
+      console.log(`OK  ${name}.png  theme pick=${picked} [${status}]`);
+    } catch (e) { console.log(`FAIL ${name}.png ${e.message.split('\n')[0]}`); status = 'FAIL'; }
+    await page.close();
+    return { name, route: '/ (theme)', status: picked ? status : `${status} THEME-NOT-FOUND` };
+  }
+  results.push(await themeShot('theme-midnight', ['midnight', 'capital']));
+  results.push(await themeShot('theme-salmon', ['salmon', 'f.t', 'ft ', 'financial']));
+
+  await browser.close();
+
+  console.log('\n===== SUMMARY =====');
+  for (const r of results) console.log(`${r.status}\t${r.name}.png\t<- ${r.route}`);
+  fs.writeFileSync('/Users/macstudio3/Projects/rentv-tour/recapture-results.json', JSON.stringify(results, null, 2));
+})();
diff --git a/videos/pitch/index.html b/videos/pitch/index.html
index b9d3fd2..2bf309a 100644
--- a/videos/pitch/index.html
+++ b/videos/pitch/index.html
@@ -1,6 +1,6 @@
 <!doctype html><html lang="en"><head>
 <meta charset="UTF-8" /><meta name="viewport" content="width=1920, height=1080" />
-<title>RENTV — Growth Strategy</title>
+<title>RENTV — Advertising &amp; Media Plan</title>
 <script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
 <style>
   body{margin:0;background:#070b10;font-family:system-ui,-apple-system,"Helvetica Neue",Arial,sans-serif}
@@ -22,15 +22,16 @@
   .label{left:56px;bottom:60px;right:auto;top:auto;width:auto;height:auto}
   .li{display:flex}.li .txt{color:#fff;font-size:30px;font-weight:500;background:rgba(9,15,24,.9);padding:8px 18px;border-radius:10px}
 </style></head><body>
-  <div id="root" data-composition-id="main" data-start="0" data-width="1920" data-height="1080" data-duration="33.05" data-fps="30">
-  <div id="bg" class="clip" data-start="0" data-duration="33.05" data-track-index="0"></div>
+  <div id="root" data-composition-id="main" data-start="0" data-width="1920" data-height="1080" data-duration="30.55" data-fps="30">
+  <div id="bg" class="clip" data-start="0" data-duration="30.55" data-track-index="0"></div>
   <audio id="vo" src="assets/vo.m4a" data-start="0" data-volume="1"></audio>
-  <section id="s0" class="clip card" data-start="0.000" data-duration="2.400" data-track-index="1"><div class="ci"><div class="tag" id="s0t">RENTV · GROWTH DECK</div><h1 id="s0h">Growth Strategy</h1><p id="s0p">Own Western U.S. commercial real estate news</p></div></section>
-  <section id="s1" class="clip card left" data-start="2.400" data-duration="9.250" data-track-index="1"><div class="ci wide"><h2 id="s1h" style="color:#57b0ff">The Plan</h2><ul><li id="s1i0">Modernize the site — fast &amp; mobile-first</li><li id="s1i1">Grow the newsletter &amp; subscriber base</li><li id="s1i2">Integrated social engine (LinkedIn · X · Instagram)</li><li id="s1i3">Search optimization to own the rank</li></ul></div></section>
-  <section id="s2" class="clip card left" data-start="11.650" data-duration="9.250" data-track-index="1"><div class="ci wide"><h2 id="s2h" style="color:#57b0ff">Outrank</h2><ul><li id="s2i0">The Real Deal</li><li id="s2i1">Bisnow</li><li id="s2i2">GlobeSt</li><li id="s2i3">CoStar News</li></ul></div></section>
-  <div id="s3" class="clip shot" data-start="20.900" data-duration="9.250" data-track-index="2"><img id="s3i" class="simg" src="assets/home.png" alt="The rebuilt front page"></div>
-  <div id="s3l" class="clip label" data-start="20.900" data-duration="9.250" data-track-index="3"><div class="li"><span class="txt">The rebuilt front page</span></div></div>
-  <section id="s4" class="clip card" data-start="30.150" data-duration="2.900" data-track-index="1"><div class="ci"><h1 id="s4h">The flywheel that compounds</h1></div></section>
+  <section id="s0" class="clip card" data-start="0.000" data-duration="2.400" data-track-index="1"><div class="ci"><div class="tag" id="s0t">RENTV · GROWTH DECK</div><h1 id="s0h">Advertising &amp; Media Plan</h1><p id="s0p">How RENTV wins attention</p></div></section>
+  <section id="s1" class="clip card left" data-start="2.400" data-duration="6.313" data-track-index="1"><div class="ci wide"><h2 id="s1h" style="color:#57b0ff">Channels — already connected</h2><ul><li id="s1i0">LinkedIn</li><li id="s1i1">X (Twitter)</li><li id="s1i2">Instagram</li></ul></div></section>
+  <section id="s2" class="clip card left" data-start="8.713" data-duration="6.313" data-track-index="1"><div class="ci wide"><h2 id="s2h" style="color:#57b0ff">Paid amplifies the winners</h2><ul><li id="s2i0">Sponsored deal alerts</li><li id="s2i1">Conference promotion</li><li id="s2i2">Newsletter retargeting</li></ul></div></section>
+  <section id="s3" class="clip card left" data-start="15.025" data-duration="6.313" data-track-index="1"><div class="ci wide"><h2 id="s3h" style="color:#57b0ff">Content engine (already produced)</h2><ul><li id="s3i0">Daily deals</li><li id="s3i1">Market Pulse</li><li id="s3i2">The REview video platform</li></ul></div></section>
+  <div id="s4" class="clip shot" data-start="21.338" data-duration="6.313" data-track-index="2"><img id="s4i" class="simg" src="assets/social.png" alt="The social command center"></div>
+  <div id="s4l" class="clip label" data-start="21.338" data-duration="6.313" data-track-index="3"><div class="li"><span class="txt">The social command center</span></div></div>
+  <section id="s5" class="clip card" data-start="27.650" data-duration="2.900" data-track-index="1"><div class="ci"><h1 id="s5h">Readers → subscribers → registrations</h1></div></section>
   </div>
   <script>
     window.__timelines = window.__timelines || {};
@@ -41,16 +42,18 @@
     tl.from('#s1i0',{x:-20,autoAlpha:0,duration:0.4},2.750);
     tl.from('#s1i1',{x:-20,autoAlpha:0,duration:0.4},3.030);
     tl.from('#s1i2',{x:-20,autoAlpha:0,duration:0.4},3.310);
-    tl.from('#s1i3',{x:-20,autoAlpha:0,duration:0.4},3.590);
-    tl.from('#s2h',{x:-24,autoAlpha:0,duration:0.5,ease:'power3.out'},11.750);
-    tl.from('#s2i0',{x:-20,autoAlpha:0,duration:0.4},12.000);
-    tl.from('#s2i1',{x:-20,autoAlpha:0,duration:0.4},12.280);
-    tl.from('#s2i2',{x:-20,autoAlpha:0,duration:0.4},12.560);
-    tl.from('#s2i3',{x:-20,autoAlpha:0,duration:0.4},12.840);
-    tl.from('#s3i',{opacity:0,duration:0.35},20.900);
-    tl.fromTo('#s3i',{scale:1.06},{scale:1.0,duration:9.250,ease:'none'},20.900);
-    tl.from('#s3l',{y:22,autoAlpha:0,duration:0.4},21.000);
-    tl.from('#s4h',{y:34,autoAlpha:0,duration:0.6,ease:'power3.out'},30.300);
+    tl.from('#s2h',{x:-24,autoAlpha:0,duration:0.5,ease:'power3.out'},8.813);
+    tl.from('#s2i0',{x:-20,autoAlpha:0,duration:0.4},9.063);
+    tl.from('#s2i1',{x:-20,autoAlpha:0,duration:0.4},9.342);
+    tl.from('#s2i2',{x:-20,autoAlpha:0,duration:0.4},9.623);
+    tl.from('#s3h',{x:-24,autoAlpha:0,duration:0.5,ease:'power3.out'},15.125);
+    tl.from('#s3i0',{x:-20,autoAlpha:0,duration:0.4},15.375);
+    tl.from('#s3i1',{x:-20,autoAlpha:0,duration:0.4},15.655);
+    tl.from('#s3i2',{x:-20,autoAlpha:0,duration:0.4},15.935);
+    tl.from('#s4i',{opacity:0,duration:0.35},21.338);
+    tl.fromTo('#s4i',{scale:1.06},{scale:1.0,duration:6.313,ease:'none'},21.338);
+    tl.from('#s4l',{y:22,autoAlpha:0,duration:0.4},21.438);
+    tl.from('#s5h',{y:34,autoAlpha:0,duration:0.6,ease:'power3.out'},27.800);
     window.__timelines['main'] = tl;
   </script>
 </body></html>
\ No newline at end of file

← 8653362 auto-save: 2026-07-28T14:31:09 (3 files) — batch-pitch.sh pi  ·  back to Rentv Tour  ·  auto-save: 2026-07-28T15:31:27 (2 files) — fullpage-results. 521eac4 →