diff --git a/calc.js b/calc.js
index 67ff2d3..01d6647 100644
--- a/calc.js
+++ b/calc.js
@@ -13,7 +13,16 @@ function run_simulation(p) {
const months = p.years * 12;
// --- mortgage setup ---
- const loan = p.home_price * (1 - p.down_pct / 100);
+ const down_payment = p.home_price * (p.down_pct / 100);
+ const closing_costs = p.home_price * (p.closing_buy_pct / 100);
+ // if financed: closing costs rolled into loan, buyer only needs down payment upfront
+ const loan = p.finance_nebenkosten
+ ? p.home_price * (1 - p.down_pct / 100) + closing_costs
+ : p.home_price * (1 - p.down_pct / 100);
+ const upfront_buy = p.finance_nebenkosten
+ ? down_payment
+ : down_payment + closing_costs;
+
const r_m = p.interest_rate / 100 / 12;
const n = p.loan_years * 12;
let monthly_payment;
@@ -23,9 +32,6 @@ function run_simulation(p) {
monthly_payment = loan * (r_m * Math.pow(1 + r_m, n)) / (Math.pow(1 + r_m, n) - 1);
}
- // upfront costs for buyer
- const down_payment = p.home_price * (p.down_pct / 100);
- const upfront_buy = down_payment + p.home_price * (p.closing_buy_pct / 100);
const start_capital = p.start_capital || 0;
// --- state ---
@@ -53,6 +59,26 @@ function run_simulation(p) {
let breakeven_month = null;
+ // --- year 0 snapshot (day of purchase) ---
+ const selling_costs_0 = home_value * (p.closing_sell_pct / 100);
+ labels.push('Jahr 0');
+ buyer_nw_arr.push(Math.round(home_value - selling_costs_0 - balance + buyer_portfolio));
+ renter_nw_arr.push(Math.round(start_capital));
+ cum_buy_arr.push(Math.round(upfront_buy));
+ cum_rent_arr.push(0);
+ detail_arr.push({
+ house_value: Math.round(home_value),
+ mortgage_balance: Math.round(balance),
+ buyer_portfolio: Math.round(buyer_portfolio),
+ yr_interest: 0,
+ yr_principal: 0,
+ yr_upkeep: 0,
+ yr_rent: 0,
+ yr_renters_ins: 0,
+ kaufnebenkosten: Math.round(closing_costs),
+ finance_nebenkosten: p.finance_nebenkosten,
+ });
+
const invest_r_m = p.invest_return / 100 / 12;
const app_r_m = p.appreciation / 100 / 12;
const rent_inc_m = p.rent_increase / 100 / 12;
diff --git a/index.html b/index.html
index 75eaa7b..c867509 100644
--- a/index.html
+++ b/index.html
@@ -120,6 +120,17 @@
+
+
+
+
+
diff --git a/style.css b/style.css
index 1e7bd1f..6abce0c 100644
--- a/style.css
+++ b/style.css
@@ -201,6 +201,51 @@ label {
.unit.prefix { color: var(--text-muted); }
+/* ===== Toggle switch ===== */
+.toggle-group {
+ flex-direction: row;
+ align-items: center;
+ justify-content: space-between;
+}
+
+.toggle-label {
+ display: flex;
+ flex-direction: column;
+ gap: 0.1rem;
+ color: var(--text-muted);
+ font-size: 0.82rem;
+ font-weight: 500;
+ cursor: pointer;
+}
+
+.toggle-switch { cursor: pointer; flex-shrink: 0; }
+.toggle-switch input { display: none; }
+
+.toggle-track {
+ display: block;
+ width: 40px;
+ height: 22px;
+ background: rgba(255,255,255,0.1);
+ border-radius: 99px;
+ position: relative;
+ transition: background 0.2s;
+}
+
+.toggle-thumb {
+ position: absolute;
+ top: 3px; left: 3px;
+ width: 16px; height: 16px;
+ background: var(--text-muted);
+ border-radius: 50%;
+ transition: transform 0.2s, background 0.2s;
+}
+
+.toggle-switch input:checked + .toggle-track { background: var(--buy-muted); }
+.toggle-switch input:checked + .toggle-track .toggle-thumb {
+ transform: translateX(18px);
+ background: var(--buy);
+}
+
/* ===== Range slider styling ===== */
input[type="range"] {
-webkit-appearance: none;
diff --git a/ui.js b/ui.js
index d0deb0b..0afdb46 100644
--- a/ui.js
+++ b/ui.js
@@ -24,7 +24,8 @@ const get_params = () => ({
monthly_rent: +document.getElementById('monthly_rent').value,
rent_increase: +document.getElementById('rent_increase').value,
renters_ins: +document.getElementById('renters_ins').value,
- start_capital: +document.getElementById('start_capital').value,
+ start_capital: +document.getElementById('start_capital').value,
+ finance_nebenkosten: document.getElementById('finance_nebenkosten').checked,
});
// ===== Charts =====
@@ -206,12 +207,22 @@ const build_table = (result) => {
const td_buy_cost = document.createElement('td');
td_buy_cost.textContent = fmt_eur(result.cum_buy_arr[i]);
td_buy_cost.classList.add('has-tip');
- td_buy_cost.addEventListener('mouseenter', (e) => show_tooltip(e, [
- `dieses Jahr`,
- tip_row('Zinsen', d.yr_interest, '#f59e0b'),
- tip_row('Tilgung', d.yr_principal, '#f59e0b'),
- tip_row('Nebenkosten', d.yr_upkeep, '#f59e0b'),
- ].join('
')));
+ td_buy_cost.addEventListener('mouseenter', (e) => {
+ const rows = i === 0
+ ? [
+ `Kauftag`,
+ tip_row('Eigenkapital', result.cum_buy_arr[0] - (d.finance_nebenkosten ? 0 : d.kaufnebenkosten), '#f59e0b'),
+ tip_row('Kaufnebenkosten', d.kaufnebenkosten, d.finance_nebenkosten ? '#8b949e' : '#f59e0b'),
+ d.finance_nebenkosten ? `↳ mitfinanziert` : null,
+ ].filter(Boolean)
+ : [
+ `dieses Jahr`,
+ tip_row('Zinsen', d.yr_interest, '#f59e0b'),
+ tip_row('Tilgung', d.yr_principal, '#f59e0b'),
+ tip_row('Nebenkosten', d.yr_upkeep, '#f59e0b'),
+ ];
+ show_tooltip(e, rows.join('
'));
+ });
const td_rent_cost = document.createElement('td');
td_rent_cost.textContent = fmt_eur(result.cum_rent_arr[i]);
@@ -304,6 +315,8 @@ const SLIDER_IDS = [
SLIDER_IDS.forEach(wire_slider);
// wire plain number inputs (no slider)
+document.getElementById('finance_nebenkosten').addEventListener('change', recalc);
+
['home_price', 'insurance', 'monthly_rent', 'renters_ins', 'start_capital'].forEach((id) => {
document.getElementById(id).addEventListener('input', recalc);
});