Skip to content

  • Call 602-354-4660 Today
  • Login
    • Owners
    • Tenants
Capstone Realty Professionals

Capstone Realty Professionals

Menu
  • For Sale
    • Buying
    • Selling
  • For Rent
  • Property Management
    • Lease Only
  • About Us
    • Meet The Team
    • Capstone Contributes
    • Preferred Partners
  • Connect With Us
CONNECT WITH US

  • 1425 E. McDowell Rd., Phoenix, AZ
    85006
  • 602.354.4660

Quick Links

  • Services
  • Join Our Team
  • Featured Listings
  • Off Market/Exclusive Deals
  • Testimonials
  • Resources
  • Blog
  • Market Stats

Newsletter Signup

Name

© 2026 Capstone Realty Professionals – All rights reserved

Privacy Policy | Terms of Use | Sitemap

document.addEventListener('DOMContentLoaded', function() { var lastData = null; function v(id) { var el = document.getElementById(id); return el ? (parseFloat(el.value) || 0) : 0; } function vs(id) { var el = document.getElementById(id); return el ? (el.value || '') : ''; } function fmt(n, dec) { dec=dec||0; if(isNaN(n)||!isFinite(n)) return '—'; return n.toLocaleString('en-US',{minimumFractionDigits:dec,maximumFractionDigits:dec}); } function fmtPct(n) { return (isNaN(n)||!isFinite(n)) ? '—' : n.toFixed(1)+'%'; } function fmtD(n) { if(isNaN(n)||!isFinite(n)) return '—'; return (n<0?'-$':'$')+fmt(Math.abs(Math.round(n))); } function cfTdClass(n){ return n>=200?'td-good':n>=0?'td-warn':'td-bad'; } // ── IRR: multi-seed Newton's method ── function calcIRR(cashFlows) { var TOLERANCE = 1e-7, MAX_ITER = 150; function npvAt(rate) { var n = 0; for (var t = 0; t < cashFlows.length; t++) n += cashFlows[t] / Math.pow(1 + rate, t); return n; } function dnpvAt(rate) { var d = 0; for (var t = 1; t < cashFlows.length; t++) d += -t * cashFlows[t] / Math.pow(1 + rate, t + 1); return d; } function tryNewton(seed) { var rate = seed; for (var i = 0; i < MAX_ITER; i++) { var n = npvAt(rate), d = dnpvAt(rate); if (Math.abs(d) < 1e-14) return null; var next = rate - n / d; if (next < -0.9999) next = -0.9999; if (Math.abs(next - rate) < TOLERANCE) return isFinite(next) ? next : null; rate = next; } return null; } var seeds = [0.10, 0.0, -0.10, 0.25, 0.50, -0.30, 0.01]; var best = null; for (var s = 0; s < seeds.length; s++) { var r = tryNewton(seeds[s]); if (r !== null && isFinite(r) && Math.abs(npvAt(r)) < 1.0) { if (best === null || Math.abs(r - 0.08) < Math.abs(best - 0.08)) best = r; } } return best !== null ? best * 100 : NaN; } // ── Core year calculator ── function calcYear(yr, params) { var rent = params.baseRent * Math.pow(1 + params.rentGrowth, yr - 1); var otherInc = params.baseOther * Math.pow(1 + params.rentGrowth, yr - 1); var grossInc = rent + otherInc; var effInc = grossInc * (1 - params.vacancy); var taxes = params.baseTaxes * Math.pow(1.02, yr - 1); var insurance = params.baseInsurance * Math.pow(1.03, yr - 1); var hoa = params.baseHoa * Math.pow(1.02, yr - 1); var mgmtCost = effInc * params.mgmtPct; var capexCost = effInc * params.capexPct; var maintCost = effInc * params.maintPct; var otherCosts= params.baseOtherCosts * Math.pow(1.02, yr - 1); var totalOpEx = taxes + insurance + hoa + mgmtCost + capexCost + maintCost + otherCosts; var noi = effInc - totalOpEx; var cashFlow = noi - params.pi; return { rent:rent, grossInc:grossInc, effInc:effInc, taxes:taxes, insurance:insurance, hoa:hoa, mgmtCost:mgmtCost, capexCost:capexCost, maintCost:maintCost, otherCosts:otherCosts, totalOpEx:totalOpEx, noi:noi, cashFlow:cashFlow }; } // ── Analyze ── function praAnalyze() { var price = v('pra-price'); var rent = v('pra-rent'); var taxes = v('pra-taxes'); var ins = v('pra-insurance'); var missing = []; if (!price) missing.push('Purchase price'); if (!rent) missing.push('Monthly gross rent'); if (!taxes) missing.push('Property taxes'); if (!ins) missing.push('Insurance'); if (missing.length) { alert('Please fill in the required fields:\n\n• ' + missing.join('\n• ')); return; } var rawDown = v('pra-downPct'); var rawRate = v('pra-rate'); var termMo = v('pra-term') * 12; var downPct = rawDown / 100; var rentRatio = (rent / price) * 100; if (rawDown < 3.5 && rawDown > 0) { alert('Warning: A down payment below 3.5% is below minimum conventional lending thresholds. Results may not reflect a financeable deal. Proceeding with entered value.'); } if (rentRatio > 1.5) { alert('Warning: Rent-to-price ratio of ' + rentRatio.toFixed(2) + '% is above 1.5% — unusually high for Phoenix SFR. Please verify rent and price inputs before proceeding.'); } var rehab = v('pra-rehab'); var downAmt = price * downPct; var loanAmt = price * (1 - downPct); var closing = v('pra-closing'); var totalCash = downAmt + closing + rehab; var pi; var mRate; if (rawRate === 0) { pi = loanAmt / termMo; mRate = 0; } else { mRate = rawRate / 100 / 12; pi = loanAmt * (mRate * Math.pow(1 + mRate, termMo)) / (Math.pow(1 + mRate, termMo) - 1); } var yearBuilt = v('pra-yearBuilt') || 2000; var holdYears = v('pra-holdPeriod'); var appreciation = v('pra-appreciation') / 100; var sellCostPct = v('pra-sellCost') / 100; var ltcgRate = v('pra-taxRate') / 100; var RECAPTURE_RATE = 0.25; var landPct = 0.20; var deprBasis = price * (1 - landPct); var annualDepr = deprBasis / 27.5; var params = { baseRent: rent, baseOther: v('pra-otherIncome'), vacancy: v('pra-vacancy') / 100, rentGrowth: v('pra-rentGrowth') / 100, baseTaxes: taxes, baseInsurance: ins, baseHoa: v('pra-hoa'), mgmtPct: v('pra-mgmt') / 100, capexPct: v('pra-capex') / 100, maintPct: v('pra-maint') / 100, baseOtherCosts: v('pra-other-costs'), pi: pi, }; var yr1 = calcYear(1, params); var capRate = (yr1.noi * 12) / price * 100; var coC = totalCash > 0 ? (yr1.cashFlow * 12) / totalCash * 100 : 0; var dscr = pi > 0 ? yr1.noi / pi : Infinity; var grm = rent > 0 ? price / (rent * 12) : 0; var balance = loanAmt; var projRows = [], cumCF = 0, cumDepr = 0; var irrCFpre = [-totalCash]; var irrCFpost = [-totalCash]; for (var yr = 1; yr <= holdYears; yr++) { var yd = calcYear(yr, params); var yearCF = yd.cashFlow * 12; cumCF += yearCF; cumDepr += annualDepr; var yearEndBal = balance; for (var m = 0; m < 12; m++) { if (mRate === 0) { yearEndBal -= pi; } else { yearEndBal -= (pi - yearEndBal * mRate); } } var principalPaid = balance - Math.max(yearEndBal, 0); balance = Math.max(yearEndBal, 0); var propVal = price * Math.pow(1 + appreciation, yr); var equity = propVal - balance; var grossProc = propVal * (1 - sellCostPct); var netProc = grossProc - balance; var adjBasis = price + closing - cumDepr; var totalGain = Math.max(grossProc - adjBasis, 0); var recapPortion = Math.min(cumDepr, totalGain); var ltcgPortion = Math.max(totalGain - recapPortion, 0); var recaptureTax = recapPortion * RECAPTURE_RATE; var ltcgTax = ltcgPortion * ltcgRate; var totalTaxOwed = recaptureTax + ltcgTax; var netAfterTax = netProc - totalTaxOwed; var totalReturnPreTax = cumCF + netProc; var totalReturnAfterTax = cumCF + netAfterTax; var roiPreTax = totalCash > 0 ? (totalReturnPreTax / totalCash) * 100 : 0; var roiAfterTax = totalCash > 0 ? (totalReturnAfterTax / totalCash) * 100 : 0; var annROIpre = (Math.pow(1 + roiPreTax / 100, 1 / yr) - 1) * 100; var annROIpost = (Math.pow(1 + roiAfterTax / 100, 1 / yr) - 1) * 100; irrCFpre.push(yearCF + (yr === holdYears ? netProc : 0)); irrCFpost.push(yearCF + (yr === holdYears ? netAfterTax : 0)); projRows.push({ yr:yr, rent:yd.rent, grossInc:yd.grossInc, effInc:yd.effInc, totalOpEx:yd.totalOpEx, noi:yd.noi, cashFlow:yd.cashFlow, yearCF:yearCF, cumCF:cumCF, principalPaid:principalPaid, balance:balance, propVal:propVal, equity:equity, netProc:netProc, netAfterTax:netAfterTax, recaptureTax:recaptureTax, ltcgTax:ltcgTax, totalTaxOwed:totalTaxOwed, ltcgPortion:ltcgPortion, recapturePortion:recapPortion, totalReturnPreTax:totalReturnPreTax, totalReturnAfterTax:totalReturnAfterTax, roiPreTax:roiPreTax, roiAfterTax:roiAfterTax, annROIpre:annROIpre, annROIpost:annROIpost, cumDepr:cumDepr, adjBasis:adjBasis }); } var irrPre = calcIRR(irrCFpre); var irrPost = calcIRR(irrCFpost); var stressVacancies = [0, 0.05, 0.10]; var stressResults = stressVacancies.map(function(vac) { var sp = {}; for (var k in params) sp[k] = params[k]; sp.vacancy = vac; return calcYear(1, sp); }); lastData = { address:vs('pra-address'), price:price, downAmt:downAmt, loanAmt:loanAmt, closing:closing, rehab:rehab, totalCash:totalCash, pi:pi, yearBuilt:yearBuilt, yr1:yr1, capRate:capRate, coC:coC, dscr:dscr, grm:grm, annualDepr:annualDepr, deprBasis:deprBasis, landPct:landPct, holdYears:holdYears, projRows:projRows, irrPre:irrPre, irrPost:irrPost, stressResults:stressResults, params:params, ltcgRate:ltcgRate, mRate:mRate, sqft:v('pra-sqft') }; praRender(lastData); } // ── Render ── function praRender(d) { var res = document.getElementById('pra-results'); res.style.display = 'block'; res.scrollIntoView({behavior:'smooth', block:'start'}); document.getElementById('pra-address-line').textContent = d.address || ''; var flags=[], score=0; var cf=d.yr1.cashFlow, cr=d.capRate, co=d.coC, ds=d.dscr, gr=d.grm; var ageYrs = new Date().getFullYear() - d.yearBuilt; var negativeCF = cf < 0; if(cf>=200){score+=2;flags.push({t:'good',msg:'Positive cash flow: '+fmtD(cf)+'/mo in Year 1 — covers expenses with margin'});} else if(cf>=0){score+=1;flags.push({t:'warn',msg:'Breakeven cash flow: '+fmtD(cf)+'/mo in Year 1 — no buffer for surprises'});} else{score-=2;flags.push({t:'bad',msg:'Negative cash flow: '+fmtD(cf)+'/mo in Year 1 — you are feeding this property every single month'});} if(cr>=6){score+=2;flags.push({t:'good',msg:'Cap rate '+fmtPct(cr)+' — strong income return relative to price'});} else if(cr>=4){score+=1;flags.push({t:'warn',msg:'Cap rate '+fmtPct(cr)+' — acceptable, but thin margin if expenses rise'});} else{flags.push({t:'bad',msg:'Cap rate '+fmtPct(cr)+' — appreciation-dependent play; income alone does not carry this deal'});} if(!isFinite(ds)){flags.push({t:'good',msg:'No debt — DSCR not applicable (all-cash purchase)'});score+=1;} else if(ds>=1.25){score+=1;flags.push({t:'good',msg:'DSCR '+ds.toFixed(2)+' — NOI comfortably covers debt service'});} else if(ds>=1.0){flags.push({t:'warn',msg:'DSCR '+ds.toFixed(2)+' — income barely covers debt; any vacancy could flip this negative'});} else{score-=1;flags.push({t:'bad',msg:'DSCR '+ds.toFixed(2)+' — NOI does not cover debt service; lender risk flag'});} if(co>=8){score+=1;flags.push({t:'good',msg:'Cash-on-cash '+fmtPct(co)+' — strong return on invested capital'});} else if(co>=4){flags.push({t:'warn',msg:'Cash-on-cash '+fmtPct(co)+' — modest; compare against alternative capital deployments'});} else{score-=1;flags.push({t:'bad',msg:'Cash-on-cash '+fmtPct(co)+' — weak Year 1 return on invested capital'});} if(gr<=14){score+=1;flags.push({t:'good',msg:'GRM '+gr.toFixed(1)+' — price is reasonable relative to rental income for Phoenix SFR'});} else if(gr<=18){flags.push({t:'warn',msg:'GRM '+gr.toFixed(1)+' — in the upper range for Phoenix; rent growth needed to justify price'});} else{flags.push({t:'bad',msg:'GRM '+gr.toFixed(1)+' — price is expensive relative to rent; appreciation must do heavy lifting'});} if(!isNaN(d.irrPre)){ if(d.irrPre>=12){score+=1;flags.push({t:'good',msg:'Pre-tax IRR '+fmtPct(d.irrPre)+' — strong risk-adjusted return over hold period'});} else if(d.irrPre>=7){flags.push({t:'warn',msg:'Pre-tax IRR '+fmtPct(d.irrPre)+' — acceptable but below typical investor hurdle of 10–12%'});} else{score-=1;flags.push({t:'bad',msg:'Pre-tax IRR '+fmtPct(d.irrPre)+' — low total return; capital likely better deployed elsewhere'});} } var lastProjRow = d.projRows[d.projRows.length-1]; if(lastProjRow && lastProjRow.netProc < 0){ score-=2; flags.push({t:'bad',msg:'Net sale proceeds are negative at exit ('+fmtD(lastProjRow.netProc)+') — loan balance exceeds sale price after selling costs. You cannot exit this deal without bringing cash to the table.'}); } if(ageYrs>=20){flags.push({t:'warn',msg:'Property is '+ageYrs+' years old — HVAC, roof, and water heater replacement cycles are near-term risks; verify CapEx reserve is adequate'});} else if(ageYrs>=10){flags.push({t:'warn',msg:'Property is '+ageYrs+' years old — mid-cycle on major systems; review CapEx reserve adequacy'});} else if(d.yearBuilt>1990){flags.push({t:'good',msg:'Property is '+ageYrs+' years old — newer build reduces near-term CapEx risk'});} var verdict, vClass, vSub; var lastPR2 = d.projRows[d.projRows.length-1]; var negProc = lastPR2 && lastPR2.netProc < 0; if(negProc){ verdict='No-Go'; vClass='pra-verdict-no'; vSub='Loan balance exceeds sale price at exit ('+fmtD(lastPR2.netProc)+'). You cannot sell this property at the end of your hold period without bringing cash to the closing table.'; } else if(negativeCF){ verdict='Proceed with Caution'; vClass='pra-verdict-maybe'; vSub='Negative cash flow means you pay to own this property every month. This is a pure appreciation bet — only acceptable with a very strong Phoenix market conviction and reserves to sustain losses.'; } else if(score>=7){verdict='Strong Go';vClass='pra-verdict-go';vSub='Multiple metrics align. This property earns its place in a long-term portfolio.';} else if(score>=5){verdict='Go with Eyes Open';vClass='pra-verdict-go';vSub='Solid fundamentals with areas to monitor. Negotiate or improve terms before closing.';} else if(score>=2){verdict='Proceed with Caution';vClass='pra-verdict-maybe';vSub='This deal leans on appreciation more than income. Acceptable only if your Phoenix market thesis is strong.';} else{verdict='No-Go';vClass='pra-verdict-no';vSub='Numbers do not support a long-term hold at this price and terms. Walk or renegotiate significantly.';} document.getElementById('pra-verdict').className='pra-verdict '+vClass; document.getElementById('pra-verdict-label').textContent=verdict; document.getElementById('pra-verdict-sub').textContent=vSub; var lastRow = d.projRows[d.projRows.length-1]; var metrics=[ {label:'Year 1 cash flow/mo', value:fmtD(cf), flag:cf>=200?'good':cf>=0?'warn':'bad'}, {label:'Cash-on-cash Yr 1', value:fmtPct(co), flag:co>=8?'good':co>=4?'warn':'bad'}, {label:'Cap rate', value:fmtPct(cr), flag:cr>=6?'good':cr>=4?'warn':'bad'}, {label:'DSCR', value:isFinite(ds)?ds.toFixed(2):'N/A', flag:isFinite(ds)&&ds>=1.25?'good':isFinite(ds)&&ds>=1.0?'warn':'bad'}, {label:'IRR (pre-tax)', value:fmtPct(d.irrPre), flag:d.irrPre>=12?'good':d.irrPre>=7?'warn':'bad'}, {label:'IRR (after-tax)', value:fmtPct(d.irrPost), flag:d.irrPost>=8?'good':d.irrPost>=4?'warn':'bad'}, {label:'Total cash in', value:fmtD(d.totalCash), flag:'neut'}, {label:'Equity at exit', value:fmtD(lastRow.equity), flag:'neut'}, ]; document.getElementById('pra-metrics').innerHTML=metrics.map(function(m){ return '
'+m.label+'
'+m.value+'
'; }).join(''); document.getElementById('pra-flags').innerHTML=flags.map(function(f){ return '
'+f.msg+'
'; }).join(''); var stressLabels=['0% vacancy (best case)','5% vacancy (base case)','10% vacancy (stress)']; document.getElementById('pra-stress-grid').innerHTML=d.stressResults.map(function(s,i){ var cfClass=s.cashFlow>=200?'clr-good':s.cashFlow>=0?'clr-warn':'clr-bad'; var dscr2 = d.pi>0 ? s.noi/d.pi : Infinity; return '
'+ '
'+stressLabels[i]+'
'+ '
Monthly CF'+fmtD(s.cashFlow)+'
'+ '
Annual CF'+fmtD(s.cashFlow*12)+'
'+ '
NOI/mo'+fmtD(s.noi)+'
'+ '
DSCR'+(isFinite(dscr2)?dscr2.toFixed(2):'N/A')+'
'+ '
'; }).join(''); document.getElementById('pra-depr-note').innerHTML= 'Depreciation note — Estimated annual depreciation: '+fmtD(d.annualDepr)+' '+ '(assumes '+Math.round(d.landPct*100)+'% land value; '+Math.round((1-d.landPct)*100)+'% improvement basis ÷ 27.5 years). '+ 'Land allocation is an assumption — actual basis depends on your purchase allocation. '+ 'At exit, accumulated depreciation is subject to 25% recapture tax, separate from long-term capital gains. '+ 'Consult your CPA on actual depreciation basis, cost segregation opportunities, and recapture planning.'; document.getElementById('pra-tab-cashflow').innerHTML='
'+ ''+ d.projRows.map(function(r){ return ''; }).join('')+'
YearMonthly rentEff. incomeTotal expensesNOI/moMonthly CFAnnual CFCumulative CF
'+r.yr+''+fmtD(r.rent)+''+fmtD(r.effInc)+''+fmtD(r.totalOpEx)+''+fmtD(r.noi)+''+fmtD(r.cashFlow)+''+fmtD(r.yearCF)+''+fmtD(r.cumCF)+'
'; document.getElementById('pra-tab-equity').innerHTML='
'+ ''+ d.projRows.map(function(r){ var gainAboveDown = r.equity - d.downAmt; return ''; }).join('')+'
YearEst. valueLoan balancePrincipal paid this yrTotal equityGain above down pmt
'+r.yr+''+fmtD(r.propVal)+''+fmtD(r.balance)+''+fmtD(r.principalPaid)+''+fmtD(r.equity)+''+fmtD(gainAboveDown)+'
'; document.getElementById('pra-tab-returns').innerHTML='
'+ ''+ d.projRows.map(function(r){ return ''; }).join('')+'
YearCum. cash flowNet sale proceedsTotal return (pre-tax)Total ROISimple ann. ROI*
'+r.yr+''+fmtD(r.cumCF)+''+fmtD(r.netProc)+(r.netProc<0?' ⚠':'')+''+fmtD(r.totalReturnPreTax)+''+fmtPct(r.roiPreTax)+''+fmtPct(r.annROIpre)+'
'+ '

* Simple annualized ROI does not account for time value of money. True IRR (shown in Key Metrics above) is the accurate measure. ⚠ = negative net proceeds.

'; document.getElementById('pra-tab-aftertax').innerHTML='
'+ ''+ d.projRows.map(function(r){ return ''; }).join('')+'
YearAdj. basisRecap. tax (25%)LTCG tax ('+fmtPct(d.ltcgRate*100)+')Total taxNet after-tax proceedsAfter-tax IRR
'+r.yr+''+fmtD(r.adjBasis)+''+fmtD(r.recaptureTax)+''+fmtD(r.ltcgTax)+''+fmtD(r.totalTaxOwed)+''+fmtD(r.netAfterTax)+''+(r.yr===d.holdYears?fmtPct(d.irrPost):'—')+'
'+ '

Recapture tax applies to accumulated depreciation at 25%. LTCG tax applies to remaining gain. Consult your CPA — a 1031 exchange eliminates both at exit.

'; } // ── Tab switching ── function praSwitchTab(name) { document.querySelectorAll('.pra-tab').forEach(function(t){ t.classList.remove('active'); }); document.querySelectorAll('.pra-tab-content').forEach(function(t){ t.classList.remove('active'); }); var btn = document.getElementById('pra-tab-btn-' + name); if (btn) btn.classList.add('active'); var content = document.getElementById('pra-tab-' + name); if (content) content.classList.add('active'); } // ── Reset ── function praReset() { document.getElementById('pra-results').style.display = 'none'; lastData = null; window.scrollTo({top: 0, behavior: 'smooth'}); } // ── PDF Export ── function praExportPDF() { if (!lastData) return; var d = lastData; var jsPDF = window.jspdf ? window.jspdf.jsPDF : null; if (!jsPDF) { alert('PDF library not loaded. Please refresh the page and try again.'); return; } var doc = new jsPDF({unit:'pt', format:'letter'}); var pw = doc.internal.pageSize.getWidth(); var ph = doc.internal.pageSize.getHeight(); var mg = 48, cw = pw - mg*2, y = 0; function newPage() { doc.addPage(); y = 0; doc.setFillColor('#1a2332'); doc.rect(0,0,pw,28,'F'); doc.setFont('helvetica','bold'); doc.setFontSize(7); doc.setTextColor('#c8a96e'); doc.text('CAPSTONE REALTY PROFESSIONALS | PROPERTY INVESTMENT ANALYSIS',mg,18); y = 44; } function checkPage(needed) { if (y + needed > ph - 40) newPage(); } function hRule(w, color) { doc.setDrawColor(color||'#1a2332'); doc.setLineWidth(w||1); doc.line(mg,y,pw-mg,y); y+=(w>=1.5?12:8); } function sTitle(txt) { checkPage(28); y+=8; doc.setFont('helvetica','bold'); doc.setFontSize(7); doc.setTextColor('#c8a96e'); doc.text(txt.toUpperCase(),mg,y); y+=4; hRule(0.5,'#e2e0db'); } function kv(lbl, val, valColor) { checkPage(16); doc.setFont('helvetica','normal'); doc.setFontSize(9); doc.setTextColor('#4a5568'); doc.text(lbl,mg,y); doc.setFont('helvetica','bold'); doc.setTextColor(valColor||'#1a2332'); doc.text(String(val),pw-mg,y,{align:'right'}); y+=16; } doc.setFillColor('#1a2332'); doc.rect(0,0,pw,60,'F'); doc.setFont('helvetica','bold'); doc.setFontSize(18); doc.setTextColor('#ffffff'); doc.text('Property Investment Analysis',mg,32); doc.setFont('helvetica','normal'); doc.setFontSize(8); doc.setTextColor('#c8a96e'); doc.text('Capstone Realty Professionals | capstonerealtypros.com | 602-354-4660',mg,48); y = 76; doc.setFont('helvetica','bold'); doc.setFontSize(12); doc.setTextColor('#1a2332'); doc.text(d.address||'Address not provided',mg,y); doc.setFont('helvetica','normal'); doc.setFontSize(8); doc.setTextColor('#4a5568'); doc.text('Generated '+new Date().toLocaleDateString('en-US',{year:'numeric',month:'long',day:'numeric'}),pw-mg,y,{align:'right'}); y+=8; hRule(2); var cf=d.yr1.cashFlow, cr=d.capRate, co=d.coC, ds=d.dscr, sc=0; var negCF=cf<0; if(cf>=200)sc+=2;else if(cf>=0)sc+=1;else sc-=2; if(cr>=6)sc+=2;else if(cr>=4)sc+=1; if(isFinite(ds)&&ds>=1.25)sc+=1;else if(isFinite(ds)&&ds<1)sc-=1; if(co>=8)sc+=1;else if(co<4)sc-=1; if(d.grm<=14)sc+=1; if(!isNaN(d.irrPre)){if(d.irrPre>=12)sc+=1;else if(d.irrPre<7)sc-=1;} var lastPR=d.projRows[d.projRows.length-1]; if(lastPR&&lastPR.netProc<0)sc-=2; var vLabel,vColor,vBg,vSub; if(lastPR&&lastPR.netProc<0){vLabel='NO-GO';vColor='#8a2020';vBg='#fdf0f0';vSub='Loan balance exceeds sale price at exit — you cannot sell without bringing cash to closing.';} else if(negCF){vLabel='PROCEED WITH CAUTION';vColor='#8a5e1a';vBg='#fdf5e6';vSub='Negative cash flow — you pay to own this property monthly. Pure appreciation bet.';} else if(sc>=7){vLabel='STRONG GO';vColor='#2d6a2d';vBg='#edf6ed';vSub='Multiple metrics align. This property earns its place in a long-term portfolio.';} else if(sc>=5){vLabel='GO WITH EYES OPEN';vColor='#2d6a2d';vBg='#edf6ed';vSub='Solid fundamentals with areas to monitor. Negotiate terms before closing.';} else if(sc>=2){vLabel='PROCEED WITH CAUTION';vColor='#8a5e1a';vBg='#fdf5e6';vSub='Leans on appreciation more than income. Acceptable only with strong market thesis.';} else{vLabel='NO-GO';vColor='#8a2020';vBg='#fdf0f0';vSub='Numbers do not support a long-term hold. Walk or renegotiate significantly.';} doc.setFillColor(vBg); doc.roundedRect(mg,y,cw,54,3,3,'F'); doc.setDrawColor(vColor); doc.setLineWidth(4); doc.line(mg,y,mg,y+54); y+=16; doc.setFont('helvetica','bold'); doc.setFontSize(13); doc.setTextColor(vColor); doc.text(vLabel,mg+12,y); y+=14; doc.setFont('helvetica','normal'); doc.setFontSize(8.5); doc.setTextColor(vColor); doc.text(vSub,mg+12,y,{maxWidth:cw-20}); y+=30; sTitle('Key metrics'); var met=[ {l:'Year 1 monthly cash flow',v:fmtD(cf),c:cf>=200?'#2d6a2d':cf>=0?'#8a5e1a':'#8a2020'}, {l:'Cash-on-cash (Year 1)',v:fmtPct(co),c:co>=8?'#2d6a2d':co>=4?'#8a5e1a':'#8a2020'}, {l:'Cap rate',v:fmtPct(cr),c:cr>=6?'#2d6a2d':cr>=4?'#8a5e1a':'#8a2020'}, {l:'DSCR',v:isFinite(ds)?ds.toFixed(2):'N/A',c:isFinite(ds)&&ds>=1.25?'#2d6a2d':isFinite(ds)&&ds>=1.0?'#8a5e1a':'#8a2020'}, {l:'IRR (pre-tax)',v:fmtPct(d.irrPre),c:d.irrPre>=10?'#2d6a2d':d.irrPre>=6?'#8a5e1a':'#8a2020'}, {l:'IRR (after-tax)',v:fmtPct(d.irrPost),c:d.irrPost>=8?'#2d6a2d':d.irrPost>=4?'#8a5e1a':'#8a2020'}, ]; var mw=cw/3-6; met.forEach(function(m,i){ var col=i%3, row=Math.floor(i/3); var mx=mg+col*(mw+9), my=y+row*46; doc.setFillColor('#f7f7f5'); doc.roundedRect(mx,my,mw,38,2,2,'F'); doc.setFont('helvetica','bold'); doc.setFontSize(6); doc.setTextColor('#4a5568'); doc.text(m.l.toUpperCase(),mx+8,my+12); doc.setFont('helvetica','bold'); doc.setFontSize(12); doc.setTextColor(m.c); doc.text(m.v,mx+8,my+28); }); y+=Math.ceil(met.length/3)*46+14; sTitle('Year 1 income & expense breakdown'); kv('Down payment',fmtD(d.downAmt)); kv('Closing costs',fmtD(d.closing)); if(d.rehab>0) kv('Rehab / initial repairs',fmtD(d.rehab)); kv('Total cash invested',fmtD(d.totalCash)); kv('Gross monthly rent',fmtD(d.yr1.rent)); kv('Effective income after vacancy',fmtD(d.yr1.effInc)); kv('Property taxes',fmtD(d.yr1.taxes)); kv('Insurance',fmtD(d.yr1.insurance)); kv('HOA',fmtD(d.yr1.hoa)); kv('Property management',fmtD(d.yr1.mgmtCost)); kv('CapEx reserve',fmtD(d.yr1.capexCost)); kv('Maintenance reserve',fmtD(d.yr1.maintCost)); kv('Other carrying costs',fmtD(d.yr1.otherCosts)); kv('Net operating income (monthly)',fmtD(d.yr1.noi)); kv('Mortgage P&I',fmtD(d.pi)); kv('Monthly cash flow Year 1',fmtD(cf),cf>=0?'#2d6a2d':'#8a2020'); kv('Annual depreciation (est. — '+Math.round(d.landPct*100)+'% land assumed)',fmtD(d.annualDepr),'#8a5e1a'); sTitle('Vacancy stress test — Year 1'); var stressLbls=['0% vacancy','5% vacancy','10% vacancy']; d.stressResults.forEach(function(s,i){ kv(stressLbls[i]+' — monthly CF',fmtD(s.cashFlow),s.cashFlow>=200?'#2d6a2d':s.cashFlow>=0?'#8a5e1a':'#8a2020'); }); newPage(); sTitle(d.holdYears+'-year cash flow projection (rent & expenses inflated annually)'); var cfCols=['Yr','Monthly Rent','Eff. Income','Total Expenses','NOI/mo','Monthly CF','Annual CF','Cumulative CF']; var cfW=[20,76,76,82,68,72,72,88]; doc.setFillColor('#1a2332'); doc.rect(mg,y,cw,18,'F'); var hx2=mg+5; cfCols.forEach(function(c,i){doc.setFont('helvetica','bold');doc.setFontSize(6.5);doc.setTextColor('#ffffff');doc.text(c,hx2,y+12);hx2+=cfW[i];}); y+=18; d.projRows.forEach(function(r,ri){ checkPage(15); if(ri%2===0){doc.setFillColor('#f7f7f5');doc.rect(mg,y,cw,15,'F');} var rx=mg+5; [String(r.yr),fmtD(r.rent),fmtD(r.effInc),fmtD(r.totalOpEx),fmtD(r.noi),fmtD(r.cashFlow),fmtD(r.yearCF),fmtD(r.cumCF)].forEach(function(val,i){ var color=i===5||i===6?(r.cashFlow>=200?'#2d6a2d':r.cashFlow>=0?'#8a5e1a':'#8a2020'):'#1a2332'; doc.setFont('helvetica',i===5||i===6?'bold':'normal');doc.setFontSize(8);doc.setTextColor(color); doc.text(val,rx,y+10);rx+=cfW[i]; }); y+=15; }); y+=10; sTitle('After-tax exit analysis by year'); var retCols=['Yr','Net Proceeds','Recap. Tax','LTCG Tax','Total Tax','After-Tax Proceeds','After-Tax IRR']; var retW=[20,80,76,72,66,96,74]; doc.setFillColor('#1a2332'); doc.rect(mg,y,cw,18,'F'); var hx3=mg+5; retCols.forEach(function(c,i){doc.setFont('helvetica','bold');doc.setFontSize(6.5);doc.setTextColor('#ffffff');doc.text(c,hx3,y+12);hx3+=retW[i];}); y+=18; d.projRows.forEach(function(r,ri){ checkPage(15); if(ri%2===0){doc.setFillColor('#f7f7f5');doc.rect(mg,y,cw,15,'F');} var rx=mg+5; [String(r.yr),fmtD(r.netProc),fmtD(r.recaptureTax),fmtD(r.ltcgTax),fmtD(r.totalTaxOwed),fmtD(r.netAfterTax),r.yr===d.holdYears?fmtPct(d.irrPost):'—'].forEach(function(val,i){ doc.setFont('helvetica','normal');doc.setFontSize(8);doc.setTextColor('#1a2332'); doc.text(val,rx,y+10);rx+=retW[i]; }); y+=15; }); var fp=ph-36; doc.setDrawColor('#e2e0db');doc.setLineWidth(0.5);doc.line(mg,fp-8,pw-mg,fp-8); doc.setFont('helvetica','normal');doc.setFontSize(6.5);doc.setTextColor('#aaaaaa'); doc.text('For informational purposes only. Does not constitute financial, legal, or investment advice. Depreciation recapture taxed at 25%; LTCG rate as entered. Consult a licensed CPA.',mg,fp); doc.text('Capstone Realty Professionals | Licensed in Arizona | 602-354-4660 | capstonerealtypros.com',mg,fp+10); var fn=(d.address?d.address.replace(/[^a-zA-Z0-9]/g,'-').substring(0,40):'property')+'-capstone-analysis.pdf'; doc.save(fn); } // ── Bind all events via addEventListener — WordPress safe ── var analyzeBtn = document.getElementById('pra-analyze-btn'); if (analyzeBtn) analyzeBtn.addEventListener('click', praAnalyze); var pdfBtn = document.getElementById('pra-pdf-btn'); if (pdfBtn) pdfBtn.addEventListener('click', praExportPDF); var resetBtn = document.getElementById('pra-reset-btn'); if (resetBtn) resetBtn.addEventListener('click', praReset); document.querySelectorAll('.pra-tab').forEach(function(btn) { btn.addEventListener('click', function() { praSwitchTab(btn.getAttribute('data-tab')); }); }); }); // end DOMContentLoaded
We use cookies to enhance your browsing experience and analyze our traffic. By clicking "Accept", you consent to our use of cookies.