Download index.html from getreadystack/car-refinance-calculator: direct link, hf CLI and curl.
- Browser
- Download file 39.7 kB
-
https://huggingface.co/spaces/getreadystack/car-refinance-calculator/resolve/main/index.html
- Command line
-
hf download hf://spaces/getreadystack/car-refinance-calculator/index.html
-
curl -L -o index.html https://huggingface.co/spaces/getreadystack/car-refinance-calculator/resolve/main/index.html
39.7 kB
| <html lang="en"> | |
| <head> | |
| <meta charset="utf-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |
| <title>Car Refinance Calculator: Refinance or Keep the Loan? 17-Formula Verdict | ReadyStack</title> | |
| <meta name="description" content="Free car refinance calculator. New payment, monthly drop, break-even on fees, lifetime savings, what stretching the term really costs — and a straight verdict: refinance or keep the loan you have. Runs in your browser; your numbers are never sent anywhere."> | |
| <link rel="canonical" href="https://getreadystack.com/tools/car-refinance-calculator"> | |
| <meta property="og:title" content="Car refinance calculator — refinance, or keep the loan?"> | |
| <meta property="og:description" content="17 real formulas: new payment, break-even on the fees, the cost of a longer term, and a verdict that flips. Free, no signup, nothing leaves your browser."> | |
| <meta property="og:type" content="website"> | |
| <meta property="og:url" content="https://getreadystack.com/tools/car-refinance-calculator.html"> | |
| <meta name="twitter:card" content="summary"> | |
| <meta name="robots" content="index,follow,max-snippet:-1"> | |
| <style> | |
| :root{--bg:#0f1020;--card:#1a1c2e;--ink:#f4f5fb;--mut:#9aa0bf;--acc:#6c5ce7;--acc2:#00d2a8;--warn:#ffb454;--bad:#ff7a7a;--line:#2a2d44} | |
| *{box-sizing:border-box;margin:0;padding:0} | |
| body{background:linear-gradient(160deg,#0f1020,#161a35);color:var(--ink);font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;line-height:1.6;min-height:100vh;padding:18px 14px 34px} | |
| .wrap{width:100%;max-width:660px;margin:0 auto} | |
| .card{background:var(--card);border:1px solid var(--line);border-radius:18px;padding:22px 18px;box-shadow:0 20px 60px rgba(0,0,0,.35);margin-bottom:16px} | |
| h1{font-size:1.62rem;line-height:1.22;margin-bottom:8px} | |
| h2{font-size:1.12rem;margin:0 0 4px} | |
| .sub{color:var(--mut);margin-bottom:6px;font-size:.97rem} | |
| .tabname{color:var(--acc2);font-size:.72rem;letter-spacing:.09em;text-transform:uppercase;font-weight:700;margin-bottom:10px} | |
| .fld{margin-bottom:11px} | |
| .fld label{display:block;font-size:.92rem;margin-bottom:5px;color:#dfe2f5} | |
| .fld .hint{display:block;color:var(--mut);font-size:.78rem;margin-top:3px} | |
| .ig{display:flex;align-items:stretch;background:#161a35;border:1px solid var(--line);border-radius:10px;overflow:hidden} | |
| .ig span{display:flex;align-items:center;padding:0 11px;color:var(--mut);background:#202444;font-size:.95rem;flex:none} | |
| .ig input{flex:1;min-width:0;background:transparent;border:none;color:var(--ink);font-size:16px;font-family:inherit;padding:12px 11px} | |
| .ig input:focus{outline:none} | |
| .ig:focus-within{border-color:var(--acc)} | |
| .verdict{border-radius:16px;padding:18px;margin-bottom:14px;border:2px solid var(--line);background:#161a35} | |
| .verdict.go{border-color:var(--acc2);background:linear-gradient(135deg,rgba(0,210,168,.14),rgba(0,210,168,.04))} | |
| .verdict.no{border-color:var(--warn);background:linear-gradient(135deg,rgba(255,180,84,.14),rgba(255,180,84,.04))} | |
| .verdict.na{border-color:var(--line)} | |
| .vlbl{font-size:.74rem;letter-spacing:.09em;text-transform:uppercase;font-weight:700;color:var(--mut)} | |
| .vbig{font-size:1.98rem;font-weight:800;line-height:1.18;margin:2px 0 6px} | |
| .verdict.go .vbig{color:var(--acc2)} | |
| .verdict.no .vbig{color:var(--warn)} | |
| .verdict.na .vbig{color:var(--mut)} | |
| .vsay{color:#e6e9fb;font-size:1rem} | |
| .tests{margin-top:12px;display:grid;gap:5px} | |
| .t{display:flex;gap:9px;align-items:flex-start;background:#202444;border-radius:9px;padding:9px 12px;font-size:.88rem;color:#cdd2f0} | |
| .t .m{flex:none;font-weight:800;width:1.1em;text-align:center} | |
| .t.p .m{color:var(--acc2)} | |
| .t.f .m{color:var(--warn)} | |
| .t.u .m{color:var(--mut)} | |
| .duo{display:flex;gap:10px;flex-wrap:wrap;margin-bottom:12px} | |
| .duo>div{flex:1;min-width:132px;background:#161a35;border:1px solid var(--line);border-radius:11px;padding:12px 13px} | |
| .lbl{color:var(--mut);font-size:.74rem;letter-spacing:.05em;text-transform:uppercase;font-weight:700} | |
| .big{font-size:1.72rem;font-weight:800;line-height:1.2;color:var(--acc2)} | |
| .rows{display:grid;gap:6px;margin-top:4px} | |
| .r{display:flex;justify-content:space-between;align-items:baseline;gap:12px;background:#202444;border-radius:9px;padding:10px 12px;font-size:.9rem;color:#cdd2f0} | |
| .r b{color:var(--acc2);font-variant-numeric:tabular-nums;white-space:nowrap;font-size:.98rem} | |
| .r b.na{color:var(--warn)} | |
| .note{color:var(--mut);font-size:.82rem;margin-top:9px} | |
| .btn{display:block;width:100%;background:linear-gradient(135deg,var(--acc),#8a7bff);color:#fff;border:none;border-radius:12px;padding:14px 20px;font-size:1rem;font-weight:700;font-family:inherit;cursor:pointer;text-align:center;text-decoration:none;margin-top:10px} | |
| .btn:active{transform:scale(.99)} | |
| .btn.alt{background:transparent;border:1px solid var(--line);color:var(--ink)} | |
| .btn.gold{background:linear-gradient(135deg,#f5c518,#ffdb63);color:#0a0a0f} | |
| .gate{border:1px dashed var(--acc2);border-radius:14px;padding:16px;background:#202444} | |
| .gate b{display:block;font-size:1.05rem;margin-bottom:6px} | |
| .gate p{color:var(--mut);font-size:.89rem;margin-bottom:10px} | |
| .gate input{width:100%;background:#161a35;border:1px solid var(--line);border-radius:10px;color:var(--ink);font-size:16px;font-family:inherit;padding:12px 13px} | |
| .gate input:focus{outline:none;border-color:var(--acc)} | |
| .gmsg{font-size:.9rem;margin-top:10px;color:#cdd2f0} | |
| .gmsg a{color:var(--acc2)} | |
| .priv{color:var(--mut);font-size:.8rem;margin-top:9px} | |
| .sell{border:1px solid rgba(245,197,24,.35);border-radius:14px;padding:16px;background:rgba(245,197,24,.05)} | |
| .sell ul{margin:9px 0 0 18px;color:#cdd2f0;font-size:.9rem} | |
| .sell li{margin-bottom:4px} | |
| .price{font-weight:800;color:#f5c518} | |
| .faq dt{font-weight:700;margin-top:12px;font-size:.96rem} | |
| .faq dd{color:var(--mut);font-size:.9rem;margin-top:3px} | |
| .foot{color:var(--mut);font-size:.79rem;text-align:center;margin-top:6px} | |
| .foot a{color:var(--acc2);text-decoration:none} | |
| .hide{display:none} | |
| </style> | |
| <script data-grow-initializer="">!(function(){window.growMe||((window.growMe=function(e){window.growMe._.push(e);}),(window.growMe._=[]));var e=document.createElement("script");(e.type="text/javascript"),(e.src="https://faves.grow.me/main.js"),(e.defer=!0),e.setAttribute("data-grow-faves-site-id","U2l0ZTphZjljMzgyMC0yMmU5LTRhYjgtYTIxNS03OTRhMGY1N2ExYzM=");var t=document.getElementsByTagName("script")[0];t.parentNode.insertBefore(e,t);})();</script> | |
| </head> | |
| <body> | |
| <div class="wrap"> | |
| <div class="card"> | |
| <h1>Should you refinance your car, or keep the loan you have?</h1> | |
| <p class="sub">Nine numbers in. Seventeen answers out — including the one that matters: a straight verdict, and the reason behind it. A lower payment is not the same as paying less.</p> | |
| <p class="note">Example numbers are pre-filled so you can see it working. Replace them with yours — everything recalculates as you type.</p> | |
| </div> | |
| <div class="card"> | |
| <div class="tabname">Your inputs</div> | |
| <div class="fld"><label for="i2">Current payoff balance<span class="hint">Call the lender and ask for the payoff figure. It includes accrued interest, so it is higher than the "balance" in the app.</span></label><div class="ig"><span>$</span><input type="number" id="i2" inputmode="decimal" step="any" value="18450"></div></div> | |
| <div class="fld"><label for="i3">Current APR on the loan you have</label><div class="ig"><input type="number" id="i3" inputmode="decimal" step="any" value="11.49"><span>%</span></div></div> | |
| <div class="fld"><label for="i4">Current monthly payment<span class="hint">Principal and interest only — leave insurance out.</span></label><div class="ig"><span>$</span><input type="number" id="i4" inputmode="decimal" step="any" value="516"></div></div> | |
| <div class="fld"><label for="i5">Months left on the current loan</label><div class="ig"><input type="number" id="i5" inputmode="numeric" step="any" value="44"><span>mo</span></div></div> | |
| <div class="fld"><label for="i6">New APR you have actually been quoted<span class="hint">Use the lowest real offer you hold — not an advertised rate.</span></label><div class="ig"><input type="number" id="i6" inputmode="decimal" step="any" value="6.29"><span>%</span></div></div> | |
| <div class="fld"><label for="i7">New loan term<span class="hint">36, 48, 60 or 72 months.</span></label><div class="ig"><input type="number" id="i7" inputmode="numeric" step="any" value="48"><span>mo</span></div></div> | |
| <div class="fld"><label for="i8">Closing costs<span class="hint">Title, lien recording, doc fee, plus any prepayment penalty.</span></label><div class="ig"><span>$</span><input type="number" id="i8" inputmode="decimal" step="any" value="185"></div></div> | |
| <div class="fld"><label for="i9">What the car is worth today<span class="hint">KBB private-party or trade value.</span></label><div class="ig"><span>$</span><input type="number" id="i9" inputmode="decimal" step="any" value="21300"></div></div> | |
| <div class="fld"><label for="i10">Months you will realistically keep it<span class="hint">Before you sell or trade it in.</span></label><div class="ig"><input type="number" id="i10" inputmode="numeric" step="any" value="36"><span>mo</span></div></div> | |
| </div> | |
| <div class="card"> | |
| <div class="verdict na" id="vBox"> | |
| <div class="vlbl">The verdict</div> | |
| <div class="vbig" id="vBig">—</div> | |
| <div class="vsay" id="vSay">—</div> | |
| <div class="tests" id="vTests"></div> | |
| </div> | |
| <div class="duo"> | |
| <div><div class="lbl">New monthly payment</div><div class="big" id="hPay">—</div></div> | |
| <div><div class="lbl">Monthly drop</div><div class="big" id="hDrop">—</div></div> | |
| </div> | |
| <div class="tabname">Payment & savings</div> | |
| <h2>Your new payment, and how fast the fees pay for themselves</h2> | |
| <div class="rows" id="secA"></div> | |
| <div class="tabname" style="margin-top:20px">True cost compare</div> | |
| <h2>Total interest both ways — and the price of a longer term</h2> | |
| <div class="rows" id="secB"></div> | |
| <div class="tabname" style="margin-top:20px">Sell early</div> | |
| <h2>What you actually pocket if you sell before either loan ends</h2> | |
| <div class="rows" id="secC"></div> | |
| <p class="note" id="warnBox"></p> | |
| <button class="btn alt" id="copyBtn">Copy my numbers</button> | |
| <button class="btn alt" id="imgBtn">Save my result as an image</button> | |
| </div> | |
| <div class="card"> | |
| <div class="gate"> | |
| <b>Take these numbers with you</b> | |
| <p>Your verdict above is already complete and always free. If you want a spreadsheet you can keep, re-run against a second quote and show the lender, we'll send the free starter workbook: the <b>Inputs</b> tab and the <b>Payment & Savings</b> tab, 6 live formulas, yours to type your own figures into.</p> | |
| <input type="email" id="gEmail" placeholder="you@email.com" autocomplete="email" aria-label="Your email"> | |
| <button class="btn" id="gSend">Email me the free workbook</button> | |
| <p class="gmsg hide" id="gMsg"></p> | |
| <p class="priv">Your numbers never leave your browser — every calculation on this page runs on your device. The only thing we ever send is the email address you type here, and one click unsubscribes.</p> | |
| </div> | |
| </div> | |
| <div class="card"> | |
| <div class="sell"> | |
| <b style="display:block;font-size:1.06rem;margin-bottom:4px">Want the full four-sheet workbook?</b> | |
| <p style="color:#cdd2f0;font-size:.92rem">This page is one slice of it. The paid version is the spreadsheet itself — 17 live cross-sheet formulas, so you can park two competing quotes side by side and watch the verdict cell flip:</p> | |
| <ul> | |
| <li><b>Inputs</b> — the nine cells you edit</li> | |
| <li><b>Payment & Savings</b> — 6 results, including the PMT amortisation</li> | |
| <li><b>True Cost Compare</b> — 6 results, including the cost of stretching the term</li> | |
| <li><b>Sell-Early & Verdict</b> — 5 results, ending in the verdict cell</li> | |
| </ul> | |
| <p style="color:#cdd2f0;font-size:.92rem;margin-top:9px">Works in Excel, Google Sheets and Numbers. <span class="price">$12</span>, one payment, yours to keep.</p> | |
| <a class="btn gold" href="https://pocketvault.gumroad.com/l/ireyql" target="_blank" rel="noopener">Get the full refinance workbook — $12</a> | |
| </div> | |
| </div> | |
| <div class="card faq"> | |
| <h2>Questions this calculator answers</h2> | |
| <dl> | |
| <dt>Is a lower monthly payment always a win?</dt> | |
| <dd>No. Stretching the term lowers the payment and can raise the total interest. The "what stretching the term costs you" row prices exactly that, by comparing your new payment against the payment that would keep your original payoff date.</dd> | |
| <dt>When do the closing costs pay for themselves?</dt> | |
| <dd>Divide the fees by the monthly drop. If that break-even lands after the month you plan to sell the car, you paid fees you never earned back — which is one of the three tests behind the verdict.</dd> | |
| <dt>Why does the payoff figure matter more than the app balance?</dt> | |
| <dd>A payoff quote includes interest accrued since your last payment, so it is higher. Refinancing uses the payoff figure, and using the app balance makes every result here look better than reality.</dd> | |
| <dt>Can I refinance if I owe more than the car is worth?</dt> | |
| <dd>Often not. Lenders commonly cap the new loan near 125% of value — the ceiling this calculator checks. Above it, most applications stop, which is why the verdict turns to "keep the loan".</dd> | |
| </dl> | |
| <p class="note">This is a calculator, not financial advice. Your lender's payoff figure, fees and loan-to-value cap are the ones that count — check them before you apply.</p> | |
| </div> | |
| <p class="foot"> | |
| <a href="/tools/heloc-loan-calculator.html">HELOC calculator</a> · | |
| <a href="/free">All free tools</a> · | |
| <a href="https://getreadystack.com">ReadyStack</a><br> | |
| Free, no signup, no tracking of your figures. Nothing on this page is stored. | |
| </p> | |
| </div> | |
| <script type="application/ld+json"> | |
| {"@context":"https://schema.org","@graph":[ | |
| {"@type":"WebApplication","name":"Car Refinance Calculator","url":"https://getreadystack.com/tools/car-refinance-calculator.html","applicationCategory":"FinanceApplication","operatingSystem":"Any (runs in the browser)","browserRequirements":"Requires JavaScript","description":"Free car refinance calculator: new payment, monthly drop, break-even on closing costs, loan-to-value headroom, total interest both ways, the cost of stretching the term, sell-early balances, and a refinance-or-keep verdict. All maths runs client-side.","offers":{"@type":"Offer","price":"0","priceCurrency":"USD"},"publisher":{"@type":"Organization","name":"ReadyStack","url":"https://getreadystack.com"}}, | |
| {"@type":"FAQPage","mainEntity":[ | |
| {"@type":"Question","name":"Is a lower monthly car payment always a win?","acceptedAnswer":{"@type":"Answer","text":"No. Stretching the term lowers the payment and can raise total interest. Compare the new payment against the payment that would keep the original payoff date to see what the longer term costs."}}, | |
| {"@type":"Question","name":"When do car refinance closing costs pay for themselves?","acceptedAnswer":{"@type":"Answer","text":"Divide the closing costs by the monthly payment drop. If that break-even point falls after the month you plan to sell the car, the fees are never earned back."}}, | |
| {"@type":"Question","name":"Why use the payoff figure instead of the app balance?","acceptedAnswer":{"@type":"Answer","text":"A payoff quote includes interest accrued since the last payment, so it is higher than the balance shown in an app. Refinancing uses the payoff figure."}}, | |
| {"@type":"Question","name":"Can I refinance a car if I owe more than it is worth?","acceptedAnswer":{"@type":"Answer","text":"Often not. Lenders commonly cap the new loan near 125 percent of the car's value, and above that ceiling most applications stop."}}]}]} | |
| </script> | |
| <script> | |
| /*--CALC-CORE-START--*/ | |
| /* ───────────────────────────────────────────────────────────────────────── | |
| CALC CORE — a 1:1 transcription of the 17 formulas in the paid workbook | |
| "refinance-your-car-or-keep-the-loan-17-f.xlsx" (SKU 10). | |
| Every line names the sheet!cell it comes from and repeats the original | |
| formula verbatim in the comment. Nothing is invented and nothing is | |
| approximated: where Excel would raise #DIV/0! this returns null and the | |
| page prints "—" instead of a number. | |
| Pure functions only — no DOM, no network. The offline checker runs these | |
| exact bytes against the workbook recalculated by LibreOffice. | |
| ───────────────────────────────────────────────────────────────────────── */ | |
| function XPMT(rate, nper, pv) { | |
| // Excel PMT(rate, nper, pv) with fv=0, type=0. | |
| if (!isFinite(rate) || !isFinite(nper) || !isFinite(pv)) return null; | |
| if (nper === 0) return null; // Excel: #DIV/0! | |
| if (rate === 0) return -pv / nper; // Excel's zero-rate branch | |
| var f = Math.pow(1 + rate, nper); | |
| if (f === 1) return null; | |
| return -pv * rate * f / (f - 1); | |
| } | |
| function XROUND(v, d) { | |
| // Excel ROUND = half away from zero (JS Math.round is half UP, which | |
| // differs for negatives), so the sign is peeled off first. | |
| if (v === null || !isFinite(v)) return null; | |
| var m = Math.pow(10, d), s = v < 0 ? -1 : 1; | |
| return s * Math.round(Math.abs(v) * m) / m; | |
| } | |
| function computeCarRefi(I) { | |
| // I = { payoff, apr, pay, monthsLeft, newApr, newTerm, fees, value, keepMonths } | |
| // apr / newApr are DECIMALS here (0.1149, 0.0629) exactly as the workbook | |
| // stores them in Inputs!B3 / Inputs!B6. | |
| var payoff = I.payoff, apr = I.apr, pay = I.pay, monthsLeft = I.monthsLeft, | |
| newApr = I.newApr, newTerm = I.newTerm, fees = I.fees, | |
| value = I.value, keep = I.keepMonths; | |
| var O = {}; | |
| // ── Sheet 'Payment & Savings' ───────────────────────────────────────── | |
| // B2 ='Inputs'!$B$6/12 | |
| O.ps_B2 = newApr / 12; | |
| // B3 =PMT('Payment & Savings'!$B$2,'Inputs'!$B$7,-'Inputs'!$B$2) | |
| O.ps_B3 = XPMT(O.ps_B2, newTerm, -payoff); | |
| // B4 ='Inputs'!$B$4-'Payment & Savings'!$B$3 | |
| O.ps_B4 = (O.ps_B3 === null) ? null : pay - O.ps_B3; | |
| // B5 ='Payment & Savings'!$B$4*12 | |
| O.ps_B5 = (O.ps_B4 === null) ? null : O.ps_B4 * 12; | |
| // B6 =IF('Payment & Savings'!$B$4<=0,999,ROUND('Inputs'!$B$8/'Payment & Savings'!$B$4,1)) | |
| O.ps_B6 = (O.ps_B4 === null) ? null : (O.ps_B4 <= 0 ? 999 : XROUND(fees / O.ps_B4, 1)); | |
| // B7 ='Inputs'!$B$9*1.25-'Inputs'!$B$2 | |
| O.ps_B7 = value * 1.25 - payoff; | |
| // ── Sheet 'True Cost Compare' ───────────────────────────────────────── | |
| // B2 ='Inputs'!$B$4*'Inputs'!$B$5-'Inputs'!$B$2 | |
| O.tc_B2 = pay * monthsLeft - payoff; | |
| // B3 ='Payment & Savings'!$B$3*'Inputs'!$B$7-'Inputs'!$B$2+'Inputs'!$B$8 | |
| O.tc_B3 = (O.ps_B3 === null) ? null : O.ps_B3 * newTerm - payoff + fees; | |
| // B4 ='True Cost Compare'!$B$2-'True Cost Compare'!$B$3 | |
| O.tc_B4 = (O.tc_B3 === null) ? null : O.tc_B2 - O.tc_B3; | |
| // B5 ='Inputs'!$B$7-'Inputs'!$B$5 | |
| O.tc_B5 = newTerm - monthsLeft; | |
| // B6 =PMT('Payment & Savings'!$B$2,'Inputs'!$B$5,-'Inputs'!$B$2) | |
| O.tc_B6 = XPMT(O.ps_B2, monthsLeft, -payoff); | |
| // B7 ='Payment & Savings'!$B$3*'Inputs'!$B$7-'True Cost Compare'!$B$6*'Inputs'!$B$5 | |
| O.tc_B7 = (O.ps_B3 === null || O.tc_B6 === null) ? null | |
| : O.ps_B3 * newTerm - O.tc_B6 * monthsLeft; | |
| // ── Sheet 'Sell-Early & Verdict' ────────────────────────────────────── | |
| // B2 =MAX('Inputs'!$B$2*POWER(1+'Inputs'!$B$3/12,'Inputs'!$B$10) | |
| // -'Inputs'!$B$4*((POWER(1+'Inputs'!$B$3/12,'Inputs'!$B$10)-1)/('Inputs'!$B$3/12)),0) | |
| var rOld = apr / 12; | |
| if (rOld === 0) { | |
| O.sv_B2 = null; // Excel: #DIV/0! | |
| } else { | |
| var fOld = Math.pow(1 + rOld, keep); | |
| O.sv_B2 = Math.max(payoff * fOld - pay * ((fOld - 1) / rOld), 0); | |
| } | |
| // B3 =MAX('Inputs'!$B$2*POWER(1+'Payment & Savings'!$B$2,'Inputs'!$B$10) | |
| // -'Payment & Savings'!$B$3*((POWER(1+'Payment & Savings'!$B$2,'Inputs'!$B$10)-1) | |
| // /'Payment & Savings'!$B$2),0) | |
| if (O.ps_B2 === 0 || O.ps_B3 === null) { | |
| O.sv_B3 = null; // Excel: #DIV/0! (or propagated) | |
| } else { | |
| var fNew = Math.pow(1 + O.ps_B2, keep); | |
| O.sv_B3 = Math.max(payoff * fNew - O.ps_B3 * ((fNew - 1) / O.ps_B2), 0); | |
| } | |
| // B4 =('Inputs'!$B$4-'Payment & Savings'!$B$3)*'Inputs'!$B$10 | |
| // +'Sell-Early & Verdict'!$B$2-'Sell-Early & Verdict'!$B$3-'Inputs'!$B$8 | |
| O.sv_B4 = (O.ps_B3 === null || O.sv_B2 === null || O.sv_B3 === null) ? null | |
| : (pay - O.ps_B3) * keep + O.sv_B2 - O.sv_B3 - fees; | |
| // B5 ='True Cost Compare'!$B$4/MAX('Inputs'!$B$8,1) | |
| O.sv_B5 = (O.tc_B4 === null) ? null : O.tc_B4 / Math.max(fees, 1); | |
| // B6 =IF('Payment & Savings'!$B$7<0,0, | |
| // IF('Sell-Early & Verdict'!$B$4<=0,0, | |
| // IF('Payment & Savings'!$B$6>'Inputs'!$B$10,0,1))) | |
| if (O.sv_B4 === null || O.ps_B6 === null) { | |
| O.sv_B6 = null; | |
| O.verdict_reason = 'unavailable'; | |
| } else if (O.ps_B7 < 0) { | |
| O.sv_B6 = 0; O.verdict_reason = 'ltv'; | |
| } else if (O.sv_B4 <= 0) { | |
| O.sv_B6 = 0; O.verdict_reason = 'net'; | |
| } else if (O.ps_B6 > keep) { | |
| O.sv_B6 = 0; O.verdict_reason = 'breakeven'; | |
| } else { | |
| O.sv_B6 = 1; O.verdict_reason = 'pass'; | |
| } | |
| return O; | |
| } | |
| if (typeof module !== 'undefined' && module.exports) { | |
| module.exports = { XPMT: XPMT, XROUND: XROUND, computeCarRefi: computeCarRefi }; | |
| } | |
| /*--CALC-CORE-END--*/ | |
| (function(){ | |
| var el = function(id){ return document.getElementById(id); }; | |
| var IDS = ['i2','i3','i4','i5','i6','i7','i8','i9','i10']; | |
| function raw(id){ var v = parseFloat(el(id).value); return isFinite(v) ? v : 0; } // blank cell = 0, as in Excel | |
| function grp(s){ var p = String(s).split('.'); p[0] = p[0].replace(/\B(?=(\d{3})+(?!\d))/g, ','); return p.join('.'); } | |
| function money(v){ if (v === null || !isFinite(v)) return null; var n = v < 0, a = Math.abs(v).toFixed(2); return (n ? '-$' : '$') + grp(a); } // $#,##0.00 | |
| function money0(v){ if (v === null || !isFinite(v)) return null; var n = v < 0, a = Math.abs(v).toFixed(0); return (n ? '-$' : '$') + grp(a); } | |
| function int(v){ if (v === null || !isFinite(v)) return null; var n = v < 0, a = Math.abs(Math.round(v)).toFixed(0); return (n ? '-' : '') + grp(a); } // #,##0 | |
| function num6(v){ if (v === null || !isFinite(v)) return null; // #,##0.###### | |
| var n = v < 0, a = Math.abs(v).toFixed(6).replace(/0+$/, '').replace(/\.$/, ''); | |
| return (n ? '-' : '') + grp(a); } | |
| function mo(v){ if (v === null) return null; return int(v) + (Math.abs(v) === 1 ? ' month' : ' months'); } | |
| function rows(host, list){ | |
| host.innerHTML = list.map(function(r){ | |
| return '<div class="r"><span>' + r.k + '</span><b class="' + (r.v === null ? 'na' : '') + '">' + (r.v === null ? '—' : r.v) + '</b></div>'; | |
| }).join(''); | |
| } | |
| function run(){ | |
| var I = { | |
| payoff: raw('i2'), apr: raw('i3') / 100, pay: raw('i4'), monthsLeft: raw('i5'), | |
| newApr: raw('i6') / 100, newTerm: raw('i7'), fees: raw('i8'), | |
| value: raw('i9'), keepMonths: raw('i10') | |
| }; | |
| var O = computeCarRefi(I); | |
| var keep = I.keepMonths; | |
| el('hPay').textContent = money(O.ps_B3) || '—'; | |
| el('hDrop').textContent = money(O.ps_B4) || '—'; | |
| // ── the verdict cell, in words ─────────────────────────────────────── | |
| var box = el('vBox'); box.className = 'verdict na'; | |
| var beTxt = (O.ps_B6 === null) ? '—' : (O.ps_B6 === 999 ? 'never at this payment' : num6(O.ps_B6) + ' months'); | |
| if (O.sv_B6 === 1) { | |
| box.className = 'verdict go'; | |
| el('vBig').textContent = 'REFINANCE'; | |
| el('vSay').innerHTML = 'All three tests pass. You are inside the loan-to-value ceiling, you come out <b>' + | |
| money(O.sv_B4) + '</b> ahead if you sell after ' + int(keep) + ' months, and the ' + money(I.fees) + | |
| ' in fees pays for itself in ' + beTxt + ' — sooner than you plan to keep the car.'; | |
| } else if (O.sv_B6 === 0) { | |
| box.className = 'verdict no'; | |
| el('vBig').textContent = 'KEEP THE LOAN YOU HAVE'; | |
| var why; | |
| if (O.verdict_reason === 'ltv') { | |
| why = 'You owe more than the 125% loan-to-value ceiling allows — the payoff balance is ' + money(-O.ps_B7) + | |
| ' above it, so most lenders will not write this loan at all. That is the first test, and it stops here.'; | |
| } else if (O.verdict_reason === 'net') { | |
| why = 'Selling after ' + int(keep) + ' months leaves you <b>' + money(O.sv_B4) + | |
| '</b> — the bigger remaining balance on the longer loan plus the fees eats the payments you saved.'; | |
| } else { | |
| why = 'The ' + money(I.fees) + ' in fees does not pay for itself in time: break-even is ' + beTxt + | |
| ', but you plan to keep the car ' + int(keep) + ' months.'; | |
| } | |
| el('vSay').innerHTML = why; | |
| } else { | |
| el('vBig').textContent = 'Not enough to decide'; | |
| el('vSay').textContent = 'One of the inputs the verdict depends on is zero, so the workbook cannot compute it — see the note below.'; | |
| } | |
| // the three tests, exactly as the verdict cell nests them | |
| var tests = []; | |
| function tRow(state, txt){ return '<div class="t ' + state + '"><span class="m">' + (state === 'p' ? '✓' : state === 'f' ? '✕' : '?') + '</span><span>' + txt + '</span></div>'; } | |
| tests.push(tRow(O.ps_B7 === null ? 'u' : (O.ps_B7 < 0 ? 'f' : 'p'), | |
| 'Inside the 125% loan-to-value ceiling — headroom ' + (money(O.ps_B7) || '—'))); | |
| tests.push(tRow(O.sv_B4 === null ? 'u' : (O.sv_B4 <= 0 ? 'f' : 'p'), | |
| 'You are ahead if you sell on schedule — net ' + (money(O.sv_B4) || '—'))); | |
| tests.push(tRow(O.ps_B6 === null ? 'u' : (O.ps_B6 > keep ? 'f' : 'p'), | |
| 'Fees pay for themselves before you sell — break-even ' + beTxt + ' vs ' + int(keep) + ' months kept')); | |
| el('vTests').innerHTML = tests.join(''); | |
| rows(el('secA'), [ | |
| { k: 'New monthly rate (APR ÷ 12)', v: (O.ps_B2 === null || !isFinite(O.ps_B2)) ? null : (O.ps_B2 * 100).toFixed(2) + '%' }, | |
| { k: 'Your new monthly payment', v: money(O.ps_B3) }, | |
| { k: 'Monthly payment drop', v: money(O.ps_B4) }, | |
| { k: 'Cash freed up over the next 12 months', v: money(O.ps_B5) }, | |
| { k: 'Months until closing costs pay for themselves', v: (O.ps_B6 === null) ? null : (O.ps_B6 === 999 ? '999 — never' : num6(O.ps_B6)) }, | |
| { k: 'Room left before you are too underwater to qualify', v: money(O.ps_B7) } | |
| ]); | |
| rows(el('secB'), [ | |
| { k: 'Interest you still owe on the loan you have', v: money(O.tc_B2) }, | |
| { k: 'Interest plus fees on the new loan, start to finish', v: money(O.tc_B3) }, | |
| { k: 'Lifetime savings if you refinance', v: money(O.tc_B4) }, | |
| { k: 'Extra months you are adding to the debt', v: int(O.tc_B5) }, | |
| { k: 'Payment at the new rate keeping your original payoff date', v: money(O.tc_B6) }, | |
| { k: 'What stretching the term costs you in extra interest', v: money(O.tc_B7) } | |
| ]); | |
| rows(el('secC'), [ | |
| { k: 'Balance still owed on the OLD loan the month you sell', v: money(O.sv_B2) }, | |
| { k: 'Balance still owed on the NEW loan that same month', v: money(O.sv_B3) }, | |
| { k: 'What you actually keep if you sell on schedule', v: money(O.sv_B4) }, | |
| { k: 'Dollars saved per $1 of closing costs', v: num6(O.sv_B5) }, | |
| { k: 'Verdict cell (1 = refinance, 0 = keep)', v: (O.sv_B6 === null) ? null : String(O.sv_B6) } | |
| ]); | |
| // honest warnings — statements about the numbers on screen, nothing invented | |
| var w = []; | |
| if (O.ps_B3 === null) w.push('New loan term is 0 months, so the new payment cannot be calculated.'); | |
| if (O.sv_B2 === null && I.apr === 0) w.push('Current APR is 0%, and the sell-early formula divides by the monthly rate — the workbook returns an error there too, so those rows stay blank.'); | |
| if (O.sv_B3 === null && I.newApr === 0) w.push('New APR is 0%, and the sell-early formula divides by the monthly rate — the workbook returns an error there too, so those rows stay blank.'); | |
| if (O.tc_B6 === null && I.monthsLeft === 0) w.push('Months left is 0, so the same-payoff-date payment cannot be calculated.'); | |
| if (keep > I.monthsLeft && I.monthsLeft > 0) w.push('You plan to keep the car longer than the old loan has left to run, so the sell-early tab treats the old balance as zero and the verdict becomes conservative. The workbook notes the same quirk.'); | |
| if (O.tc_B5 !== null && O.tc_B5 > 0 && O.tc_B4 !== null && O.tc_B4 < 0) w.push('The new loan costs more in total than the one you have, even though the monthly payment is lower.'); | |
| el('warnBox').innerHTML = w.length ? '⚠ ' + w.join(' ') : ''; | |
| window.__card = 'Car refinance check on a ' + money0(I.payoff) + ' payoff at ' + (I.apr * 100).toFixed(2) + | |
| '% -> ' + (I.newApr * 100).toFixed(2) + '% over ' + int(I.newTerm) + ' months:\n' + | |
| '- Verdict: ' + (O.sv_B6 === 1 ? 'REFINANCE' : O.sv_B6 === 0 ? 'KEEP THE LOAN' : 'not enough to decide') + '\n' + | |
| '- New payment: ' + (money(O.ps_B3) || 'n/a') + '/mo (drop ' + (money(O.ps_B4) || 'n/a') + ')\n' + | |
| '- Lifetime savings: ' + (money(O.tc_B4) || 'n/a') + '\n' + | |
| '- Fees break even: ' + beTxt + '\n' + | |
| 'Run yours free: https://getreadystack.com/tools/car-refinance-calculator.html'; | |
| } | |
| IDS.forEach(function(id){ el(id).addEventListener('input', run); }); | |
| run(); | |
| el('copyBtn').addEventListener('click', function(){ | |
| var t = window.__card || '', b = el('copyBtn'), o = b.textContent; | |
| function done(m){ b.textContent = m; setTimeout(function(){ b.textContent = o; }, 1800); } | |
| if (navigator.share) { navigator.share({ text: t }).then(function(){ done('Shared'); }).catch(function(){}); return; } | |
| if (navigator.clipboard) { navigator.clipboard.writeText(t).then(function(){ done('Copied'); }).catch(function(){ done('Copy failed'); }); } | |
| else { done('Copy not supported'); } | |
| }); | |
| function rr(x,px,py,w,h,r){ x.beginPath(); x.moveTo(px+r,py); x.arcTo(px+w,py,px+w,py+h,r); x.arcTo(px+w,py+h,px,py+h,r); x.arcTo(px,py+h,px,py,r); x.arcTo(px,py,px+w,py,r); x.closePath(); } | |
| el('imgBtn').addEventListener('click', function(){ | |
| var W=1200,H=630,c=document.createElement('canvas'); c.width=W; c.height=H; | |
| var x=c.getContext('2d'); if(!x) return; | |
| var FF='-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif'; | |
| var g=x.createLinearGradient(0,0,W,H); g.addColorStop(0,'#0f1020'); g.addColorStop(1,'#161a35'); | |
| x.fillStyle=g; x.fillRect(0,0,W,H); | |
| x.fillStyle='#1a1c2e'; rr(x,40,40,W-80,H-80,28); x.fill(); | |
| x.lineWidth=2; x.strokeStyle='rgba(255,255,255,0.08)'; rr(x,40,40,W-80,H-80,28); x.stroke(); | |
| x.textAlign='left'; | |
| x.fillStyle='#9aa0bf'; x.font='600 28px '+FF; x.fillText('CAR REFINANCE — THE VERDICT', 80, 122); | |
| var vb=el('vBig').textContent, go=el('vBox').classList.contains('go'); | |
| x.fillStyle = go ? '#00d2a8' : (el('vBox').classList.contains('no') ? '#ffb454' : '#9aa0bf'); | |
| x.font='800 78px '+FF; x.fillText(vb.slice(0,26), 80, 216); | |
| x.fillStyle='#9aa0bf'; x.font='600 25px '+FF; x.fillText('New payment', 80, 300); | |
| x.fillStyle='#f4f5fb'; x.font='800 60px '+FF; x.fillText(el('hPay').textContent, 80, 364); | |
| x.fillStyle='#9aa0bf'; x.font='600 25px '+FF; x.fillText('Monthly drop', 620, 300); | |
| x.fillStyle='#f4f5fb'; x.font='800 60px '+FF; x.fillText(el('hDrop').textContent, 620, 364); | |
| x.fillStyle='#cdd2f0'; x.font='500 27px '+FF; | |
| var st=(el('vSay').textContent||'').replace(/\s+/g,' ').slice(0,190), words=st.split(' '), line='', y=430; | |
| for (var i=0;i<words.length;i++){ | |
| var t2=line+words[i]+' '; | |
| if (x.measureText(t2).width > W-170) { x.fillText(line,80,y); line=words[i]+' '; y+=36; if(y>500) break; } | |
| else line=t2; | |
| } | |
| if (y<=500) x.fillText(line,80,y); | |
| x.fillStyle='#6c5ce7'; x.font='700 27px '+FF; x.fillText('getreadystack.com/tools/car-refinance-calculator.html', 80, H-80); | |
| x.fillStyle='#9aa0bf'; x.font='500 24px '+FF; x.fillText('Free car refinance calculator — runs in your browser', 80, H-44); | |
| var b=el('imgBtn'), o=b.textContent; | |
| function dl(u){ var a=document.createElement('a'); a.href=u; a.download='readystack-car-refinance-result.png'; document.body.appendChild(a); a.click(); document.body.removeChild(a); b.textContent='Saved'; setTimeout(function(){ b.textContent=o; },1800); } | |
| if (c.toBlob) { c.toBlob(function(bl){ if(!bl) return; var u=URL.createObjectURL(bl); dl(u); setTimeout(function(){ URL.revokeObjectURL(u); },5000); },'image/png'); } | |
| else { try { dl(c.toDataURL('image/png')); } catch(e){} } | |
| }); | |
| // ── email gate (the ONLY network call this page can make) ─────────────── | |
| // Payload shape = the live contract measured on 2026-07-30: | |
| // { email, magnet_id, source, lang } -> 200 { ok, download_url, ... } | |
| // 'niche' is deliberately OMITTED: the worker resolves 'mniche:<niche>' | |
| // BEFORE 'magnet:<id>', and both calculator magnets carry niche='APP', so | |
| // sending it could hand the visitor the other calculator's file. | |
| // Written inline rather than through hub-api.js because HubAPI.subscribe() | |
| // forwards only niche/result and would silently DROP 'source'. | |
| // Note: the visitor's figures are NOT part of this body. Only the address. | |
| var MAGNET = 'car-refinance-calculator-6e9955-starter'; | |
| var SOURCE = 'tool_car_refinance_calculator'; | |
| var EMAIL_OK = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; | |
| function gmsg(t){ var m = el('gMsg'); m.textContent = t; m.classList.remove('hide'); return m; } | |
| el('gSend').addEventListener('click', function(){ | |
| var email = (el('gEmail').value || '').trim(), btn = el('gSend'), old = btn.textContent; | |
| if (!EMAIL_OK.test(email) || email.length > 255) { el('gEmail').style.borderColor = '#ffb454'; el('gEmail').focus(); gmsg('That email does not look right — check it and try again.'); return; } | |
| btn.disabled = true; btn.textContent = 'Sending…'; | |
| function fail(t){ gmsg(t); btn.disabled = false; btn.textContent = old; } | |
| fetch(location.origin + '/api/subscribe', { | |
| method: 'POST', headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ email: email, magnet_id: MAGNET, source: SOURCE, lang: 'en' }) | |
| }).then(function(res){ | |
| return res.json().then(function(d){ return { status: res.status, data: d }; }) | |
| .catch(function(){ return { status: res.status, data: null }; }); | |
| }).then(function(r){ | |
| if (r && r.status === 200 && r.data && r.data.download_url) { | |
| el('gEmail').classList.add('hide'); btn.classList.add('hide'); | |
| var m = gmsg('Done. It is on the way — or grab it right now: '); | |
| var a = document.createElement('a'); a.href = r.data.download_url; a.target = '_blank'; a.rel = 'noopener'; | |
| a.textContent = 'download the workbook'; m.appendChild(a); | |
| } else if (r && r.status === 503) { | |
| fail('That download unlocks at launch. Your verdict above is complete either way.'); | |
| } else { | |
| fail('Could not send that right now — try again in a moment. Your verdict above is complete.'); | |
| } | |
| }).catch(function(){ fail('Could not reach the server — try again later. Your verdict above is complete.'); }); | |
| }); | |
| })(); | |
| </script> | |
| <section data-yj-cta="paid" style="margin:3rem auto;max-width:46rem;padding:1.5rem 1.25rem;border:1px solid #d8dee9;border-radius:12px;background:#fbfcfe;font:16px/1.6 system-ui,-apple-system,Segoe UI,sans-serif"><h2 style="margin:0 0 .5rem;font-size:1.25rem">Get the complete version</h2><p style="margin:.25rem 0 1rem;color:#41505f">This page is the working piece. The full pack has everything below.</p><a href="https://getreadystack.com/free?p=car-refinance-calculator&lang=en" rel="nofollow" style="display:inline-block;padding:.7rem 1.3rem;border-radius:8px;background:#1b64f2;color:#fff;text-decoration:none;font-weight:600">Send it to me</a></section> | |
| <script data-yj="yj-view-beacon">(function(){try{var p=location.pathname;var k='yjv:'+p;if(sessionStorage.getItem(k))return;sessionStorage.setItem(k,'1');fetch('/api/ev',{method:'POST',keepalive:true,headers:{'Content-Type':'application/json'},body:JSON.stringify({path:p,lang:"en"})}).catch(function(){});}catch(e){}})();</script> | |
| <div class="yj-home-find"><div class="yj-lgbar"><a href="/" class="yj-lg on">EN</a><a href="/de/" class="yj-lg">DE</a><a href="/ja/" class="yj-lg">JA</a><a href="/es/" class="yj-lg">ES</a><a href="/pt/" class="yj-lg">PT</a></div><section class="yj-find"><h2>Find a tool</h2><input id="yjq" type="search" autocomplete="off" placeholder="Type a product name, format or task"><div id="yjr"></div></section><style>.yj-lgbar{position:absolute;top:14px;right:18px;display:flex;gap:6px;z-index:60}.yj-lg{font:600 12px/1 system-ui,sans-serif;padding:6px 9px;border-radius:4px;text-decoration:none;color:#8b96a3;border:1px solid rgba(140,150,165,.28)}.yj-lg:hover{color:#e8eaed;border-color:rgba(160,175,195,.6)}.yj-lg.on{color:#0e1116;background:#cfd6de;border-color:#cfd6de}.yj-find{max-width:760px;margin:34px auto 8px;padding:0 20px}.yj-find h2{font:600 20px/1.3 system-ui,sans-serif;margin:0 0 10px}#yjq{width:100%;box-sizing:border-box;padding:13px 15px;font:400 15.5px system-ui,sans-serif;border:1px solid rgba(140,150,165,.34);border-radius:8px;background:rgba(255,255,255,.04);color:inherit}#yjq:focus{outline:none;border-color:#7FD1C4}#yjr{margin-top:10px;display:flex;flex-direction:column;gap:6px}#yjr a{display:flex;justify-content:space-between;gap:12px;padding:11px 14px;border:1px solid rgba(140,150,165,.22);border-radius:6px;text-decoration:none;font:400 14.5px/1.4 system-ui,sans-serif;color:inherit}#yjr a:hover{border-color:#7FD1C4}#yjr .pr{font:600 13px system-ui,sans-serif;color:#5fbf9a;white-space:nowrap}</style><script>(function(){var q=document.getElementById("yjq"),r=document.getElementById("yjr"),D=null;function load(){if(D)return Promise.resolve(D);return fetch("/search-index.json").then(function(x){return x.json();}).then(function(j){D=j;return j;}).catch(function(){D=[];return D;});}function esc(t){return String(t).replace(/[&<>"]/g,function(c){return {"&":"&","<":"<",">":">",'"':"""}[c];});}function run(){var v=q.value.trim().toLowerCase();if(v.length<2){r.innerHTML="";return;}load().then(function(j){var w=v.split(/\s+/),o=[];for(var i=0;i<j.length&&o.length<12;i++){var t=j[i].t.toLowerCase();var ok=true;for(var k=0;k<w.length;k++){if(t.indexOf(w[k])<0){ok=false;break;}}if(ok)o.push(j[i]);}r.innerHTML=o.length?o.map(function(x){return "<a href=\""+esc(x.u)+"\">"+"<span>"+esc(x.t)+"</span>"+(x.p?"<span class=\"pr\">$"+x.p+"</span>":"")+"</a>";}).join(""):"";});}q.addEventListener("input",run);q.addEventListener("focus",load);})();</script></div></body> | |
| </html> | |