← back to Rentv Sheet Enrich
rentv :9797 draft composer: richer Follow-up (thank-you + questions + scheduling options)
1098075c6dfebd37172c9cd0780e4a48d0ce0d14 · 2026-08-13 16:57:32 -0700 · Steve Abrams
Follow-up purpose now has its own copy across Email/LinkedIn/Phone: thanks them
for taking the time to review, asks 2-3 tailored questions, and offers creative
next steps (15-min call windows, 'name a better week' follow-up date, one-pager).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Files touched
Diff
commit 1098075c6dfebd37172c9cd0780e4a48d0ce0d14
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Thu Aug 13 16:57:32 2026 -0700
rentv :9797 draft composer: richer Follow-up (thank-you + questions + scheduling options)
Follow-up purpose now has its own copy across Email/LinkedIn/Phone: thanks them
for taking the time to review, asks 2-3 tailored questions, and offers creative
next steps (15-min call windows, 'name a better week' follow-up date, one-pager).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---
public_live/index.html | 34 ++++++++++++++++++++--------------
1 file changed, 20 insertions(+), 14 deletions(-)
diff --git a/public_live/index.html b/public_live/index.html
index de0b794..9d73f7d 100644
--- a/public_live/index.html
+++ b/public_live/index.html
@@ -126,6 +126,8 @@
.lkrow td{padding:6px 10px} .lkv{max-width:680px} .lkn{color:var(--dim);text-align:right;white-space:nowrap}
.lkrow .lkbx{width:18px;color:transparent} .lkrow.on{background:rgba(123,192,67,.12)} .lkrow.on .lkbx{color:#7bc043}
.rowbtn{background:var(--accent);color:#04121f;border:0;border-radius:6px;padding:3px 8px;font-size:11px;font-weight:700;cursor:pointer;white-space:nowrap}
+ /* row-number gutter down the left */
+ td.rownum,th.rownum{color:var(--dim);text-align:right;font-size:.82em;font-variant-numeric:tabular-nums;background:var(--panel2);border-right:1px solid var(--line);user-select:none;padding:6px 7px}
.grouphdr td{background:var(--ghdr-bg);color:var(--ghdr-fg);font-weight:600;font-size:11px;letter-spacing:.3px;padding:5px 9px;position:sticky;top:26px;box-shadow:inset 3px 0 0 var(--accent);border-bottom:1px solid var(--line)}
/* cards */
.cards{display:grid;gap:11px} .cards.grid{grid-template-columns:repeat(auto-fill,minmax(300px,1fr))} .cards.list{grid-template-columns:1fr}
@@ -477,13 +479,13 @@ function render(){
}
function tableHTML(rows){
const cols=visCols();
- const cg='<colgroup><col style="width:44px">'+cols.map(c=>`<col style="width:${(COLW[c.i]||180)}px">`).join('')+'</colgroup>';
- const totalW=44+cols.reduce((s,c)=>s+(COLW[c.i]||180),0); // absolute widths -> columns don't re-proportion on render
- let h='<table id="grid" style="width:'+totalW+'px">'+cg+'<thead><tr><th></th>'+cols.map(c=>`<th data-c="${c.i}" title="drag to move · drag right edge to resize · dbl-click edge to autofit">${esc(c.h)}${SORT&&SORT.col===c.i?(SORT.dir<0?' ▼':' ▲'):''}<span class="rz" data-c="${c.i}"></span></th>`).join('')+'</tr></thead><tbody>';
- let lastG=null;
+ const cg='<colgroup><col style="width:46px"><col style="width:44px">'+cols.map(c=>`<col style="width:${(COLW[c.i]||180)}px">`).join('')+'</colgroup>';
+ const totalW=46+44+cols.reduce((s,c)=>s+(COLW[c.i]||180),0); // # gutter + action col + absolute data widths
+ let h='<table id="grid" style="width:'+totalW+'px">'+cg+'<thead><tr><th class="rownum">#</th><th></th>'+cols.map(c=>`<th data-c="${c.i}" title="drag to move · drag right edge to resize · dbl-click edge to autofit">${esc(c.h)}${SORT&&SORT.col===c.i?(SORT.dir<0?' ▼':' ▲'):''}<span class="rz" data-c="${c.i}"></span></th>`).join('')+'</tr></thead><tbody>';
+ let lastG=null, rn=0;
for(const [r,ri] of rows){
- if(GROUP!==''){ const gv=g(r,+GROUP)||'(blank)'; if(gv!==lastG){ lastG=gv; h+=`<tr class="grouphdr"><td colspan="${cols.length+1}">${esc(gv)}</td></tr>`; } }
- h+=`<tr><td><button class="rowbtn" data-r="${ri}">✍</button></td>`;
+ if(GROUP!==''){ const gv=g(r,+GROUP)||'(blank)'; if(gv!==lastG){ lastG=gv; h+=`<tr class="grouphdr"><td colspan="${cols.length+2}">${esc(gv)}</td></tr>`; } }
+ h+=`<tr><td class="rownum">${++rn}</td><td><button class="rowbtn" data-r="${ri}">✍</button></td>`;
for(const c of cols){ const gr=GREENSET.has(ri+','+c.i), isN=c.i===ROLE.name, val=g(r,c.i);
h+=cellTD(ri,c.i,val,isN,gr); }
h+='</tr>';
@@ -615,7 +617,7 @@ function wireGrid(){
$$('#grid th .rz').forEach(rz=>rz.addEventListener('mousedown',e=>{
e.preventDefault();e.stopPropagation();
const ci=+rz.dataset.c, cols=visCols(), idx=cols.findIndex(c=>c.i===ci);
- const col=document.querySelectorAll('#grid colgroup col')[idx+1]; // +1 for the action col
+ const col=document.querySelectorAll('#grid colgroup col')[idx+2]; // +2 for the # gutter + action col
const startX=e.clientX, startW=col.getBoundingClientRect().width;
// SMOOTH: drag a light guide line (no per-frame table reflow); apply width once on release
document.body.classList.add('resizing');
@@ -631,9 +633,9 @@ function wireGrid(){
$$('#grid th .rz').forEach(rz=>rz.addEventListener('dblclick',e=>{
e.preventDefault();e.stopPropagation();
const ci=+rz.dataset.c, cols=visCols(), idx=cols.findIndex(c=>c.i===ci);
- const col=document.querySelectorAll('#grid colgroup col')[idx+1];
+ const col=document.querySelectorAll('#grid colgroup col')[idx+2];
let max=String(DATA.headers[ci]||'').length;
- document.querySelectorAll('#grid tbody tr').forEach(tr=>{const t=tr.children[idx+1]?.textContent||'';if(t.length>max)max=t.length;});
+ document.querySelectorAll('#grid tbody tr').forEach(tr=>{const t=tr.children[idx+2]?.textContent||'';if(t.length>max)max=t.length;});
const w=Math.min(600,Math.max(70,Math.round(max*7.1)+26));col.style.width=w+'px';COLW[ci]=w;LS.set('colw.'+curKey(),COLW);
}));
$$('td[contenteditable="true"]').forEach(td=>{td.addEventListener('focus',()=>{editing=true;td._o=td.textContent});td.addEventListener('blur',()=>saveCell(td));td.addEventListener('keydown',e=>{if(e.key==='Enter'&&!e.shiftKey){e.preventDefault();td.blur()}})});
@@ -674,14 +676,18 @@ function syncChan(){$$('#chanseg button').forEach(b=>b.classList.toggle('on',b.d
function regen(){const c=contact(DATA.rows[curRow]),d=buildDraft(c,curChan,$('#purpose').value,$('#tone').value);$('#d-subject').value=d.subject||'';$('#d-body').value=d.body;$('#d-target').textContent=d.target?('→ '+d.target):''}
function buildDraft(c,chan,purpose,tone){const first=(c.first&&/^[A-Za-z]/.test(c.first))?c.first:'there',co=c.company||'your team';
const hi=tone==='formal'?('Dear '+(c.name||first)+','):(tone==='direct'?('Hi '+first+' —'):('Hi '+first+','));
- const val={partnership:`I run partnerships for RENTV (Real Estate News Television), the CRE industry's video news network. I'd love to explore ways ${co} and RENTV could work together — featuring your deals and leadership on our broadcasts and co-marketing to our commercial-real-estate audience.`,sponsorship:`RENTV is lining up sponsors for our upcoming commercial real estate conference, and ${co} stood out as a strong fit — on-camera visibility, stage presence, and direct access to CRE principals, brokers, and capital sources.`,speaker:`We're building the panel lineup for RENTV's upcoming CRE conference and I'd like to invite ${co} to join as a speaker.`,followup:`Following up on RENTV's outreach to ${co} — wanted to make sure this reached the right person and see if a short call this week makes sense.`}[purpose];
+ const val={partnership:`I run partnerships for RENTV (Real Estate News Television), the CRE industry's video news network. I'd love to explore ways ${co} and RENTV could work together — featuring your deals and leadership on our broadcasts and co-marketing to our commercial-real-estate audience.`,sponsorship:`RENTV is lining up sponsors for our upcoming commercial real estate conference, and ${co} stood out as a strong fit — on-camera visibility, stage presence, and direct access to CRE principals, brokers, and capital sources.`,speaker:`We're building the panel lineup for RENTV's upcoming CRE conference and I'd like to invite ${co} to join as a speaker.`,followup:`Following up on our conversation about RENTV and ${co} — and thank you again for taking the time to review it with me.`}[purpose];
const close=tone==='formal'?'Kind regards,\nSteve Bloom\nRENTV — Real Estate News Television':'Best,\nSteve Bloom\nRENTV — Real Estate News Television';
- if(chan==='email'){const subj={partnership:'RENTV × '+co+' — partnership',sponsorship:co+' as a RENTV CRE conference sponsor',speaker:'Speaker invite — RENTV CRE conference',followup:'Following up — RENTV × '+co}[purpose];
- return{subject:subj,body:`${hi}\n\n${val}\n\nWould you be open to a 15-minute call this week?\n\n${close}`,target:c.email||'(no email)'}}
- if(chan==='linkedin'){const note=`${hi.replace(/ —$/,'')} — I run partnerships at RENTV (CRE video news). ${purpose==='sponsorship'?"We're selecting sponsors for our CRE conference and "+co+' is a great fit.':purpose==='speaker'?"I'd love to invite "+co+' onto a panel at our CRE conference.':"I'd love to connect about ways RENTV and "+co+' could work together.'} Open to a quick chat?`;
+ if(chan==='email'){const subj={partnership:'RENTV × '+co+' — partnership',sponsorship:co+' as a RENTV CRE conference sponsor',speaker:'Speaker invite — RENTV CRE conference',followup:'Great connecting — next steps for RENTV × '+co}[purpose];
+ return{subject:subj,body:(purpose==='followup'
+ ?`${hi}\n\nThank you for taking the time to review RENTV with me — I really enjoyed the conversation and appreciate you thinking it through with me.\n\nA couple of quick questions so I can tailor this to ${co}:\n• Did any of the formats — deal features, leadership spotlights, or our CRE conference — stand out as the best fit?\n• Are you the right person to move this forward, or is there a colleague I should loop in?\n\nWhenever you're ready, here are a few easy ways to take the next step — pick whatever's simplest:\n• 📅 A quick 15-minute call — I'm open Tuesday or Thursday afternoon this week (or just send two times that work and I'll make one work).\n• 🗓️ Prefer to circle back later? Name a better week and I'll follow up then — no chasing.\n• 📄 Short on time? I'll send a one-page overview you can review whenever it suits you.\n\nEither way, thanks again — I'm excited about what RENTV and ${co} could do together.\n\n${close}`
+ :`${hi}\n\n${val}\n\nWould you be open to a 15-minute call this week?\n\n${close}`),target:c.email||'(no email)'}}
+ if(chan==='linkedin'){const note=purpose==='followup'?`${hi.replace(/ —$/,'')} — thanks for taking the time to look at RENTV with me, I really appreciated it. Quick follow-up: did any of the ideas for ${co} stand out, and are you the right person to run with it or should I loop in a colleague? Happy to grab 15 min — I'm open Tue/Thu this week, or name a better week and I'll circle back. Or I can send a one-pager first — whatever's easiest.`:`${hi.replace(/ —$/,'')} — I run partnerships at RENTV (CRE video news). ${purpose==='sponsorship'?"We're selecting sponsors for our CRE conference and "+co+' is a great fit.':purpose==='speaker'?"I'd love to invite "+co+' onto a panel at our CRE conference.':"I'd love to connect about ways RENTV and "+co+' could work together.'} Open to a quick chat?`;
return{subject:'',body:note.slice(0,600),target:(c.liC||c.mvpL||c.liCo)||'(no LinkedIn)'}}
return{subject:'',target:c.phone||'(no phone on file)',
- body:`📞 PHONE PITCH — ${co}${c.name?' · '+c.name:''}\n\nOPEN\n"Hi${c.name?', '+first:''} — this is Steve Bloom, President of RENTV, the commercial real estate news network. Do you have a quick minute?"\n\nPITCH\n"${val} We reach roughly 50,000 CRE professionals — investors, developers, brokers and capital sources — twice a week, and I think ${co} would land really well with that audience."\n\nASK\n"I'd love to grab 15 minutes this week to walk you through a couple of options tailored to ${co}. What does later this week look like?"\n\nIF A GATEKEEPER ANSWERS\n"Who handles marketing or partnerships for ${co}?${c.mvpN?' I believe it may be '+c.mvpN+'.':''} Happy to send a quick email first${c.email?' — is '+c.email+' best?':'.'}"\n\nLEAVE-BEHIND\n"No problem if now isn't ideal — I'll follow up by email so you have it in writing."`}
+ body:(purpose==='followup'
+ ?`📞 FOLLOW-UP CALL — ${co}${c.name?' · '+c.name:''}\n\nOPEN\n"Hi${c.name?', '+first:''} — it's Steve Bloom at RENTV again. Thanks for taking the time to look this over with me — do you have a quick minute?"\n\nRECONNECT\n"Just following up on RENTV and ${co} — wanted to see what stood out and answer any questions that came up after we spoke."\n\nQUESTIONS (ask, then listen)\n• "Did any of the formats — deal features, leadership spots, or the CRE conference — feel like the best fit for ${co}?"\n• "Are you the right person to move this forward, or should I loop in a colleague?"\n• "Anything about timing or budget I should keep in mind?"\n\nSCHEDULE (offer options)\n"Let's put 15 minutes on the calendar — does Tuesday or Thursday afternoon work? If this week's tight, just tell me a better week and I'll follow up then. I can also send a one-page overview first so you have it in writing."\n\nLEAVE-BEHIND\n"No worries at all if now isn't ideal — I'll email you a quick overview and circle back${c.email?' at '+c.email:''}. Thanks again for your time."`
+ :`📞 PHONE PITCH — ${co}${c.name?' · '+c.name:''}\n\nOPEN\n"Hi${c.name?', '+first:''} — this is Steve Bloom, President of RENTV, the commercial real estate news network. Do you have a quick minute?"\n\nPITCH\n"${val} We reach roughly 50,000 CRE professionals — investors, developers, brokers and capital sources — twice a week, and I think ${co} would land really well with that audience."\n\nASK\n"I'd love to grab 15 minutes this week to walk you through a couple of options tailored to ${co}. What does later this week look like?"\n\nIF A GATEKEEPER ANSWERS\n"Who handles marketing or partnerships for ${co}?${c.mvpN?' I believe it may be '+c.mvpN+'.':''} Happy to send a quick email first${c.email?' — is '+c.email+' best?':'.'}"\n\nLEAVE-BEHIND\n"No problem if now isn't ideal — I'll follow up by email so you have it in writing."`)}
}
async function doEnhance(){const b=$('#enhance'),o=b.textContent;b.textContent='✨ thinking…';b.disabled=true;
try{const r=await (await fetch('/api/enhance',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({prompt:`Rewrite this ${curChan} outreach so it sounds natural, concise, professional. Keep it truthful, sender "Steve Bloom, RENTV". Return ONLY the message.\n\n---\n${$('#d-body').value}`})})).json();
← dfed233 Live console: email cells are now clickable mailto links; co
·
back to Rentv Sheet Enrich
·
Live console: row-number gutter down the left; drag-to-fill fe7b566 →