From 4c2ef3dcabae3a51215ae1e4043f599ed9e7acbc Mon Sep 17 00:00:00 2001 From: Jen Breese-Kauth Date: Thu, 14 May 2026 21:02:03 -0700 Subject: [PATCH 01/12] IFDM-152: initial commit for mortgage refi fixup --- .../mortgage-refinancing-calculator/page.tsx | 434 +++--------------- app/ui/globals.css | 2 + 2 files changed, 63 insertions(+), 373 deletions(-) diff --git a/app/interactives/mortgage-refinancing-calculator/page.tsx b/app/interactives/mortgage-refinancing-calculator/page.tsx index c5ba0f5..9ba82de 100644 --- a/app/interactives/mortgage-refinancing-calculator/page.tsx +++ b/app/interactives/mortgage-refinancing-calculator/page.tsx @@ -199,149 +199,71 @@ export default function MortgageCalculator() { -
- - setMonthsRemaining(e.target.value)} - min="0" - step="1" - className="font-bold [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none" - /> -
- - -
-

{monthsRemaining || "0"} months = {(Number.parseFloat(monthsRemaining || "0") / 12).toFixed(1)} years

-
- -
- - setAnnualRate(e.target.value)} - min="0" - step="0.1" - className="text-lagunita font-bold [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none" - /> -
- - + {currentBalance !== null ? "Reset" : "Calculate Current Balance"} +
+
+

Your current mortgage balance will appear here.

+

+ Enter your loan details to see your estimated remaining balance. +

- -
- - setMonthlyPayment(e.target.value)} - min="0" - step="0.01" - className="[appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none" - /> -
- - -
- {currentBalance !== null && ( -
-

Current Mortgage Balance

-

- ${currentBalance.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 })} -

-
- )} - @@ -370,32 +292,6 @@ export default function MortgageCalculator() { step="0.01" className="font-bold [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none" /> -
- - -
@@ -410,32 +306,6 @@ export default function MortgageCalculator() { step="1" className="font-bold [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none" /> -
- - -
@@ -450,32 +320,6 @@ export default function MortgageCalculator() { step="0.01" className="text-lagunita font-bold [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none" /> -
- - -
@@ -490,32 +334,6 @@ export default function MortgageCalculator() { step="0.01" className="font-bold [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none" /> -
- - -
@@ -533,32 +351,6 @@ export default function MortgageCalculator() { step="0.01" className="font-bold [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none" /> -
- - -
@@ -573,32 +365,6 @@ export default function MortgageCalculator() { step="1" className="font-bold [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none" /> -
- - -

{refNewMonths || "0"} months = {(Number.parseFloat(refNewMonths || "0") / 12).toFixed(1)} years

@@ -614,32 +380,6 @@ export default function MortgageCalculator() { step="0.1" className="text-lagunita font-bold [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none" /> -
- - -
@@ -656,32 +396,6 @@ export default function MortgageCalculator() { step="0.01" className="font-bold [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none" /> -
- - -
@@ -696,32 +410,6 @@ export default function MortgageCalculator() { step="1" className="font-bold [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none" /> -
- - -
diff --git a/app/ui/globals.css b/app/ui/globals.css index f0c916f..9830a8f 100644 --- a/app/ui/globals.css +++ b/app/ui/globals.css @@ -119,6 +119,7 @@ --foreground: oklch(0.145 0 0); --button-green: rgba(30, 113, 102, 1); --button-berry: rgba(151, 24, 87, 1); + --results-card-empty: rgba(102, 112, 133, 1); } .dark { @@ -171,6 +172,7 @@ --additional-background: rgb(52, 51, 51); --button-green: rgba(30, 113, 102, 1); --button-berry: rgba(151, 24, 87, 1); + --results-card-empty: oklch(0.985 0 0); } @layer base { From 83fe704e6ae635abd20b215daa3fbae764607157 Mon Sep 17 00:00:00 2001 From: Jen Breese-Kauth Date: Fri, 15 May 2026 09:05:39 -0700 Subject: [PATCH 02/12] refactoring --- .../mortgage-refinancing-calculator/page.tsx | 919 +++++++++++------- app/ui/globals.css | 1 + 2 files changed, 590 insertions(+), 330 deletions(-) diff --git a/app/interactives/mortgage-refinancing-calculator/page.tsx b/app/interactives/mortgage-refinancing-calculator/page.tsx index 9ba82de..d3b3cd9 100644 --- a/app/interactives/mortgage-refinancing-calculator/page.tsx +++ b/app/interactives/mortgage-refinancing-calculator/page.tsx @@ -1,21 +1,23 @@ "use client" -import { useState } from "react" -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/app/ui/components/card" +import { useState, useEffect } from "react" +import { Card, CardContent, CardDescription, CardHeader } from "@/app/ui/components/card" import { Label } from "@/app/ui/components/label" import { Input } from "@/app/ui/components/input" import { Button } from "@/app/ui/components/button" import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/app/ui/components/tabs" -import ThemeToggle from "@/app/lib/theme-toggle"; -import { BiSolidUpArrow, BiSolidDownArrow } from "react-icons/bi"; +import ThemeToggle from "@/app/lib/theme-toggle" +import { FaRotateLeft, FaArrowRight, FaArrowLeft, FaCircleCheck, FaCircleXmark, FaCircleInfo } from "react-icons/fa6" -const formatCurrency = (amount: number) => - amount.toLocaleString("en-US", { - minimumFractionDigits: 2, - maximumFractionDigits: 2 +const formatCurrency = (amount: number) => + amount.toLocaleString("en-US", { + minimumFractionDigits: 2, + maximumFractionDigits: 2, }) export default function MortgageCalculator() { + const [activeTab, setActiveTab] = useState("current-balance") + const [monthsRemaining, setMonthsRemaining] = useState("") const [annualRate, setAnnualRate] = useState("") const [monthlyPayment, setMonthlyPayment] = useState("") @@ -38,129 +40,123 @@ export default function MortgageCalculator() { totalNewCost: number totalSavings: number breakEvenMonths: number - breakEvenMessage: string + breakEvenMessage: string } | null>(null) - const calculateBalance = () => { + const [refErrors, setRefErrors] = useState<{ + newLoanAmount?: string + newRate?: string + newMonths?: string + }>({}) + + // ── Live balance calculation ────────────────────────────────────────────── + useEffect(() => { const months = Number.parseFloat(monthsRemaining) const rate = Number.parseFloat(annualRate) / 100 / 12 const payment = Number.parseFloat(monthlyPayment) - if (isNaN(months) || isNaN(rate) || isNaN(payment) || months <= 0 || rate < 0 || payment <= 0) { - alert("Please enter valid positive numbers") + if ( + !monthsRemaining || !annualRate || !monthlyPayment || + isNaN(months) || isNaN(rate) || isNaN(payment) || + months <= 0 || rate < 0 || payment <= 0 + ) { + setCurrentBalance(null) return } - let balance: number - - if (rate === 0) { - balance = payment * months - } else { - balance = payment * ((1 - Math.pow(1 + rate, -months)) / rate) - } + const balance = rate === 0 + ? payment * months + : payment * ((1 - Math.pow(1 + rate, -months)) / rate) setCurrentBalance(balance) + // Keep refinance tab pre-filled in sync setRefCurrentBalance(balance.toFixed(2)) setRefCurrentMonths(monthsRemaining) setRefCurrentRate(annualRate) setRefCurrentMonthlyPayment(monthlyPayment) + }, [monthsRemaining, annualRate, monthlyPayment]) + + const handleReset = () => { + setCurrentBalance(null) + setMonthsRemaining("") + setAnnualRate("") + setMonthlyPayment("") + setRefCurrentBalance("") + setRefCurrentMonths("") + setRefCurrentRate("") + setRefCurrentMonthlyPayment("") } const calculateRefinance = () => { - const currentBal = Number.parseFloat(refCurrentBalance) - const currentR = Number.parseFloat(refCurrentRate) / 100 / 12 - const currentM = Number.parseFloat(refCurrentMonths) - const newR = Number.parseFloat(refNewRate) / 100 / 12 - const newM = Number.parseFloat(refNewMonths) - const closingCosts = Number.parseFloat(refClosingCosts) || 0 - - if ( - isNaN(currentBal) || - isNaN(currentR) || - isNaN(currentM) || - isNaN(newR) || - isNaN(newM) || - currentBal <= 0 || - currentR < 0 || - currentM <= 0 || - newR < 0 || - newM <= 0 || - closingCosts < 0 - ) { - alert("Please enter valid numbers") - return - } - - // Calculate current monthly payment: PMT = P × [r(1 + r)^n] / [(1 + r)^n - 1] - let currentMonthlyPayment: number - if (currentR === 0) { - currentMonthlyPayment = currentBal / currentM - } else { - currentMonthlyPayment = - (currentBal * (currentR * Math.pow(1 + currentR, currentM))) / (Math.pow(1 + currentR, currentM) - 1) - } - - // Calculate new monthly payment - const newLoanAmount = Number.parseFloat(refNewLoanAmount) - - // Validate newLoanAmount - if ( - isNaN(currentBal) || - isNaN(currentR) || - isNaN(currentM) || - isNaN(newLoanAmount) || - isNaN(newR) || - isNaN(newM) || - currentBal <= 0 || - currentR < 0 || - currentM <= 0 || - newLoanAmount <= 0 || - newR < 0 || - newM <= 0 || - closingCosts < 0 - ) { - alert("Please enter valid numbers") - return - } - - let newMonthlyPayment: number - if (newR === 0) { - newMonthlyPayment = newLoanAmount / newM - } else { - newMonthlyPayment = (newLoanAmount * (newR * Math.pow(1 + newR, newM))) / (Math.pow(1 + newR, newM) - 1) - } - - const monthlySavings = currentMonthlyPayment - newMonthlyPayment - const totalCurrentCost = currentMonthlyPayment * currentM - const totalNewCost = newMonthlyPayment * newM + closingCosts - const totalSavings = totalCurrentCost - totalNewCost - - let breakEvenMonths: number - let breakEvenMessage: string + const errors: { + newLoanAmount?: string; + newRate?: string; + newMonths?: string; + } = {}; + + const currentBal = Number.parseFloat(refCurrentBalance); + const currentR = Number.parseFloat(refCurrentRate) / 100 / 12; + const currentM = Number.parseFloat(refCurrentMonths); + const newR = Number.parseFloat(refNewRate) / 100 / 12; + const newM = Number.parseFloat(refNewMonths); + const closingCosts = Number.parseFloat(refClosingCosts) || 0; + const newLoanAmount = Number.parseFloat(refNewLoanAmount); + + if (!refNewLoanAmount || isNaN(newLoanAmount) || newLoanAmount <= 0) + errors.newLoanAmount = "Please enter a valid loan amount greater than 0."; + if (!refNewRate || isNaN(newR) || newR < 0) + errors.newRate = "Please enter a valid interest rate."; + if (!refNewMonths || isNaN(newM) || newM <= 0) + errors.newMonths = "Please enter a valid loan term greater than 0."; + + setRefErrors(errors); + + if (Object.keys(errors).length > 0) return; + + const currentMonthlyPayment = + currentR === 0 + ? currentBal / currentM + : (currentBal * (currentR * Math.pow(1 + currentR, currentM))) / + (Math.pow(1 + currentR, currentM) - 1); + + const newMonthlyPayment = + newR === 0 + ? newLoanAmount / newM + : (newLoanAmount * (newR * Math.pow(1 + newR, newM))) / + (Math.pow(1 + newR, newM) - 1); + + const monthlySavings = currentMonthlyPayment - newMonthlyPayment; + const totalCurrentCost = currentMonthlyPayment * currentM; + const totalNewCost = newMonthlyPayment * newM + closingCosts; + const totalSavings = totalCurrentCost - totalNewCost; + + let breakEvenMonths: number; + let breakEvenMessage: string; if (monthlySavings > 0) { - // Case 1: Lower monthly payment - breakEvenMonths = closingCosts > 0 ? closingCosts / monthlySavings : 0 - breakEvenMessage = closingCosts > 0 - ? `You'll recover closing costs in ${breakEvenMonths.toFixed(1)} months` - : "No closing costs to recover - immediate savings!" + breakEvenMonths = closingCosts > 0 ? closingCosts / monthlySavings : 0; + breakEvenMessage = + closingCosts > 0 + ? `You'll recover closing costs in ${breakEvenMonths.toFixed(1)} months` + : "No closing costs to recover - immediate savings!"; } else if (monthlySavings < 0) { - // Case 2: Higher monthly payment if (totalSavings > 0) { - breakEvenMonths = closingCosts > 0 ? closingCosts / (-monthlySavings) : 0 - breakEvenMessage = closingCosts > 0 - ? `Despite higher monthly payments, you'll save overall if you stay ${breakEvenMonths.toFixed(1)}+ months` - : "Despite higher monthly payments, you save overall on total interest" + breakEvenMonths = closingCosts > 0 ? closingCosts / -monthlySavings : 0; + breakEvenMessage = + closingCosts > 0 + ? `Despite higher monthly payments, you'll save overall if you stay ${breakEvenMonths.toFixed(1)}+ months` + : "Despite higher monthly payments, you save overall on total interest"; } else { - breakEvenMonths = Infinity - breakEvenMessage = "This refinance costs more overall - not recommended" + breakEvenMonths = Infinity; + breakEvenMessage = + "This refinance costs more overall - not recommended"; } } else { - // Case 3: Same monthly payment - breakEvenMonths = totalSavings > 0 ? 0 : Infinity - breakEvenMessage = totalSavings > 0 - ? "Same monthly payment, but you save on total interest" - : "No financial benefit" + breakEvenMonths = totalSavings > 0 ? 0 : Infinity; + breakEvenMessage = + totalSavings > 0 + ? "Same monthly payment, but you save on total interest" + : "No financial benefit"; } setRefinanceResults({ @@ -172,314 +168,577 @@ export default function MortgageCalculator() { totalSavings, breakEvenMonths, breakEvenMessage, - }) + }); } return (
- {/* Header */}

Mortgage Calculator Suite

- {/* Mode Selection */}
- - + - Current Balance - Refinance Analysis + + Current Balance + + + Refinance Analysis + + {/* ── Tab 1: Current Balance ─────────────────────────────────── */} - Current Mortgage Balance Calculator - Calculate the present value of your remaining monthly mortgage payments (your mortgage balance). + Calculate your remaining mortgage balance (the present value + of your remaining monthly mortgage payments).
+ {/* Left — inputs */}
- - setMonthsRemaining(e.target.value)} - min="0" - step="1" - className="font-bold [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none" - /> -

{monthsRemaining || "0"} months = {(Number.parseFloat(monthsRemaining || "0") / 12).toFixed(1)} years

- - - setAnnualRate(e.target.value)} - min="0" - step="0.1" - className="text-lagunita font-bold [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none" - /> - - - setMonthlyPayment(e.target.value)} - min="0" - step="0.01" - className="[appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none" - /> +
+ +
+ setMonthsRemaining(e.target.value)} + min="0" + step="1" + className="font-bold pr-16 [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none" + /> + {monthsRemaining && ( + + months + + )} +
+ {monthsRemaining && ( +

+ {monthsRemaining} months ={" "} + {(Number.parseFloat(monthsRemaining) / 12).toFixed( + 1, + )}{" "} + years +

+ )} +
+ +
+
+ +
+ + +
+
+
+ setAnnualRate(e.target.value)} + min="0" + step="0.1" + className="text-lagunita font-bold pr-8 [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none" + /> + {annualRate && ( + + % + + )} +
+
+ +
+ +
+ {monthlyPayment && ( + + $ + + )} + setMonthlyPayment(e.target.value)} + min="0" + step="0.01" + className={`font-bold [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none ${monthlyPayment ? "pl-6" : ""}`} + /> +
+
+ + {/* Reset — only visible once there's a result */} {currentBalance !== null && ( -
-

Current Mortgage Balance

+ + )} +
+ + {/* Right — live result or empty state */} +
+ {currentBalance !== null ? ( + <> +

+ Estimated current balance +

- ${currentBalance.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 })} + ${formatCurrency(currentBalance)}

-
+

+ Based on the remaining monthly payments +

+ + + ) : ( + <> +

+ Your current mortgage balance will appear here. +

+

+ Enter your loan details to see your estimated + remaining balance. +

+ )} -
-
-

Your current mortgage balance will appear here.

-

- Enter your loan details to see your estimated remaining balance. -

-
+ {/* ── Tab 2: Refinance Analysis ──────────────────────────────── */} - Refinance Analysis - Analyze if refinancing makes financial sense for your situation. + + Analyze if refinancing makes financial sense for your + situation. -
+ {!currentBalance ? ( +
+

+ Start by calculating your current mortgage balance + first. Enter your details in the Current Balance tab, + then come back here to explore refinance options. +

+ +
+ ) : null} +
+ {/* Left — read-only summary from Tab 1 */}
-

Current Loan Term

- -
- - setRefCurrentBalance(e.target.value)} - min="0" - step="0.01" - className="font-bold [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none" - /> -
- -
- - setRefCurrentMonths(e.target.value)} - min="0" - step="1" - className="font-bold [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none" - /> -
- -
- - setRefCurrentRate(e.target.value)} - min="0" - step="0.01" - className="text-lagunita font-bold [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none" - /> -
- -
- - setRefCurrentMonthlyPayment(e.target.value)} - min="0" - step="0.01" - className="font-bold [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none" - /> -
+

+ Current Loan Terms +

+ + {currentBalance ? ( +
+
+
+ Current balance +
+
+ ${formatCurrency(currentBalance)} +
+
+
+
+ Time remaining +
+
+ {refCurrentMonths} months + {refCurrentMonths && + ` (${(Number.parseFloat(refCurrentMonths) / 12).toFixed(1)} years)`} +
+
+
+
+ Interest rate +
+
+ {refCurrentRate}% +
+
+
+
+ Monthly payment +
+
+ ${refCurrentMonthlyPayment} +
+
+
+ +
+
+ ) : ( +

+ No balance calculated yet. +

+ )}
+ + {/* Right — new loan term inputs */}
-

New Loan Terms

+

+ New Loan Terms +

- - setRefNewLoanAmount(e.target.value)} - min="0" - step="0.01" - className="font-bold [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none" - /> + +
+ {refNewLoanAmount && ( + + $ + + )} + + setRefNewLoanAmount(e.target.value) + } + min="0" + step="0.01" + className={`font-bold [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none ${refNewLoanAmount ? "pl-6" : ""} ${refErrors.newLoanAmount ? "border-error" : ""}`} + /> +
+ {refErrors.newLoanAmount && ( +

+ {refErrors.newLoanAmount} +

+ )}
- - setRefNewMonths(e.target.value)} - min="0" - step="1" - className="font-bold [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none" - /> -

{refNewMonths || "0"} months = {(Number.parseFloat(refNewMonths || "0") / 12).toFixed(1)} years

+ +
+ setRefNewMonths(e.target.value)} + min="0" + step="1" + className="font-bold pr-16 [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none" + /> + {refNewMonths && ( + + months + + )} +
+ {refNewMonths && ( +

+ {refNewMonths} months ={" "} + {(Number.parseFloat(refNewMonths) / 12).toFixed(1)}{" "} + years +

+ )} + {refErrors.newMonths && ( +

+ {refErrors.newMonths} +

+ )}
- - setRefNewRate(e.target.value)} - min="0" - step="0.1" - className="text-lagunita font-bold [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none" - /> +
+ +
+
+ setRefNewRate(e.target.value)} + min="0" + step="0.1" + className="text-lagunita font-bold pr-8 [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none" + /> + {refNewRate && ( + + % + + )} +
+ {refErrors.newRate && ( +

+ {refErrors.newRate} +

+ )}
-
+
- - setRefClosingCosts(e.target.value)} - min="0" - step="0.01" - className="font-bold [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none" - /> + +
+ {refClosingCosts && ( + + $ + + )} + setRefClosingCosts(e.target.value)} + min="0" + step="0.01" + className={`font-bold [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none ${refClosingCosts ? "pl-6" : ""}`} + /> +
- - setRefYearsIn(e.target.value)} - min="0" - step="1" - className="font-bold [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none" - /> + +
+ setRefYearsIn(e.target.value)} + min="0" + step="1" + className="font-bold pr-14 [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none" + /> + {refYearsIn && ( + + years + + )} +
- {refinanceResults && ( -
- -
-

New monthly payment

-

- ${formatCurrency(refinanceResults.newMonthlyPayment)} -

-
- -
-

- {refinanceResults.monthlySavings >= 0 ? "Monthly savings" : "Monthly increase"} -

-

= 0 - ? "bg-lagunita-lighter text-black border-1 border-lagunita" - : "bg-sky border-sky-dark text-black" - }`}> - ${formatCurrency(Math.abs(refinanceResults.monthlySavings))} -

+ {/* Results */} + {/* Action buttons */} + {refinanceResults ? ( +
+ {/* Header */} +
+ {refinanceResults.totalSavings >= 0 ? ( + <> +

+ Refinancing may be + worth it +

+

+ You could save money over the time you plan to + stay in the home. +

+ + ) : ( + <> +

+ Refinancing may not be + worth it +

+

+ Based on the planned time in the home, this + refinance may cost more than staying with the + current loan. +

+ + )}
-
-

- {refinanceResults.totalSavings >= 0 ? "Refinancing saves you" : "Refinancing costs you"} -

-

= 0 ? "text-lagunita" : "text-berry" - }`}> - ${formatCurrency(Math.abs(refinanceResults.totalSavings))} -

+ {/* Table */} +
+
+ + New monthly payment + + + $ + {formatCurrency(refinanceResults.newMonthlyPayment)} + +
+
+ + {refinanceResults.monthlySavings >= 0 + ? "Monthly savings" + : "Monthly increase"} + + = 0 ? "text-lagunita" : "text-berry"}`} + > + {refinanceResults.monthlySavings >= 0 ? "−" : "+"}$ + {formatCurrency( + Math.abs(refinanceResults.monthlySavings), + )} + +
+
+ + Closing costs & fees + + + $ + {formatCurrency( + Number.parseFloat(refClosingCosts) || 0, + )} + +
+
+ + {refinanceResults.totalSavings >= 0 + ? "Estimated savings over planned stay" + : "Estimated total cost difference"} + + = 0 ? "text-lagunita" : "text-berry"}`} + > + {refinanceResults.totalSavings >= 0 ? "−" : "+"}$ + {formatCurrency( + Math.abs(refinanceResults.totalSavings), + )} + +
+ ) : ( +
+

+ Your refinance analysis will appear here. +

+

Enter your new loan details to compare options.

+
)} + {/* Action buttons */} {refinanceResults ? (
- + + Reset new loan terms +
) : ( )} - +
-
- ) + ); } \ No newline at end of file diff --git a/app/ui/globals.css b/app/ui/globals.css index 9830a8f..f2c7c06 100644 --- a/app/ui/globals.css +++ b/app/ui/globals.css @@ -45,6 +45,7 @@ --color-sky-dark: rgba(14, 59, 79, 1); --color-berry: rgba(195, 31, 112, 1); --color-berry-light: rgba(255, 237, 246, 1); + --color-error: #8C1515; --color-palo-verde: rgba(39, 153, 137, 1); --color-palo-verde-light: rgba(239, 247, 239, 1); --color-lagunita: rgba(0, 124, 146, 1); From ac7afd17bdada918d49c4d1540c0194bb7441533 Mon Sep 17 00:00:00 2001 From: Jen Breese-Kauth Date: Fri, 15 May 2026 10:40:15 -0700 Subject: [PATCH 03/12] design fixup --- .../mortgage-refinancing-calculator/page.tsx | 666 +++++++++--------- app/ui/globals.css | 4 +- 2 files changed, 349 insertions(+), 321 deletions(-) diff --git a/app/interactives/mortgage-refinancing-calculator/page.tsx b/app/interactives/mortgage-refinancing-calculator/page.tsx index d3b3cd9..0ca3c6c 100644 --- a/app/interactives/mortgage-refinancing-calculator/page.tsx +++ b/app/interactives/mortgage-refinancing-calculator/page.tsx @@ -252,7 +252,7 @@ export default function MortgageCalculator() { ) : null} +
- {/* Left — read-only summary from Tab 1 */} -
-

- Current Loan Terms -

- - {currentBalance ? ( -
-
-
- Current balance -
-
- ${formatCurrency(currentBalance)} -
-
-
-
- Time remaining -
-
- {refCurrentMonths} months - {refCurrentMonths && - ` (${(Number.parseFloat(refCurrentMonths) / 12).toFixed(1)} years)`} -
-
-
-
- Interest rate -
-
- {refCurrentRate}% -
+ {/* ── Left column ─────────────────────────────────────────── */} +
+ {/* Current Loan Terms — read-only */} +
+

+ Current Loan Terms +

+ {currentBalance ? ( +
+
+
+
+ Current balance: +
+
+ ${formatCurrency(currentBalance)} +
+
+
+
+ Time remaining: +
+
+ {refCurrentMonths} months + {refCurrentMonths && + ` (${(Number.parseFloat(refCurrentMonths) / 12).toFixed(1)} years)`} +
+
+
+
+ Interest rate: +
+
+ {refCurrentRate}% +
+
+
+
+ Monthly payment: +
+
+ ${refCurrentMonthlyPayment} +
+
+
+ +
+
-
-
- Monthly payment -
-
- ${refCurrentMonthlyPayment} -
-
-
- + ) : ( +
+

+ No balance calculated yet. +

-
- ) : ( -

- No balance calculated yet. -

- )} -
+ )} +
- {/* Right — new loan term inputs */} -
-

- New Loan Terms -

+ {/* New Loan Terms — inputs */} +
+

+ New Loan Terms +

-
- -
- {refNewLoanAmount && ( - - $ - +
+ +
+ {refNewLoanAmount && ( + + $ + + )} + + setRefNewLoanAmount(e.target.value) + } + min="0" + step="0.01" + className={`font-bold [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none ${refNewLoanAmount ? "pl-6" : ""} ${refErrors.newLoanAmount ? "border-error" : ""}`} + /> +
+ {refErrors.newLoanAmount && ( +

+ {refErrors.newLoanAmount} +

)} - - setRefNewLoanAmount(e.target.value) - } - min="0" - step="0.01" - className={`font-bold [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none ${refNewLoanAmount ? "pl-6" : ""} ${refErrors.newLoanAmount ? "border-error" : ""}`} - />
- {refErrors.newLoanAmount && ( -

- {refErrors.newLoanAmount} -

- )} -
-
- -
- setRefNewMonths(e.target.value)} - min="0" - step="1" - className="font-bold pr-16 [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none" - /> +
+ +
+ setRefNewMonths(e.target.value)} + min="0" + step="1" + className={`font-bold pr-16 [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none ${refErrors.newMonths ? "border-error" : ""}`} + /> + {refNewMonths && ( + + months + + )} +
{refNewMonths && ( - - months - +

+ {refNewMonths} months ={" "} + {(Number.parseFloat(refNewMonths) / 12).toFixed( + 1, + )}{" "} + years +

+ )} + {refErrors.newMonths && ( +

+ {refErrors.newMonths} +

)}
- {refNewMonths && ( -

- {refNewMonths} months ={" "} - {(Number.parseFloat(refNewMonths) / 12).toFixed(1)}{" "} - years -

- )} - {refErrors.newMonths && ( -

- {refErrors.newMonths} -

- )} -
-
-
+
-
-
- setRefNewRate(e.target.value)} - min="0" - step="0.1" - className="text-lagunita font-bold pr-8 [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none" - /> - {refNewRate && ( - - % - +
+ setRefNewRate(e.target.value)} + min="0" + step="0.1" + className={`text-lagunita font-bold pr-8 [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none ${refErrors.newRate ? "border-error" : ""}`} + /> + {refNewRate && ( + + % + + )} +
+ {refErrors.newRate && ( +

+ {refErrors.newRate} +

)}
- {refErrors.newRate && ( -

- {refErrors.newRate} -

- )} -
-
+
-
- -
- {refClosingCosts && ( - - $ - - )} - setRefClosingCosts(e.target.value)} - min="0" - step="0.01" - className={`font-bold [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none ${refClosingCosts ? "pl-6" : ""}`} - /> +
+ +
+ {refClosingCosts && ( + + $ + + )} + + setRefClosingCosts(e.target.value) + } + min="0" + step="0.01" + className={`font-bold [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none ${refClosingCosts ? "pl-6" : ""}`} + /> +
-
-
- -
- setRefYearsIn(e.target.value)} - min="0" - step="1" - className="font-bold pr-14 [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none" - /> - {refYearsIn && ( - - years - - )} +
+ +
+ setRefYearsIn(e.target.value)} + min="0" + step="1" + className="font-bold pr-14 [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none" + /> + {refYearsIn && ( + + years + + )} +
-
-
-
- {/* Results */} - {/* Action buttons */} - {refinanceResults ? ( -
- {/* Header */} -
- {refinanceResults.totalSavings >= 0 ? ( - <> -

- Refinancing may be - worth it -

-

- You could save money over the time you plan to - stay in the home. -

- + {/* Action buttons */} + {refinanceResults ? ( +
+ + +
) : ( - <> -

- Refinancing may not be - worth it -

-

- Based on the planned time in the home, this - refinance may cost more than staying with the - current loan. -

- + )}
+
- {/* Table */} -
-
- - New monthly payment - - - $ - {formatCurrency(refinanceResults.newMonthlyPayment)} - -
-
- - {refinanceResults.monthlySavings >= 0 - ? "Monthly savings" - : "Monthly increase"} - - = 0 ? "text-lagunita" : "text-berry"}`} - > - {refinanceResults.monthlySavings >= 0 ? "−" : "+"}$ - {formatCurrency( - Math.abs(refinanceResults.monthlySavings), - )} - -
-
- - Closing costs & fees - - - $ - {formatCurrency( - Number.parseFloat(refClosingCosts) || 0, + {/* ── Right column — results or empty state ───────────────── */} +
+ {refinanceResults ? ( +
+
+ {refinanceResults.totalSavings >= 0 ? ( + <> +

+

+

+ You could save money over the time you plan to + stay in the home. +

+ + ) : ( + <> +

+

+

+ Based on the planned time in the home, this + refinance may cost more than staying with the + current loan. +

+ )} - +
+ +
+
+
+
+ New monthly payment: +
+
+ $ + {formatCurrency( + refinanceResults.newMonthlyPayment, + )} +
+
+ +
+
+ {refinanceResults.monthlySavings >= 0 + ? "Monthly savings:" + : "Monthly increase:"} +
+
+ {refinanceResults.monthlySavings >= 0 + ? "−" + : "+"} + $ + {formatCurrency( + Math.abs(refinanceResults.monthlySavings), + )} +
+
+ +
+
+ Closing costs & fees: +
+
+ $ + {formatCurrency( + Number.parseFloat(refClosingCosts) || 0, + )} +
+
+ +
+
+ {refinanceResults.totalSavings >= 0 + ? "Estimated savings over planned stay:" + : "Estimated total cost difference:"} +
+
+ {refinanceResults.totalSavings >= 0 + ? "−" + : "+"} + $ + {formatCurrency( + Math.abs(refinanceResults.totalSavings), + )} +
+
+
+
-
- - {refinanceResults.totalSavings >= 0 - ? "Estimated savings over planned stay" - : "Estimated total cost difference"} - - = 0 ? "text-lagunita" : "text-berry"}`} - > - {refinanceResults.totalSavings >= 0 ? "−" : "+"}$ - {formatCurrency( - Math.abs(refinanceResults.totalSavings), - )} - + ) : ( +
+

+ Your refinance analysis will appear here. +

+

Enter your new loan details to compare options.

-
-
- ) : ( -
-

- Your refinance analysis will appear here. -

-

Enter your new loan details to compare options.

-
- )} - - {/* Action buttons */} - {refinanceResults ? ( -
- - + )}
- ) : ( - - )} +
diff --git a/app/ui/globals.css b/app/ui/globals.css index f2c7c06..0e3f66d 100644 --- a/app/ui/globals.css +++ b/app/ui/globals.css @@ -58,7 +58,6 @@ --color-grey-light: rgba(251, 251, 255, 1); --color-grey-border: rgba(207, 207, 207, 1); --color-pinecone: rgba(233, 255, 251, 1); - --color-badge-green: rgba(135, 205, 168, 1); --color-badge-yellow: rgba(253, 225, 131, 1); --color-badge-red: rgba(249, 80, 72, 1); @@ -91,6 +90,7 @@ --results-blue-background: rgba(255, 255, 255, 1); --results-white-background: rgba(255, 255, 255, 1); --results-year-background: rgba(233, 255, 251, 1); + --results-card-grey-background: rgba(248, 248, 248, 1); --year-by-year-table: rgba(246, 246, 246, 1); --year-by-year-table-line: rgba(200, 200, 200, 1); --accent: oklch(0.97 0 0); @@ -121,6 +121,7 @@ --button-green: rgba(30, 113, 102, 1); --button-berry: rgba(151, 24, 87, 1); --results-card-empty: rgba(102, 112, 133, 1); + --info-popup-background: rgba(239, 255, 252, 1); } .dark { @@ -144,6 +145,7 @@ --results-blue-background: rgba(25, 47, 57, 1); --results-white-background: oklch(0.145 0 0); --results-year-background: oklch(0.145 0 0); + --results-card-grey-background: oklch(0.145 0 0); --year-by-year-table: oklch(0.145 0 0); --year-by-year-table-line: rgba(255, 255, 255, 1); --muted-foreground: oklch(0.708 0 0); From 48e633cd2451a8c3cd7610f4d0888a2820b283dd Mon Sep 17 00:00:00 2001 From: Jen Breese-Kauth Date: Fri, 15 May 2026 10:56:19 -0700 Subject: [PATCH 04/12] fixup --- .../mortgage-refinancing-calculator/page.tsx | 39 +++++++++---------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/app/interactives/mortgage-refinancing-calculator/page.tsx b/app/interactives/mortgage-refinancing-calculator/page.tsx index 0ca3c6c..21a67c3 100644 --- a/app/interactives/mortgage-refinancing-calculator/page.tsx +++ b/app/interactives/mortgage-refinancing-calculator/page.tsx @@ -271,7 +271,7 @@ export default function MortgageCalculator() { onChange={(e) => setAnnualRate(e.target.value)} min="0" step="0.1" - className="text-lagunita font-bold pr-8 [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none" + className="font-bold pr-8 [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none" /> {annualRate && ( @@ -368,18 +368,19 @@ export default function MortgageCalculator() { {!currentBalance ? ( -
-

+

+

Start by calculating your current mortgage balance first. Enter your details in the Current Balance tab, then come back here to explore refinance options. + +

-
) : null} @@ -416,7 +417,7 @@ export default function MortgageCalculator() {
Interest rate:
-
+
{refCurrentRate}%
@@ -442,7 +443,7 @@ export default function MortgageCalculator() {
) : ( -
+

No balance calculated yet.

@@ -479,11 +480,11 @@ export default function MortgageCalculator() { } min="0" step="0.01" - className={`font-bold [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none ${refNewLoanAmount ? "pl-6" : ""} ${refErrors.newLoanAmount ? "border-error" : ""}`} + className={`font-bold [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none ${refNewLoanAmount ? "pl-6" : ""} ${refErrors.newLoanAmount ? "border-error border-2" : ""}`} />
{refErrors.newLoanAmount && ( -

+

{refErrors.newLoanAmount}

)} @@ -505,7 +506,7 @@ export default function MortgageCalculator() { onChange={(e) => setRefNewMonths(e.target.value)} min="0" step="1" - className={`font-bold pr-16 [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none ${refErrors.newMonths ? "border-error" : ""}`} + className={`font-bold pr-16 [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none ${refErrors.newMonths ? "border-error border-2" : ""}`} /> {refNewMonths && ( @@ -523,7 +524,7 @@ export default function MortgageCalculator() {

)} {refErrors.newMonths && ( -

+

{refErrors.newMonths}

)} @@ -545,7 +546,7 @@ export default function MortgageCalculator() { onChange={(e) => setRefNewRate(e.target.value)} min="0" step="0.1" - className={`text-lagunita font-bold pr-8 [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none ${refErrors.newRate ? "border-error" : ""}`} + className={`font-bold pr-8 [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none ${refErrors.newRate ? "border-error border-2" : ""}`} /> {refNewRate && ( @@ -554,7 +555,7 @@ export default function MortgageCalculator() { )}
{refErrors.newRate && ( -

+

{refErrors.newRate}

)} @@ -660,7 +661,6 @@ export default function MortgageCalculator() { {refinanceResults.totalSavings >= 0 ? ( <>

-

@@ -670,8 +670,7 @@ export default function MortgageCalculator() { ) : ( <> -

-

@@ -553,7 +553,7 @@ export default function MortgageCalculator() { } min="0" step="0.01" - className={`font-bold [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none ${refNewLoanAmount ? "pl-6" : ""} ${refErrors.newLoanAmount ? "border-error border-2" : ""}`} + className={`[appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none ${refNewLoanAmount ? "pl-6" : ""} ${refErrors.newLoanAmount ? "border-error border-2" : ""}`} />
{refErrors.newLoanAmount && ( @@ -579,7 +579,7 @@ export default function MortgageCalculator() { onChange={(e) => setRefNewMonths(e.target.value)} min="0" step="1" - className={`font-bold pr-16 [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none ${refErrors.newMonths ? "border-error border-2" : ""}`} + className={`pr-16 [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none ${refErrors.newMonths ? "border-error border-2" : ""}`} /> {refNewMonths && ( @@ -619,7 +619,7 @@ export default function MortgageCalculator() { onChange={(e) => setRefNewRate(e.target.value)} min="0" step="0.1" - className={`font-bold pr-8 [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none ${refErrors.newRate ? "border-error border-2" : ""}`} + className={`pr-8 [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none ${refErrors.newRate ? "border-error border-2" : ""}`} /> {refNewRate && ( @@ -659,7 +659,7 @@ export default function MortgageCalculator() { } min="0" step="0.01" - className={`font-bold [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none ${refClosingCosts ? "pl-6" : ""}`} + className={`[appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none ${refClosingCosts ? "pl-6" : ""}`} />
@@ -680,7 +680,7 @@ export default function MortgageCalculator() { onChange={(e) => setRefYearsIn(e.target.value)} min="0" step="1" - className="font-bold pr-14 [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none" + className="pr-14 [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none" /> {refYearsIn && ( From 1da4cc372d5cc92aee5007a5685b381816831bd9 Mon Sep 17 00:00:00 2001 From: Jen Breese-Kauth Date: Tue, 19 May 2026 15:34:48 -0700 Subject: [PATCH 09/12] fixup --- .../page.tsx | 38 ++++++------------- app/ui/globals.css | 1 + 2 files changed, 13 insertions(+), 26 deletions(-) diff --git a/app/interactives/mortgage-refinancing-calculator-v2/page.tsx b/app/interactives/mortgage-refinancing-calculator-v2/page.tsx index bd2a326..83447fb 100644 --- a/app/interactives/mortgage-refinancing-calculator-v2/page.tsx +++ b/app/interactives/mortgage-refinancing-calculator-v2/page.tsx @@ -267,16 +267,12 @@ export default function MortgageCalculator() { {/* ── Tab 1: Current Balance ─────────────────────────────────── */} - - - Calculate your remaining mortgage balance (the present value - of your remaining monthly mortgage payments). - -
{/* Left — inputs */}
+

Calculate your remaining mortgage balance (the present value of your remaining monthly mortgage payments).

+
@@ -433,13 +432,8 @@ export default function MortgageCalculator() { {/* ── Tab 2: Refinance Analysis ──────────────────────────────── */} - - - Analyze if refinancing makes financial sense for your - situation. - - +

Analyze if refinancing makes financial sense for your situation.

{!currentBalance ? (

@@ -581,11 +575,9 @@ export default function MortgageCalculator() { step="1" className={`pr-16 [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none ${refErrors.newMonths ? "border-error border-2" : ""}`} /> - {refNewMonths && ( - - months - - )} + + months +

{refNewMonths && (

@@ -778,9 +770,6 @@ export default function MortgageCalculator() {

- {refinanceResults.monthlySavings >= 0 - ? "−" - : "+"} $ {formatCurrency( Math.abs(refinanceResults.monthlySavings), @@ -809,9 +798,6 @@ export default function MortgageCalculator() {
- {refinanceResults.totalSavings >= 0 - ? "−" - : "+"} $ {formatCurrency( Math.abs(refinanceResults.totalSavings), diff --git a/app/ui/globals.css b/app/ui/globals.css index 0e3f66d..56dce2f 100644 --- a/app/ui/globals.css +++ b/app/ui/globals.css @@ -176,6 +176,7 @@ --button-green: rgba(30, 113, 102, 1); --button-berry: rgba(151, 24, 87, 1); --results-card-empty: oklch(0.985 0 0); + --info-popup-background: rgba(239, 255, 252, 1); } @layer base { From 63ed2d7cf0aa7ae8ace5d0f51369135759c4399e Mon Sep 17 00:00:00 2001 From: Jen Breese-Kauth Date: Tue, 19 May 2026 15:43:54 -0700 Subject: [PATCH 10/12] fixup --- .../page.tsx | 39 +++++++++++++------ 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/app/interactives/mortgage-refinancing-calculator-v2/page.tsx b/app/interactives/mortgage-refinancing-calculator-v2/page.tsx index 83447fb..bb0d16a 100644 --- a/app/interactives/mortgage-refinancing-calculator-v2/page.tsx +++ b/app/interactives/mortgage-refinancing-calculator-v2/page.tsx @@ -271,7 +271,10 @@ export default function MortgageCalculator() {
{/* Left — inputs */}
-

Calculate your remaining mortgage balance (the present value of your remaining monthly mortgage payments).

+

+ Calculate your remaining mortgage balance (the present + value of your remaining monthly mortgage payments). +

{refErrors.newRate && ( -

+

{refErrors.newRate}

)} @@ -767,9 +786,7 @@ export default function MortgageCalculator() { ? "Monthly savings:" : "Monthly increase:"}
-
+
$ {formatCurrency( Math.abs(refinanceResults.monthlySavings), @@ -795,9 +812,7 @@ export default function MortgageCalculator() { ? "Estimated savings over planned stay:" : "Estimated total cost difference:"}
-
+
$ {formatCurrency( Math.abs(refinanceResults.totalSavings), From 0004d0d7f27b0c62c93b8a288e6ab3deaef52237 Mon Sep 17 00:00:00 2001 From: Jen Breese-Kauth Date: Tue, 19 May 2026 16:01:13 -0700 Subject: [PATCH 11/12] fixup --- .../page.tsx | 21 +++++-------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/app/interactives/mortgage-refinancing-calculator-v2/page.tsx b/app/interactives/mortgage-refinancing-calculator-v2/page.tsx index bb0d16a..3a5b2f3 100644 --- a/app/interactives/mortgage-refinancing-calculator-v2/page.tsx +++ b/app/interactives/mortgage-refinancing-calculator-v2/page.tsx @@ -355,14 +355,9 @@ export default function MortgageCalculator() {
- {monthlyPayment && ( - - $ - - )} - - $ - + $
@@ -542,11 +535,6 @@ export default function MortgageCalculator() { New loan amount
- {refNewLoanAmount && ( - - $ - - )} + $
{refErrors.newLoanAmount && (

- + months

From aa36646ce25638b81484404bad4ee8ccf7ec4970 Mon Sep 17 00:00:00 2001 From: Jen Breese-Kauth Date: Wed, 20 May 2026 15:47:32 -0700 Subject: [PATCH 12/12] updated error colors and fixed the dark mode $ or % --- .../page.tsx | 22 +++++++++---------- app/ui/globals.css | 5 +++++ 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/app/interactives/mortgage-refinancing-calculator-v2/page.tsx b/app/interactives/mortgage-refinancing-calculator-v2/page.tsx index 3a5b2f3..ef13052 100644 --- a/app/interactives/mortgage-refinancing-calculator-v2/page.tsx +++ b/app/interactives/mortgage-refinancing-calculator-v2/page.tsx @@ -292,7 +292,7 @@ export default function MortgageCalculator() { className="pr-16 [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none" /> {monthsRemaining && ( - + months )} @@ -346,7 +346,7 @@ export default function MortgageCalculator() { className="pr-8 [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none" /> {annualRate && ( - + % )} @@ -373,7 +373,7 @@ export default function MortgageCalculator() { data-form-type="other" // Dashlane specifically className="w-full pl-8 pr-4 py-3 border-2 border-gray-300 rounded-lg focus:border-blue-500 focus:ring-2 focus:ring-blue-200 outline-none transition [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none" /> - $ + $
@@ -545,14 +545,14 @@ export default function MortgageCalculator() { } min="0" step="0.01" - className={`[appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none ${refNewLoanAmount ? "pl-8" : "pl-8 pr-4"} ${refErrors.newLoanAmount ? "border-error border-2" : ""}`} + className={`[appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none ${refNewLoanAmount ? "pl-8" : "pl-8 pr-4"} ${refErrors.newLoanAmount ? "border-[var(--color-inline-error)] border-2" : ""}`} /> - $ + $
{refErrors.newLoanAmount && (

{refErrors.newLoanAmount}

@@ -575,9 +575,9 @@ export default function MortgageCalculator() { onChange={(e) => setRefNewMonths(e.target.value)} min="0" step="1" - className={`pr-16 [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none ${refErrors.newMonths ? "border-error border-2" : ""}`} + className={`pr-16 [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none ${refErrors.newMonths ? "border-[var(--color-inline-error)] border-2" : ""}`} /> - + months
@@ -593,7 +593,7 @@ export default function MortgageCalculator() { {refErrors.newMonths && (

{refErrors.newMonths}

@@ -616,7 +616,7 @@ export default function MortgageCalculator() { onChange={(e) => setRefNewRate(e.target.value)} min="0" step="0.1" - className={`pr-8 [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none ${refErrors.newRate ? "border-error border-2" : ""}`} + className={`pr-8 [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none ${refErrors.newRate ? "border-[var(--color-inline-error)] border-2" : ""}`} /> {refNewRate && ( @@ -627,7 +627,7 @@ export default function MortgageCalculator() { {refErrors.newRate && (

{refErrors.newRate}

diff --git a/app/ui/globals.css b/app/ui/globals.css index 56dce2f..b0a5200 100644 --- a/app/ui/globals.css +++ b/app/ui/globals.css @@ -120,6 +120,9 @@ --foreground: oklch(0.145 0 0); --button-green: rgba(30, 113, 102, 1); --button-berry: rgba(151, 24, 87, 1); + --color-teal: rgba(74, 203, 225, 1); + --color-symbols: #616877; + --color-inline-error: #8C1515; --results-card-empty: rgba(102, 112, 133, 1); --info-popup-background: rgba(239, 255, 252, 1); } @@ -175,6 +178,8 @@ --additional-background: rgb(52, 51, 51); --button-green: rgba(30, 113, 102, 1); --button-berry: rgba(151, 24, 87, 1); + --color-symbols: oklch(0.985 0 0); + --color-inline-error: #F4795B; --results-card-empty: oklch(0.985 0 0); --info-popup-background: rgba(239, 255, 252, 1); }