← back to Rentv
tools: CRE Toolkit (/tools) — cap rate, price per unit/SF, loan sizing (LTV vs DSCR) calculators, client-side + private
b236f48768199db7b93c3e3cbc546fea72ee25f7 · 2026-07-23 15:58:43 -0700 · Steve
Files touched
M public/index.htmlA public/tools.htmlA tools/specs/tools.json
Diff
commit b236f48768199db7b93c3e3cbc546fea72ee25f7
Author: Steve <steve@designerwallcoverings.com>
Date: Thu Jul 23 15:58:43 2026 -0700
tools: CRE Toolkit (/tools) — cap rate, price per unit/SF, loan sizing (LTV vs DSCR) calculators, client-side + private
---
public/index.html | 2 +-
public/tools.html | 175 +++++++++++++++++++++++++++++++++++++++++++++++++
tools/specs/tools.json | 23 +++++++
3 files changed, 199 insertions(+), 1 deletion(-)
diff --git a/public/index.html b/public/index.html
index 8394f393..3887ac7e 100644
--- a/public/index.html
+++ b/public/index.html
@@ -104,7 +104,7 @@
<div class="dh"><span class="w">REN<span>TV</span></span><button id="dclose" aria-label="Close">✕</button></div>
<div class="snap" id="snap"></div>
<div class="grp"><div class="gt">Discover</div></div>
- <nav><a href="/search">🔎 Search</a><a href="/pulse">📊 Market Pulse</a></nav>
+ <nav><a href="/search">🔎 Search</a><a href="/pulse">📊 Market Pulse</a><a href="/tools">🧮 CRE Toolkit</a></nav>
<div class="grp"><div class="gt">Sections</div></div>
<nav><a href="/sales">Sales</a><a href="/leases">Leases</a><a href="/financing">Financing</a><a href="/development">Development</a><a href="/multifamily">Multifamily</a><a href="/industrial">Industrial</a><a href="/retail">Retail</a></nav>
<div class="grp"><div class="gt">Intelligence</div></div>
diff --git a/public/tools.html b/public/tools.html
new file mode 100644
index 00000000..ec78a861
--- /dev/null
+++ b/public/tools.html
@@ -0,0 +1,175 @@
+<!doctype html><html lang="en"><head>
+<meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
+<title>CRE Toolkit — RENTV.com</title>
+<meta name="description" content="RENTV CRE Toolkit — free commercial real estate calculators: cap rate, price per unit and per square foot, and loan sizing by LTV and DSCR.">
+<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:ital,wght@0,700;0,800;1,600&family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
+<style>
+ :root{--red:#c8102e;--ink:#14171a;--sub:#5b636b;--line:#e4e7ea;--wash:#f5f6f8;--up:#0f8a4d}
+ *{box-sizing:border-box;margin:0}
+ body{font-family:'Inter',system-ui,sans-serif;color:var(--ink);background:#fbfbfc;line-height:1.45}
+ a{color:inherit;text-decoration:none}
+ .top{position:sticky;top:0;z-index:900;display:flex;align-items:center;justify-content:space-between;height:54px;padding:0 16px;background:#fff;border-bottom:1px solid var(--line)}
+ .burger{display:inline-flex;align-items:center;gap:10px;border:0;background:none;cursor:pointer;font:inherit;color:inherit;padding:7px 9px;border-radius:8px}
+ .burger:hover{background:rgba(0,0,0,.06)}
+ .burger .lbl{font-size:12px;font-weight:800;letter-spacing:.14em;text-transform:uppercase;color:var(--sub)}
+ .brand{position:absolute;left:50%;transform:translateX(-50%);font-family:'Playfair Display',Georgia,serif;font-weight:800;font-size:25px;letter-spacing:.02em}.brand span{color:var(--red)}
+ .user{width:37px;height:37px;border-radius:50%;border:1.5px solid var(--line);background:#fff;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;color:var(--ink)}
+ .user:hover{border-color:var(--red);color:var(--red)}
+ .scrim{position:fixed;inset:0;background:rgba(6,9,12,.5);opacity:0;pointer-events:none;transition:opacity .22s;z-index:950}
+ .scrim.open{opacity:1;pointer-events:auto}
+ .drawer{position:fixed;top:0;left:0;height:100%;width:360px;max-width:88vw;background:#0b0e12;color:#e7ecf1;transform:translateX(-102%);transition:transform .26s cubic-bezier(.4,0,.2,1);z-index:951;display:flex;flex-direction:column;overflow-y:auto;box-shadow:2px 0 40px rgba(0,0,0,.4)}
+ .drawer.open{transform:none}
+ .drawer .dh{display:flex;align-items:center;justify-content:space-between;padding:16px 18px;border-bottom:1px solid #1c232b}
+ .drawer .dh .w{font-family:'Playfair Display',serif;font-size:23px;font-weight:800}.drawer .dh .w span{color:var(--red)}
+ .drawer .dh button{background:none;border:0;color:#8b949e;font-size:22px;cursor:pointer;line-height:1}
+ .grp{padding:14px 18px 4px}.grp .gt{font-size:10px;letter-spacing:.16em;text-transform:uppercase;color:#6b747d;margin-bottom:6px}
+ .drawer nav a{display:block;padding:9px 18px;color:#dbe1e7;font-size:14.5px;border-left:3px solid transparent}
+ .drawer nav a:hover{background:#141a21;border-left-color:var(--red);color:#fff}
+ .drawer nav a.active{background:#141a21;border-left-color:var(--red);color:#fff;font-weight:700}
+ .drawer .df{margin-top:auto;padding:16px 18px;border-top:1px solid #1c232b;font-size:11px;color:#6b747d}
+
+ .wrap{max-width:1040px;margin:0 auto;padding:0 22px;width:100%}
+ .thead{padding:34px 0 6px}
+ .thead .k{color:var(--red);font-weight:800;font-size:12px;letter-spacing:.16em;text-transform:uppercase}
+ .thead h1{font-family:'Playfair Display',serif;font-size:clamp(32px,3.6vw,46px);font-weight:700;letter-spacing:-.01em;margin-top:4px}
+ .thead p{color:var(--sub);font-size:15px;margin-top:4px}
+ .grid{display:grid;grid-template-columns:1fr 1fr;gap:22px;padding:22px 0 60px}
+ .calc{background:#fff;border:1px solid var(--line);border-radius:14px;padding:20px 22px;display:flex;flex-direction:column}
+ .calc h3{font-size:12px;text-transform:uppercase;letter-spacing:.09em;border-top:2px solid var(--ink);padding-top:10px;margin-bottom:4px;font-weight:800}
+ .calc .hint{color:var(--sub);font-size:12.5px;margin-bottom:14px}
+ .f{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:11px}
+ .f label{font-size:13.5px;font-weight:600;color:#2c3238}
+ .f .in{position:relative}
+ .f .in .pre,.f .in .suf{position:absolute;top:50%;transform:translateY(-50%);color:var(--sub);font-size:14px;font-weight:600}
+ .f .in .pre{left:11px}.f .in .suf{right:11px}
+ .f input{width:150px;font:600 15px 'Inter';padding:9px 12px;border:1.5px solid var(--line);border-radius:8px;outline:0;text-align:right;background:#fff}
+ .f input.pre{padding-left:22px}.f input.suf{padding-right:26px}
+ .f input:focus{border-color:var(--red)}
+ .out{margin-top:auto;background:var(--wash);border:1px solid var(--line);border-radius:10px;padding:14px 16px;display:flex;flex-direction:column;gap:8px}
+ .out .row{display:flex;align-items:baseline;justify-content:space-between}
+ .out .row .lab{font-size:12.5px;font-weight:700;color:var(--sub);text-transform:uppercase;letter-spacing:.05em}
+ .out .row .val{font-family:'Playfair Display',serif;font-size:26px;font-weight:800;letter-spacing:-.01em}
+ .out .row .val.red{color:var(--red)}
+ .out .note{font-size:12px;color:var(--sub)}
+ .out .note b{color:var(--ink)}
+ .foot{padding:0 0 50px;color:var(--sub);font-size:12px}
+ @media(max-width:820px){.grid{grid-template-columns:1fr}}
+</style></head><body>
+
+<header class="top">
+ <button class="burger" id="burger" aria-label="Menu">
+ <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><path d="M3 6h18M3 12h18M3 18h18"/></svg>
+ <span class="lbl">Menu</span>
+ </button>
+ <a class="brand" href="/">REN<span>TV</span></a>
+ <a class="user" href="/search" aria-label="Search" title="Search">
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><circle cx="11" cy="11" r="7"/><path d="M21 21l-4.3-4.3"/></svg>
+ </a>
+</header>
+
+<div class="scrim" id="scrim"></div>
+<aside class="drawer" id="drawer">
+ <div class="dh"><div class="w">REN<span>TV</span></div><button id="dx" aria-label="Close">×</button></div>
+ <div class="grp"><div class="gt">Discover</div></div>
+ <nav><a href="/search">Search</a><a href="/pulse">Market Pulse</a><a href="/tools" class="active">CRE Toolkit</a></nav>
+ <div class="grp"><div class="gt">Sections</div></div>
+ <nav><a href="/sales">Sales</a><a href="/leases">Leases</a><a href="/financing">Financing</a><a href="/development">Development</a><a href="/multifamily">Multifamily</a><a href="/industrial">Industrial</a><a href="/retail">Retail</a></nav>
+ <div class="grp"><div class="gt">Intelligence</div></div>
+ <nav><a href="/deals">Deals</a><a href="/markets">Markets</a><a href="/la-commercial">LA Commercial</a><a href="/desk">Broker & Owner Desk</a><a href="/review">The REview</a><a href="/cre-talk">CRE Talk</a><a href="/conferences">Conferences</a><a href="/blog">Blog</a></nav>
+ <div class="grp"><div class="gt">Company</div></div>
+ <nav><a href="/about">About</a><a href="/advertise">Advertise</a><a href="/subscribe">Subscribe / Newsletters</a></nav>
+ <div class="df">RENTV.com — Commercial real estate news, Western U.S.</div>
+</aside>
+
+<main class="wrap">
+ <div class="thead">
+ <div class="k">CRE Toolkit</div>
+ <h1>Underwrite a deal in seconds</h1>
+ <p>Free, private calculators for brokers, owners and investors. Everything runs in your browser — no numbers leave the page.</p>
+ </div>
+ <section class="grid">
+
+ <div class="calc" id="cap">
+ <h3>Cap Rate</h3><div class="hint">Net operating income ÷ purchase price.</div>
+ <div class="f"><label>Net operating income</label><span class="in"><span class="pre">$</span><input class="pre" data-k="noi" type="number" value="850000"></span></div>
+ <div class="f"><label>Purchase price</label><span class="in"><span class="pre">$</span><input class="pre" data-k="price" type="number" value="12500000"></span></div>
+ <div class="out">
+ <div class="row"><span class="lab">Cap rate</span><span class="val red" id="cap_out">—</span></div>
+ <div class="note" id="cap_note"></div>
+ </div>
+ </div>
+
+ <div class="calc" id="unit">
+ <h3>Price per Unit & per SF</h3><div class="hint">Basis metrics to compare against the comps.</div>
+ <div class="f"><label>Purchase price</label><span class="in"><span class="pre">$</span><input class="pre" data-k="price" type="number" value="12500000"></span></div>
+ <div class="f"><label>Units</label><span class="in"><input data-k="units" type="number" value="120"></span></div>
+ <div class="f"><label>Rentable SF</label><span class="in"><input data-k="sf" type="number" value="98000"></span></div>
+ <div class="out">
+ <div class="row"><span class="lab">Per unit</span><span class="val" id="unit_perunit">—</span></div>
+ <div class="row"><span class="lab">Per SF</span><span class="val" id="unit_persf">—</span></div>
+ </div>
+ </div>
+
+ <div class="calc" id="loan" style="grid-column:1/-1">
+ <h3>Loan Sizing — LTV vs. DSCR</h3><div class="hint">Lenders size to the lower of the two. This shows which one constrains your deal.</div>
+ <div style="display:grid;grid-template-columns:1fr 1fr;gap:0 30px">
+ <div class="f"><label>Purchase price</label><span class="in"><span class="pre">$</span><input class="pre" data-k="price" type="number" value="12500000"></span></div>
+ <div class="f"><label>Net operating income</label><span class="in"><span class="pre">$</span><input class="pre" data-k="noi" type="number" value="850000"></span></div>
+ <div class="f"><label>Max LTV</label><span class="in"><input class="suf" data-k="ltv" type="number" value="65"><span class="suf">%</span></span></div>
+ <div class="f"><label>Interest rate</label><span class="in"><input class="suf" data-k="rate" type="number" step="0.05" value="6.75"><span class="suf">%</span></span></div>
+ <div class="f"><label>Amortization</label><span class="in"><input class="suf" data-k="amort" type="number" value="30"><span class="suf">yr</span></span></div>
+ <div class="f"><label>Min DSCR</label><span class="in"><input data-k="dscr" type="number" step="0.05" value="1.25"></span></div>
+ </div>
+ <div class="out">
+ <div class="row"><span class="lab">Max loan (by LTV)</span><span class="val" id="loan_ltv">—</span></div>
+ <div class="row"><span class="lab">Max loan (by DSCR)</span><span class="val" id="loan_dscr">—</span></div>
+ <div class="row"><span class="lab">Loan proceeds</span><span class="val red" id="loan_final">—</span></div>
+ <div class="note" id="loan_note"></div>
+ </div>
+ </div>
+
+ </section>
+ <div class="foot">Estimates for screening only — not a lending commitment or investment advice. Verify with your lender and accountant.</div>
+</main>
+
+<script>
+const $=id=>document.getElementById(id);
+const drawer=$('drawer'),scrim=$('scrim');
+$('burger').onclick=()=>{drawer.classList.add('open');scrim.classList.add('open')};
+const close=()=>{drawer.classList.remove('open');scrim.classList.remove('open')};
+$('dx').onclick=close;scrim.onclick=close;document.addEventListener('keydown',e=>{if(e.key==='Escape')close()});
+
+const num=el=>{const v=parseFloat(el.value);return isFinite(v)?v:0;};
+const vals=id=>{const o={};document.querySelectorAll('#'+id+' input[data-k]').forEach(el=>o[el.dataset.k]=num(el));return o;};
+const usd=n=>!isFinite(n)?'—':n>=1e6?('$'+(n/1e6).toFixed(2)+'M'):n>=1e3?('$'+Math.round(n).toLocaleString()):('$'+Math.round(n));
+const usd0=n=>!isFinite(n)?'—':'$'+Math.round(n).toLocaleString();
+
+function calc(){
+ // Cap rate
+ const c=vals('cap'); const cap=c.price>0?c.noi/c.price*100:NaN;
+ $('cap_out').textContent=isFinite(cap)?cap.toFixed(2)+'%':'—';
+ $('cap_note').innerHTML=isFinite(cap)?`At this cap rate, every <b>$100</b> of purchase price buys <b>$${cap.toFixed(2)}</b> of annual NOI.`:'';
+ // Per unit / SF
+ const u=vals('unit');
+ $('unit_perunit').textContent=u.units>0?usd0(u.price/u.units):'—';
+ $('unit_persf').textContent=u.sf>0?'$'+(u.price/u.sf).toFixed(0)+'/SF':'—';
+ // Loan sizing
+ const l=vals('loan');
+ const byLtv=l.price*(l.ltv/100);
+ const i=(l.rate/100)/12, n=l.amort*12;
+ const maxAnnualDS=l.dscr>0?l.noi/l.dscr:0; // debt service the NOI can cover at min DSCR
+ const pmt=maxAnnualDS/12;
+ const byDscr=(i>0&&n>0)?pmt*(1-Math.pow(1+i,-n))/i:(n>0?pmt*n:0); // PV of that payment stream
+ const finalLoan=Math.min(byLtv,byDscr);
+ $('loan_ltv').textContent=usd(byLtv);
+ $('loan_dscr').textContent=usd(byDscr);
+ $('loan_final').textContent=usd(finalLoan);
+ const bind=byDscr<byLtv?'DSCR':'LTV';
+ const down=l.price-finalLoan;
+ $('loan_note').innerHTML=isFinite(finalLoan)&&finalLoan>0?`Constrained by <b>${bind}</b>. Implied equity/down payment: <b>${usd(down)}</b> (${(down/l.price*100).toFixed(0)}%).`:'';
+}
+document.querySelectorAll('input[data-k]').forEach(el=>el.addEventListener('input',calc));
+calc();
+</script>
+</body></html>
diff --git a/tools/specs/tools.json b/tools/specs/tools.json
new file mode 100644
index 00000000..85063aaa
--- /dev/null
+++ b/tools/specs/tools.json
@@ -0,0 +1,23 @@
+{
+ "slug": "toolkit",
+ "title": "CRE Toolkit",
+ "subtitle": "Underwrite a deal in seconds",
+ "voice": "Samantha",
+ "scenes": [
+ {
+ "url": "/tools",
+ "say": "The RENTV CRE Toolkit lets brokers and investors underwrite a deal in seconds. Cap rate, price per unit, and price per square foot — all computed live as you type.",
+ "caption": "Cap rate, price per unit & per SF — live as you type"
+ },
+ {
+ "url": "/tools#loan",
+ "say": "Size a loan the way a lender does. It shows your maximum proceeds by L-T-V and by D-S-C-R, and flags which one actually constrains the deal.",
+ "caption": "Loan sizing — LTV vs. DSCR, and which one binds"
+ },
+ {
+ "url": "/tools",
+ "say": "And it's completely private. Everything runs right in your browser — no numbers ever leave the page.",
+ "caption": "100% private — nothing leaves your browser"
+ }
+ ]
+}
← 24a3fedc pulse: CRE Market Pulse dashboard (/pulse + /api/pulse) — KP
·
back to Rentv
·
watchlist: personalized Deal Watchlist (/watchlist) — pin pr 04e9cb85 →