const { useState } = React; const formatCurrency = (val) => new Intl.NumberFormat("en-US", { style: "currency", currency: "USD", maximumFractionDigits: 0 }).format(val); function calculateMonthly(price, downPct, rate) { const principal = price * (1 - downPct / 100); if (principal <= 0) return 0; const monthlyRate = rate / 100 / 12; if (monthlyRate === 0) return principal / 360; return (principal * monthlyRate * Math.pow(1 + monthlyRate, 360)) / (Math.pow(1 + monthlyRate, 360) - 1); } function SliderInput({ label, value, onChange, min, max, step, format, suffix, unit }) { const [localText, setLocalText] = useState(null); const pct = ((value - min) / (max - min)) * 100; return (
30-year fixed rate
{formatCurrency(price * downPct / 100)} down ยท {formatCurrency(principal)} loan
Monthly Mortgage
{formatCurrency(monthly)}
Total Monthly
{formatCurrency(totalMonthly)}
Sensitivity Table
{tableIncludeFees ? "Total monthly" : "Mortgage only"} at {downPct}% down
| Price \ Rate | {rates.map((r) => ({r % 0.25 === 0 ? r.toFixed(2) : r.toFixed(3)}% | ))}
|---|---|
| {formatCurrency(p)} | {rates.map((r) => { const val = calculateMonthly(p, downPct, r) + (tableIncludeFees ? monthlyFees : 0); const highlight = isActivePrice(p) && isActiveRate(r); const rowHL = isActivePrice(p); const colHL = isActiveRate(r); return ({formatCurrency(Math.round(val))} | ); })}
Cash Required at Close
Estimated NYC condo closing costs
{(() => { const loanAmount = price * (1 - downPct / 100); const downPayment = price * downPct / 100; const mansionTax = (() => { if (price < 1000000) return 0; if (price < 2000000) return price * 0.01; if (price < 3000000) return price * 0.0125; if (price < 5000000) return price * 0.0150; if (price < 10000000) return price * 0.0225; if (price < 15000000) return price * 0.0325; if (price < 20000000) return price * 0.0350; if (price < 25000000) return price * 0.0375; return price * 0.039; })(); const mortgageRecordingTax = loanAmount >= 500000 ? loanAmount * 0.01925 : loanAmount * 0.018; const titleInsurance = price * 0.0045; const attorneyFees = 5000; const bankFees = 3500; const recordingFees = 750; const misc = 1500; const closingCostsTotal = mansionTax + mortgageRecordingTax + titleInsurance + attorneyFees + bankFees + recordingFees + misc; const totalCash = downPayment + closingCostsTotal; const rows = [ { label: "Mansion Tax", amount: mansionTax, note: `NY state, progressive scale` }, { label: "Mortgage Recording Tax", amount: mortgageRecordingTax, note: `1.925% of loan (over $500K)` }, { label: "Title Insurance", amount: titleInsurance, note: `~0.45% of purchase price` }, { label: "Buyer's Attorney", amount: attorneyFees, note: "Flat fee" }, { label: "Bank Fees & Appraisal", amount: bankFees, note: "Lender charges" }, { label: "Recording Fees", amount: recordingFees, note: "Deed & mortgage filing" }, { label: "Miscellaneous", amount: misc, note: "Inspections, adjustments" }, ]; return (| {row.label} | {row.note} | {formatCurrency(Math.round(row.amount))} |
| CLOSING COSTS SUBTOTAL | {formatCurrency(Math.round(closingCostsTotal))} | |
| Down Payment | {downPct}% of purchase price | {formatCurrency(Math.round(downPayment))} |
| TOTAL CASH AT CLOSE | {formatCurrency(Math.round(totalCash))} |