@@ -151,25 +151,25 @@ export default function ConformingStatus({
)}
- {/* Suggestion to increase down payment */}
- {suggestion && (
+ {/* Conforming option — down payment to avoid jumbo */}
+ {conformingOption && (
- Increase down payment to{" "}
+ A down payment of{" "}
- {suggestion.requiredDownPercent.toFixed(1)}%
+ {conformingOption.requiredDownPercent.toFixed(1)}%
- {" "}({fmt(suggestion.requiredDownAmount)}) to stay conforming.
+ {" "}({fmt(conformingOption.requiredDownAmount)}) would keep the loan conforming.
- +{fmt(suggestion.additionalDown)} more needed
+ +{fmt(conformingOption.additionalDown)} more needed
diff --git a/src/lib/__tests__/loanLimits.test.js b/src/lib/__tests__/loanLimits.test.js
index 80ac4d7..cd6f0fb 100644
--- a/src/lib/__tests__/loanLimits.test.js
+++ b/src/lib/__tests__/loanLimits.test.js
@@ -1,7 +1,7 @@
import { describe, it, expect } from "vitest";
import { getConformingLimit, BASELINE_LIMIT } from "../../data/conformingLimits.js";
import {
- detectJumbo, calcEffectiveRate, suggestConformingDown, calcJumboImpact,
+ detectJumbo, calcEffectiveRate, calcConformingDown, calcJumboImpact,
DEFAULT_JUMBO_PREMIUM,
} from "../loanLimits.js";
@@ -104,21 +104,21 @@ describe("calcEffectiveRate", () => {
});
});
-// ── suggestConformingDown ────────────────────────────────────────────────────
+// ── calcConformingDown ────────────────────────────────────────────────────
-describe("suggestConformingDown", () => {
+describe("calcConformingDown", () => {
it("returns null when loan is already conforming", () => {
// 500K home, 20% down = 400K loan, well under 832,750
- expect(suggestConformingDown(500000, BASELINE_LIMIT, 20)).toBeNull();
+ expect(calcConformingDown(500000, BASELINE_LIMIT, 20)).toBeNull();
});
it("returns null when home price is below limit", () => {
- expect(suggestConformingDown(700000, BASELINE_LIMIT, 0)).toBeNull();
+ expect(calcConformingDown(700000, BASELINE_LIMIT, 0)).toBeNull();
});
- it("suggests increased down payment for jumbo loan", () => {
+ it("calculates required down payment for jumbo loan", () => {
// 1M home, 832,750 limit, 10% down = 900K loan (67,250 over limit)
- const result = suggestConformingDown(1000000, BASELINE_LIMIT, 10);
+ const result = calcConformingDown(1000000, BASELINE_LIMIT, 10);
expect(result).not.toBeNull();
// Required down = 1M - 832,750 = 167,250 = 16.725% → rounds up to 16.8%
expect(result.requiredDownPercent).toBeCloseTo(16.8, 0);
@@ -127,8 +127,8 @@ describe("suggestConformingDown", () => {
});
it("returns null for zero/invalid inputs", () => {
- expect(suggestConformingDown(0, BASELINE_LIMIT, 20)).toBeNull();
- expect(suggestConformingDown(-100, BASELINE_LIMIT, 20)).toBeNull();
+ expect(calcConformingDown(0, BASELINE_LIMIT, 20)).toBeNull();
+ expect(calcConformingDown(-100, BASELINE_LIMIT, 20)).toBeNull();
});
});
diff --git a/src/lib/__tests__/purchasePlanner.test.js b/src/lib/__tests__/purchasePlanner.test.js
index 8d2f914..2bce6fe 100644
--- a/src/lib/__tests__/purchasePlanner.test.js
+++ b/src/lib/__tests__/purchasePlanner.test.js
@@ -148,20 +148,20 @@ describe("calcLiquidationAnalysis", () => {
cashFlow: { net: cfNet },
});
- it("returns canAfford=true when surplus", () => {
+ it("returns isCovered=true when surplus", () => {
const summary = makeSummary(20000, 80000, 5000, 10000, 5000);
// Available: 20k + (80k - 5k) + 10k + 5k = 110k
const result = calcLiquidationAnalysis(90000, summary);
- expect(result.canAfford).toBe(true);
+ expect(result.isCovered).toBe(true);
expect(result.surplus).toBe(20000);
expect(result.shortfall).toBe(0);
});
- it("returns canAfford=false when shortfall", () => {
+ it("returns isCovered=false when shortfall", () => {
const summary = makeSummary(5000, 20000, 3000, 0, 2000);
// Available: 5k + (20k - 3k) + 0 + 2k = 24k
const result = calcLiquidationAnalysis(50000, summary);
- expect(result.canAfford).toBe(false);
+ expect(result.isCovered).toBe(false);
expect(result.shortfall).toBe(26000);
expect(result.surplus).toBe(0);
});
@@ -177,7 +177,7 @@ describe("calcLiquidationAnalysis", () => {
it("handles zero values gracefully", () => {
const summary = makeSummary(0, 0, 0, 0, 0);
const result = calcLiquidationAnalysis(10000, summary);
- expect(result.canAfford).toBe(false);
+ expect(result.isCovered).toBe(false);
expect(result.totalAvailable).toBe(0);
expect(result.shortfall).toBe(10000);
});
@@ -186,7 +186,7 @@ describe("calcLiquidationAnalysis", () => {
// Simulate summary with missing nested properties
const sparse = { cashTotal: 5000 };
const result = calcLiquidationAnalysis(10000, sparse);
- expect(result.canAfford).toBe(false);
+ expect(result.isCovered).toBe(false);
expect(result.totalAvailable).toBe(5000);
expect(Number.isNaN(result.totalAvailable)).toBe(false);
expect(Number.isNaN(result.shortfall)).toBe(false);
diff --git a/src/lib/loanLimits.js b/src/lib/loanLimits.js
index 079bcd0..544e136 100644
--- a/src/lib/loanLimits.js
+++ b/src/lib/loanLimits.js
@@ -5,10 +5,9 @@
* conforming loan limit) and calculates the rate/payment impact.
*/
-import { getConformingLimit, BASELINE_LIMIT } from "../data/conformingLimits.js";
+import { getConformingLimit } from "../data/conformingLimits.js";
+export { BASELINE_LIMIT } from "../data/conformingLimits.js";
import { calcMonthlyPI } from "./mortgageCalc.js";
-
-export { BASELINE_LIMIT };
export const DEFAULT_JUMBO_PREMIUM = 0.25; // percentage points above conforming rate
/**
@@ -43,14 +42,14 @@ export function calcEffectiveRate(baseRatePercent, isJumbo, jumboSpreadPercent =
}
/**
- * Suggest a down payment increase to stay under the conforming limit.
+ * Calculate the down payment needed to stay under the conforming limit.
* Returns null if the loan is already conforming.
* @param {number} homePrice
* @param {number} conformingLimit
* @param {number} currentDownPercent
* @returns {{ requiredDownPercent: number, requiredDownAmount: number, additionalDown: number } | null}
*/
-export function suggestConformingDown(homePrice, conformingLimit, currentDownPercent) {
+export function calcConformingDown(homePrice, conformingLimit, currentDownPercent) {
if (!homePrice || homePrice <= 0 || !conformingLimit) return null;
const currentDown = homePrice * (currentDownPercent / 100);
diff --git a/src/lib/mortgageCalc.js b/src/lib/mortgageCalc.js
index 1dacf8a..96032c0 100644
--- a/src/lib/mortgageCalc.js
+++ b/src/lib/mortgageCalc.js
@@ -144,18 +144,18 @@ export function calcPointsBreakEven(pointsCost, monthlySavings, opportunityRateP
}
/**
- * Buy-down recommendation signal based on break-even vs expected stay.
+ * Buy-down break-even signal based on break-even vs expected stay.
* green: adjusted break-even <= 60% of stay (clear win)
* yellow: 60-100% of stay (marginal)
- * red: > stay or Infinity (don't buy down)
+ * red: > stay or Infinity (unlikely to break even)
*/
export function calcBuyDownSignal(adjustedBreakEvenMonths, expectedStayYears) {
const stayMonths = expectedStayYears * 12;
if (adjustedBreakEvenMonths === Infinity || adjustedBreakEvenMonths > stayMonths) {
- return { signal: "red", label: "Skip the buy-down, invest instead" };
+ return { signal: "red", label: "Buy-down unlikely to break even" };
}
if (adjustedBreakEvenMonths <= stayMonths * 0.6) {
- return { signal: "green", label: "Buy down the rate" };
+ return { signal: "green", label: "Buy-down likely to break even" };
}
return { signal: "yellow", label: "Marginal \u2014 depends on priorities" };
}
diff --git a/src/lib/purchasePlanner.js b/src/lib/purchasePlanner.js
index 61a2a8d..5488b88 100644
--- a/src/lib/purchasePlanner.js
+++ b/src/lib/purchasePlanner.js
@@ -101,12 +101,12 @@ function fmtShortfall(n) {
// ─── Public API ───────────────────────────────────────────────────────────────
/**
- * Liquidation analysis — can the user afford the purchase with current assets?
+ * Liquidation analysis — do available funds cover the purchase at current asset values?
* Uses calcSummary output to determine available cash from all sources.
*
* @param {number} cashNeeded - total cash needed from calcTotalCashNeeded
* @param {Object} summary - result of calcSummary(state, prices)
- * @returns {{ canAfford, surplus, shortfall, cashContribution, assetContribution, retirementContribution, cashFlowContribution, totalAvailable }}
+ * @returns {{ isCovered, surplus, shortfall, cashContribution, assetContribution, retirementContribution, cashFlowContribution, totalAvailable }}
*/
export function calcLiquidationAnalysis(cashNeeded, summary) {
const cashAvailable = summary.cashTotal || 0;
@@ -120,7 +120,7 @@ export function calcLiquidationAnalysis(cashNeeded, summary) {
const surplus = totalAvailable - cashNeeded;
return {
- canAfford: surplus >= 0,
+ isCovered: surplus >= 0,
surplus: Math.max(0, surplus),
shortfall: Math.max(0, -surplus),
cashContribution: cashAvailable,
diff --git a/src/lib/readiness.js b/src/lib/readiness.js
index af7b8a3..4df00f4 100644
--- a/src/lib/readiness.js
+++ b/src/lib/readiness.js
@@ -1,6 +1,6 @@
-// Financial readiness projection engine
-// Projects cash position forward month-by-month to determine when the user
-// can afford a target purchase amount. Default: 0% growth (pure linear).
+// Purchase readiness projection engine
+// Projects cash position forward month-by-month to determine when available
+// funds meet a target purchase amount. Default: 0% growth (pure linear).
import { calcSummary, calcMonthlySavings, isLongTerm } from "./calculations.js";
diff --git a/src/pages/Dashboard.jsx b/src/pages/Dashboard.jsx
index 5037b3f..f1f1f80 100644
--- a/src/pages/Dashboard.jsx
+++ b/src/pages/Dashboard.jsx
@@ -332,10 +332,10 @@ function ReadinessWidget({ state, projections, statusResult, cashNeeded, monthly
{label}
{isReady
- ? "Ready now"
+ ? "Funds available"
: readinessDate
- ? `Ready in ~${readinessDate.month} months`
- : "Not reachable in 5 years"}
+ ? `Estimated in ~${readinessDate.month} months`
+ : "Not estimated within 5 years"}
diff --git a/src/pages/LoansCalc.jsx b/src/pages/LoansCalc.jsx
index 9656df6..607494e 100644
--- a/src/pages/LoansCalc.jsx
+++ b/src/pages/LoansCalc.jsx
@@ -179,7 +179,7 @@ function MortgageView({