We have helped thousands of families all over the valley find their dream homes. We are direct and straight forward and we always keep our client’s best interest at the heart of our home search. We navigate the challenges of home buying so that our clients don’t have to. Capstone Realty has the experience and expertise to find you the best home at the best rate guaranteed.
Communication
Our communication with our clients is unmatched. Whether it is your first home buying experience or one of many, we will guide you through the entire process. From exit strategy to helping you find your dream home, we make the search easy, fun, and informative. Unlike other agencies in Phoenix, we make sure to communicate with you through each step of the process, always making sure your concerns are heard.
Education
Capstone Realtors make sure that each client is educated on their home buying options. It can be overwhelming talking about loans, taxes, and fees, trying to keep up with which neighborhoods are higher than others. Your realtor will never let you choose a home without knowing exactly what comes with it. Educating you on your options sets us apart from other real estate agencies in Phoenix.
HOW CAN CAPSTONE HELP YOU
Thank you for finding us and trusting us with your home-buying experience in Phoenix. Finding a trusted real estate agency in Phoenix when looking to purchase a home is crucial. Here at Capstone Realty, we put YOUR needs first. It isn’t just about looking at the homes for sale in Phoenix, it is about the entire home-buying experience. As the leading realty group in Phoenix, we strive to help our clients find the best homes in the best neighborhoods for the best rate! If this is your first time searching for homes for sale in Phoenix, Capstone Realty is the agency to help you. When working with Capstone Realty, you are working with a TEAM of real estate professionals with more than 25 years of experience helping clients buy, sell, manage and lease residential real estate. You have a full team of professionals that you can call on anytime, for anything. Think of it as your personal concierge when you buy or sell.
With a top-notch agent, a buyer’s agent commission is negotiated so that it is paid by the seller. Ideally, the seller agrees to a total commission with their listing agent which is then split between the listing and buyer’s agents. This commission is paid from the seller’s proceeds at closing, so the buyer doesn’t pay their agent directly. This common setup incentivizes agents to connect qualified buyers with properties, streamlining real estate transactions.
Navigating the real estate jungle without a buyer’s agent is like hiking without a map—you might reach your destination, but it could take longer, cost more, and be a lot more stressful! Here’s why teaming up with a buyer’s agent is a smart move:
Expert Navigator: Buyer’s agents know the ins and outs of the market. They can spot the hidden gems and steer you away from overpriced pitfalls. Their local knowledge and expertise are invaluable when it comes to finding the right home at the right price.
Fierce Negotiator: Negotiating a home purchase isn’t just about the price. It’s about closing costs, contingencies, repairs, and more. A buyer’s agent fights for your best interests, ensuring you get the best deal possible.
Paperwork Pro: Real estate contracts are no joke—they’re full of legal jargon and fine print. A buyer’s agent handles the paperwork maze, making sure all the i’s are dotted and t’s are crossed, so you can focus on imagining yourself in your new home.
Free for You: Here’s the kicker—most of the time, the seller pays the buyer’s agent commission, so you get all this expertise without any direct cost to you!
Working with a buyer’s agent makes the home-buying process smoother, faster, and less stressful. So, why go it alone? Partner with a pro who has your back from the first showing to the final signature!
Buying your first home is a thrilling adventure, but it’s easy to feel overwhelmed without a game plan. Here’s a fun, straightforward roadmap to get you from dreaming about your new place to holding those keys in your hand!
Check Your Financial Fitness: Start by checking your credit score and getting your finances in shape. Save for a down payment, and remember, the stronger your credit, the better your mortgage rates will be!
Get Pre-Approved: Before you start touring homes, get pre-approved for a mortgage. This not only shows you’re serious but also gives you a clear budget, so you don’t fall in love with a house outside your price range.
Team Up with a Buyer’s Agent: Your buyer’s agent is your home-buying MVP! They’ll help you navigate the market, find homes that meet your needs, and negotiate the best deal. Plus, their expertise comes at no direct cost to you—the seller usually covers their commission!
House Hunt Like a Pro: With your agent by your side, start touring homes. Make a list of must-haves and nice-to-haves, and keep an open mind—you might just find a hidden gem!
Make a Smart Offer: When you find “the one,” your agent will help you craft a competitive offer. They’ll guide you through contingencies, inspections, and all the fine print to ensure your interests are protected.
Seal the Deal: Once your offer is accepted, it’s time for inspections, appraisals, and finalizing your loan. Your agent will keep everything on track, so there are no last-minute surprises.
Closing Time!: At closing, you’ll sign the final documents, pay closing costs, and—voilà!—you’re a homeowner. Time to pop the champagne and celebrate your new digs!
Following these steps will make your first home-buying experience smooth, exciting, and successful. Ready to start the journey? Let’s find your dream home together!
At Capstone, we believe, 99% of our buyers should talk to a lender before they start looking for a house. First getting your finances in order and speaking to a lender before looking at houses will only help you as a buyer. Having a conversation with a lender will help you determine not only what you can afford but also, what you want to spend. Letting you look at homes without a proper pre-qualification can be one of the biggest mistakes an inexperienced Realtor can make as it can only hurt you, the buyer, in the long run. In addition, your Lender should be able to get information from your Realtor, to ensure that all elements of your purchase are seamless and that your agent knows how to best negotiate for you and what you need.
Whether buying or renting is best for you depends on your financial situation, long-term goals, and lifestyle preferences. If you crave stability, ownership, and investment potential, buying might be your ticket. But if flexibility, lower costs, and less responsibility sound better, renting could be the way to go.
Many of our first time home buyers have life-changing events that help them decide what is right for them. A couple of questions to ask yourself that may make the decision easier are:
1. Do you see yourself living in one place for the next 2-5 years? 2. Are you willing and able to coordinate and do you have the budget to pay for repairs on a home or are you more inclined to let a landlord cover it for you? 3. Do you love where you live or know of a place that you would love to live?
WHAT MATTERS TO YOU MATTERS TO US
When on the search for homes for sale in Phoenix, Capstone always has the client in mind. Capstone Realty utilizes multi-platform technology to ensure consistent exposure to the prospective buyer/seller pool. As residential resale and property management experts, we leverage our vast contact list and local knowledge to get the scoop on the latest properties and pocket listings to give our clients greater access than the competition.
Capstone Realty makes customer service a priority. We stay in constant communication with our clients, providing information on the latest opportunities and sharing new strategies to help our clients exceed their goals. Capstone Realty has carved it’s own path as to how team approach is best utilized in the real estate industry for the benefit of the client.
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 '
';
}).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='
* 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.
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