@jjlmoya/utils-finance 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. package/package.json +64 -0
  2. package/src/category/i18n/en.ts +159 -0
  3. package/src/category/i18n/es.ts +159 -0
  4. package/src/category/i18n/fr.ts +159 -0
  5. package/src/category/index.ts +24 -0
  6. package/src/category/seo.astro +15 -0
  7. package/src/components/PreviewNavSidebar.astro +116 -0
  8. package/src/components/PreviewToolbar.astro +143 -0
  9. package/src/data.ts +11 -0
  10. package/src/env.d.ts +5 -0
  11. package/src/index.ts +32 -0
  12. package/src/layouts/PreviewLayout.astro +117 -0
  13. package/src/pages/[locale]/[slug].astro +146 -0
  14. package/src/pages/[locale].astro +251 -0
  15. package/src/pages/index.astro +4 -0
  16. package/src/tests/faq_count.test.ts +19 -0
  17. package/src/tests/locale_completeness.test.ts +39 -0
  18. package/src/tests/mocks/astro_mock.js +2 -0
  19. package/src/tests/no_h1_in_components.test.ts +48 -0
  20. package/src/tests/schemas_fulfillment.test.ts +23 -0
  21. package/src/tests/seo_length.test.ts +23 -0
  22. package/src/tests/title_quality.test.ts +55 -0
  23. package/src/tests/tool_validation.test.ts +17 -0
  24. package/src/tool/compoundInterest/bibliography.astro +14 -0
  25. package/src/tool/compoundInterest/component.astro +697 -0
  26. package/src/tool/compoundInterest/i18n/en.ts +212 -0
  27. package/src/tool/compoundInterest/i18n/es.ts +212 -0
  28. package/src/tool/compoundInterest/i18n/fr.ts +212 -0
  29. package/src/tool/compoundInterest/index.ts +34 -0
  30. package/src/tool/compoundInterest/logic.ts +40 -0
  31. package/src/tool/compoundInterest/seo.astro +14 -0
  32. package/src/tool/compoundInterest/ui.ts +19 -0
  33. package/src/tool/courtFeeCalculator/bibliography.astro +14 -0
  34. package/src/tool/courtFeeCalculator/component.astro +427 -0
  35. package/src/tool/courtFeeCalculator/i18n/en.ts +268 -0
  36. package/src/tool/courtFeeCalculator/i18n/es.ts +268 -0
  37. package/src/tool/courtFeeCalculator/i18n/fr.ts +268 -0
  38. package/src/tool/courtFeeCalculator/index.ts +33 -0
  39. package/src/tool/courtFeeCalculator/logic.ts +86 -0
  40. package/src/tool/courtFeeCalculator/seo.astro +14 -0
  41. package/src/tool/courtFeeCalculator/ui.ts +31 -0
  42. package/src/tool/debtSnowball/bibliography.astro +14 -0
  43. package/src/tool/debtSnowball/component.astro +1052 -0
  44. package/src/tool/debtSnowball/i18n/en.ts +288 -0
  45. package/src/tool/debtSnowball/i18n/es.ts +288 -0
  46. package/src/tool/debtSnowball/i18n/fr.ts +288 -0
  47. package/src/tool/debtSnowball/index.ts +37 -0
  48. package/src/tool/debtSnowball/logic.ts +156 -0
  49. package/src/tool/debtSnowball/seo.astro +14 -0
  50. package/src/tool/debtSnowball/ui.ts +49 -0
  51. package/src/tool/fireCalculator/bibliography.astro +14 -0
  52. package/src/tool/fireCalculator/component.astro +689 -0
  53. package/src/tool/fireCalculator/i18n/en.ts +261 -0
  54. package/src/tool/fireCalculator/i18n/es.ts +261 -0
  55. package/src/tool/fireCalculator/i18n/fr.ts +261 -0
  56. package/src/tool/fireCalculator/index.ts +33 -0
  57. package/src/tool/fireCalculator/logic.ts +108 -0
  58. package/src/tool/fireCalculator/seo.astro +14 -0
  59. package/src/tool/fireCalculator/ui.ts +33 -0
  60. package/src/tool/ibanBicSwiftConverter/bibliography.astro +14 -0
  61. package/src/tool/ibanBicSwiftConverter/component.astro +581 -0
  62. package/src/tool/ibanBicSwiftConverter/i18n/en.ts +167 -0
  63. package/src/tool/ibanBicSwiftConverter/i18n/es.ts +216 -0
  64. package/src/tool/ibanBicSwiftConverter/i18n/fr.ts +167 -0
  65. package/src/tool/ibanBicSwiftConverter/index.ts +33 -0
  66. package/src/tool/ibanBicSwiftConverter/logic.ts +101 -0
  67. package/src/tool/ibanBicSwiftConverter/seo.astro +14 -0
  68. package/src/tool/ibanBicSwiftConverter/ui.ts +17 -0
  69. package/src/tool/inflation/bibliography.astro +14 -0
  70. package/src/tool/inflation/component.astro +400 -0
  71. package/src/tool/inflation/i18n/en.ts +301 -0
  72. package/src/tool/inflation/i18n/es.ts +301 -0
  73. package/src/tool/inflation/i18n/fr.ts +301 -0
  74. package/src/tool/inflation/index.ts +34 -0
  75. package/src/tool/inflation/logic.ts +92 -0
  76. package/src/tool/inflation/seo.astro +14 -0
  77. package/src/tool/inflation/ui.ts +17 -0
  78. package/src/tool/lateInterest/bibliography.astro +14 -0
  79. package/src/tool/lateInterest/component.astro +655 -0
  80. package/src/tool/lateInterest/i18n/en.ts +255 -0
  81. package/src/tool/lateInterest/i18n/es.ts +224 -0
  82. package/src/tool/lateInterest/i18n/fr.ts +224 -0
  83. package/src/tool/lateInterest/index.ts +34 -0
  84. package/src/tool/lateInterest/logic.ts +89 -0
  85. package/src/tool/lateInterest/seo.astro +14 -0
  86. package/src/tool/lateInterest/ui.ts +33 -0
  87. package/src/tool/legalInterestRate/bibliography.astro +14 -0
  88. package/src/tool/legalInterestRate/component.astro +650 -0
  89. package/src/tool/legalInterestRate/i18n/en.ts +159 -0
  90. package/src/tool/legalInterestRate/i18n/es.ts +159 -0
  91. package/src/tool/legalInterestRate/i18n/fr.ts +159 -0
  92. package/src/tool/legalInterestRate/index.ts +33 -0
  93. package/src/tool/legalInterestRate/logic.ts +29 -0
  94. package/src/tool/legalInterestRate/seo.astro +14 -0
  95. package/src/tool/legalInterestRate/ui.ts +23 -0
  96. package/src/tool/lotteryOptimizer/bibliography.astro +14 -0
  97. package/src/tool/lotteryOptimizer/component.astro +863 -0
  98. package/src/tool/lotteryOptimizer/i18n/en.ts +188 -0
  99. package/src/tool/lotteryOptimizer/i18n/es.ts +281 -0
  100. package/src/tool/lotteryOptimizer/i18n/fr.ts +188 -0
  101. package/src/tool/lotteryOptimizer/index.ts +33 -0
  102. package/src/tool/lotteryOptimizer/logic.ts +133 -0
  103. package/src/tool/lotteryOptimizer/seo.astro +14 -0
  104. package/src/tool/lotteryOptimizer/ui.ts +33 -0
  105. package/src/tool/mortgage/bibliography.astro +14 -0
  106. package/src/tool/mortgage/component.astro +1030 -0
  107. package/src/tool/mortgage/i18n/en.ts +227 -0
  108. package/src/tool/mortgage/i18n/es.ts +227 -0
  109. package/src/tool/mortgage/i18n/fr.ts +227 -0
  110. package/src/tool/mortgage/index.ts +34 -0
  111. package/src/tool/mortgage/logic.ts +118 -0
  112. package/src/tool/mortgage/seo.astro +14 -0
  113. package/src/tool/mortgage/ui.ts +35 -0
  114. package/src/tool/percentageCalculator/bibliography.astro +14 -0
  115. package/src/tool/percentageCalculator/component.astro +640 -0
  116. package/src/tool/percentageCalculator/i18n/en.ts +233 -0
  117. package/src/tool/percentageCalculator/i18n/es.ts +233 -0
  118. package/src/tool/percentageCalculator/i18n/fr.ts +233 -0
  119. package/src/tool/percentageCalculator/index.ts +34 -0
  120. package/src/tool/percentageCalculator/logic.ts +48 -0
  121. package/src/tool/percentageCalculator/seo.astro +14 -0
  122. package/src/tool/percentageCalculator/ui.ts +30 -0
  123. package/src/tool/rentIncreaseCalculator/bibliography.astro +14 -0
  124. package/src/tool/rentIncreaseCalculator/component.astro +587 -0
  125. package/src/tool/rentIncreaseCalculator/i18n/en.ts +159 -0
  126. package/src/tool/rentIncreaseCalculator/i18n/es.ts +220 -0
  127. package/src/tool/rentIncreaseCalculator/i18n/fr.ts +159 -0
  128. package/src/tool/rentIncreaseCalculator/index.ts +33 -0
  129. package/src/tool/rentIncreaseCalculator/logic.ts +43 -0
  130. package/src/tool/rentIncreaseCalculator/seo.astro +14 -0
  131. package/src/tool/rentIncreaseCalculator/ui.ts +19 -0
  132. package/src/tools.ts +16 -0
  133. package/src/types.ts +74 -0
@@ -0,0 +1,1030 @@
1
+ ---
2
+ import type { MortgageUI } from './ui';
3
+ import { MortgageCalculator } from './logic';
4
+
5
+ interface Props {
6
+ ui: MortgageUI;
7
+ }
8
+
9
+ const { ui } = Astro.props;
10
+
11
+ const defaultInput = {
12
+ amount: 200000,
13
+ annualInterestRate: 3.5,
14
+ years: 30,
15
+ monthlyExtraPayment: 0,
16
+ };
17
+
18
+ MortgageCalculator.calculate(defaultInput);
19
+ ---
20
+
21
+ <div class="mortgage-container" data-currency-locale={ui.currencyLocale} data-currency-code={ui.currencyCode} data-currency-symbol={ui.currencySymbol} data-year-plural={ui.yearPlural}>
22
+ <div class="mortgage-header">
23
+ <div class="mortgage-header-title-group">
24
+ <div class="mortgage-header-icon">
25
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"></rect><path d="M7 11V7a5 5 0 0 1 10 0v4"></path></svg>
26
+ </div>
27
+ <h2 class="mortgage-header-title">{ui.labelTitle}</h2>
28
+ </div>
29
+ </div>
30
+
31
+ <div class="mortgage-grid">
32
+ <div class="mortgage-left">
33
+ <div class="mortgage-input-group">
34
+ <label class="mortgage-label">{ui.labelLoanAmount}</label>
35
+ <div class="mortgage-input-wrapper">
36
+ <span class="mortgage-input-prefix">{ui.currencySymbol}</span>
37
+ <input
38
+ type="number"
39
+ id="mortgage-amount"
40
+ value={defaultInput.amount}
41
+ class="mortgage-input mortgage-input-prefixed"
42
+ />
43
+ </div>
44
+ </div>
45
+
46
+ <div class="mortgage-input-row">
47
+ <div class="mortgage-input-group">
48
+ <label class="mortgage-label">{ui.labelInterestRate}</label>
49
+ <div class="mortgage-input-wrapper">
50
+ <input
51
+ type="number"
52
+ id="mortgage-rate"
53
+ value={defaultInput.annualInterestRate}
54
+ step="0.01"
55
+ class="mortgage-input mortgage-input-suffixed"
56
+ />
57
+ <span class="mortgage-input-suffix">%</span>
58
+ </div>
59
+ </div>
60
+ <div class="mortgage-input-group">
61
+ <label class="mortgage-label">{ui.labelYears}</label>
62
+ <input
63
+ type="number"
64
+ id="mortgage-years"
65
+ value={defaultInput.years}
66
+ class="mortgage-input"
67
+ />
68
+ </div>
69
+ </div>
70
+
71
+ <div class="mortgage-divider"></div>
72
+
73
+ <div class="mortgage-input-group">
74
+ <label class="mortgage-label mortgage-label-extra">
75
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2v20"></path><path d="m17 5-5-5-5 5"></path><path d="m5 19 5 5 5-5"></path></svg>
76
+ {ui.labelMonthlyExtra}
77
+ </label>
78
+ <div class="mortgage-input-wrapper">
79
+ <span class="mortgage-input-prefix">{ui.currencySymbol}</span>
80
+ <input
81
+ type="number"
82
+ id="mortgage-extra"
83
+ value="0"
84
+ placeholder="0"
85
+ class="mortgage-input mortgage-input-prefixed mortgage-input-extra"
86
+ />
87
+ </div>
88
+ <p class="mortgage-input-hint">Enter an amount to see how much time and interest you save.</p>
89
+ </div>
90
+
91
+ <div class="mortgage-payment-card">
92
+ <span class="mortgage-payment-label">{ui.labelMonthlyPayment}</span>
93
+ <span id="mortgage-monthly" class="mortgage-payment-value">0</span>
94
+ <span class="mortgage-payment-duration">
95
+ During <span id="mortgage-months-count">0</span> {ui.monthPlural}
96
+ </span>
97
+ </div>
98
+
99
+ <div id="mortgage-savings-card" class="mortgage-savings-card mortgage-savings-inactive">
100
+ <h4 class="mortgage-savings-title">
101
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2v20"></path><path d="M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6"></path></svg>
102
+ {ui.labelSavingsCard}
103
+ </h4>
104
+ <div class="mortgage-savings-grid">
105
+ <div class="mortgage-savings-item">
106
+ <span class="mortgage-savings-label">{ui.labelSavingsInterest}</span>
107
+ <span id="mortgage-saved-interest" class="mortgage-savings-value">0</span>
108
+ </div>
109
+ <div class="mortgage-savings-item">
110
+ <span class="mortgage-savings-label">{ui.labelSavingsTime}</span>
111
+ <span id="mortgage-saved-time" class="mortgage-savings-value">0 {ui.yearPlural}</span>
112
+ </div>
113
+ </div>
114
+ </div>
115
+ </div>
116
+
117
+ <div class="mortgage-right">
118
+ <div class="mortgage-section">
119
+ <h3 class="mortgage-section-title">{ui.labelTimelineComparison}</h3>
120
+ <div class="mortgage-timeline">
121
+ <div class="mortgage-timeline-item">
122
+ <div class="mortgage-timeline-header">
123
+ <span class="mortgage-timeline-label">{ui.labelTimelineOriginal}</span>
124
+ <span id="mortgage-timeline-original-text">30 {ui.yearPlural}</span>
125
+ </div>
126
+ <div class="mortgage-timeline-bar">
127
+ <div class="mortgage-timeline-fill"></div>
128
+ </div>
129
+ </div>
130
+
131
+ <div class="mortgage-timeline-item">
132
+ <div class="mortgage-timeline-header">
133
+ <span class="mortgage-timeline-label mortgage-timeline-label-optimized">{ui.labelTimelineOptimized}</span>
134
+ <span id="mortgage-timeline-optimized-text">30 {ui.yearPlural}</span>
135
+ </div>
136
+ <div class="mortgage-timeline-bar">
137
+ <div id="mortgage-timeline-optimized-bar" class="mortgage-timeline-fill mortgage-timeline-fill-optimized"></div>
138
+ </div>
139
+ </div>
140
+ </div>
141
+ </div>
142
+
143
+ <div class="mortgage-section-divider"></div>
144
+
145
+ <div class="mortgage-section">
146
+ <h3 class="mortgage-section-title">{ui.labelCostBreakdown}</h3>
147
+
148
+ <div class="mortgage-breakdown-bar">
149
+ <div
150
+ id="mortgage-bar-principal"
151
+ class="mortgage-breakdown-principal"
152
+ style="width: 70%"
153
+ >
154
+ <span class="mortgage-breakdown-label">{ui.labelBorrowed}</span>
155
+ <span id="mortgage-bar-principal-val" class="mortgage-breakdown-value"></span>
156
+ </div>
157
+ <div
158
+ id="mortgage-bar-interest"
159
+ class="mortgage-breakdown-interest"
160
+ style="width: 30%"
161
+ >
162
+ <span class="mortgage-breakdown-label">{ui.labelTableInterest}</span>
163
+ <span id="mortgage-bar-interest-val" class="mortgage-breakdown-value"></span>
164
+ </div>
165
+ </div>
166
+
167
+ <div class="mortgage-breakdown-summary">
168
+ <div class="mortgage-breakdown-item">
169
+ {ui.labelTotalBorrowed}: <span id="mortgage-total-amount" class="mortgage-breakdown-emphasis">0</span>
170
+ </div>
171
+ <div class="mortgage-breakdown-item">
172
+ {ui.labelTotalInterest}: <span id="mortgage-total-interest" class="mortgage-breakdown-emphasis mortgage-breakdown-interest-emphasis">0</span>
173
+ </div>
174
+ </div>
175
+ </div>
176
+
177
+ <div class="mortgage-section-divider"></div>
178
+
179
+ <div class="mortgage-table-container">
180
+ <div class="mortgage-table-header">
181
+ <h3 class="mortgage-table-title">{ui.labelAmortizationTable}</h3>
182
+ <button id="mortgage-toggle-table-btn" class="mortgage-toggle-btn">
183
+ {ui.labelTableHeader}
184
+ </button>
185
+ </div>
186
+
187
+ <div class="mortgage-table-scroll">
188
+ <table class="mortgage-table">
189
+ <thead>
190
+ <tr>
191
+ <th class="mortgage-table-cell">{ui.labelTableMonth}</th>
192
+ <th class="mortgage-table-cell mortgage-table-interest">{ui.labelTableInterest}</th>
193
+ <th class="mortgage-table-cell mortgage-table-principal">{ui.labelTablePrincipal}</th>
194
+ <th class="mortgage-table-cell mortgage-table-extra">{ui.labelTableExtra}</th>
195
+ <th class="mortgage-table-cell mortgage-table-balance">{ui.labelTableBalance}</th>
196
+ </tr>
197
+ </thead>
198
+ <tbody id="mortgage-table-body">
199
+ </tbody>
200
+ </table>
201
+ </div>
202
+ </div>
203
+ </div>
204
+ </div>
205
+ </div>
206
+
207
+ <script>
208
+ import { MortgageCalculator } from './logic';
209
+ import type { MortgageResult } from './logic';
210
+
211
+ const container = document.querySelector('.mortgage-container') as HTMLElement;
212
+ const currencyLocale = container.dataset.currencyLocale || 'en-US';
213
+ const currencyCode = container.dataset.currencyCode || 'USD';
214
+ const yearPlural = container.dataset.yearPlural || 'years';
215
+
216
+ const amountEl = document.getElementById('mortgage-amount') as HTMLInputElement;
217
+ const rateEl = document.getElementById('mortgage-rate') as HTMLInputElement;
218
+ const yearsEl = document.getElementById('mortgage-years') as HTMLInputElement;
219
+ const extraEl = document.getElementById('mortgage-extra') as HTMLInputElement;
220
+
221
+ const monthlyEl = document.getElementById('mortgage-monthly');
222
+ const totalAmountEl = document.getElementById('mortgage-total-amount');
223
+ const totalInterestEl = document.getElementById('mortgage-total-interest');
224
+ const monthsCountEl = document.getElementById('mortgage-months-count');
225
+ const tableBody = document.getElementById('mortgage-table-body');
226
+
227
+ const savingsCard = document.getElementById('mortgage-savings-card');
228
+ const savedInterestEl = document.getElementById('mortgage-saved-interest');
229
+ const savedTimeEl = document.getElementById('mortgage-saved-time');
230
+
231
+ const timelineOriginalText = document.getElementById('mortgage-timeline-original-text');
232
+ const timelineOptimizedText = document.getElementById('mortgage-timeline-optimized-text');
233
+ const timelineOptimizedBar = document.getElementById('mortgage-timeline-optimized-bar');
234
+
235
+ const barPrincipal = document.getElementById('mortgage-bar-principal');
236
+ const barPrincipalVal = document.getElementById('mortgage-bar-principal-val');
237
+ const barInterest = document.getElementById('mortgage-bar-interest');
238
+ const barInterestVal = document.getElementById('mortgage-bar-interest-val');
239
+
240
+ const toggleTableBtn = document.getElementById('mortgage-toggle-table-btn');
241
+ let isTableExpanded = false;
242
+ let currentResult: MortgageResult | null = null;
243
+
244
+ const formatMoney = (val: number) =>
245
+ new Intl.NumberFormat(currencyLocale, {
246
+ style: 'currency',
247
+ currency: currencyCode,
248
+ maximumFractionDigits: 0,
249
+ })
250
+ .format(val)
251
+ .replace(/\u00A0/g, ' ');
252
+
253
+ function renderTable(result: MortgageResult): void {
254
+ if (!tableBody) return;
255
+
256
+ const limit = 24;
257
+ const rows = isTableExpanded
258
+ ? result.amortizationTable
259
+ : result.amortizationTable.slice(0, limit);
260
+
261
+ const html = rows
262
+ .map(
263
+ (row) => `
264
+ <tr class="mortgage-table-row">
265
+ <td class="mortgage-table-cell">Month ${row.month}</td>
266
+ <td class="mortgage-table-cell mortgage-table-interest">${formatMoney(row.interest)}</td>
267
+ <td class="mortgage-table-cell mortgage-table-principal">${formatMoney(row.principal)}</td>
268
+ <td class="mortgage-table-cell mortgage-table-extra">${row.extraPayment > 0 ? formatMoney(row.extraPayment) : '<span class="mortgage-table-dash">-</span>'}</td>
269
+ <td class="mortgage-table-cell mortgage-table-balance">${formatMoney(row.remainingBalance)}</td>
270
+ </tr>
271
+ `
272
+ )
273
+ .join('');
274
+
275
+ let footer = '';
276
+ if (!isTableExpanded && result.amortizationTable.length > limit) {
277
+ const remaining = result.amortizationTable.length - limit;
278
+ footer = `<tr><td colspan="5" class="mortgage-table-footer">... and ${remaining} more ${remaining === 1 ? 'month' : 'months'}. <button class="mortgage-table-footer-btn" onclick="document.getElementById('mortgage-toggle-table-btn').click()">View all</button></td></tr>`;
279
+ } else if (isTableExpanded) {
280
+ footer = `<tr><td colspan="5" class="mortgage-table-footer mortgage-table-paid">Mortgage Paid Off</td></tr>`;
281
+ }
282
+
283
+ tableBody.innerHTML = html + footer;
284
+ }
285
+
286
+ interface MortgageInput {
287
+ amount: number;
288
+ annualInterestRate: number;
289
+ years: number;
290
+ monthlyExtraPayment: number;
291
+ }
292
+
293
+ function updateSummary(result: MortgageResult, input: MortgageInput): void {
294
+ if (monthlyEl) monthlyEl.innerText = formatMoney(result.monthlyPayment);
295
+ if (monthsCountEl) monthsCountEl.innerText = result.actualDurationMonths + '';
296
+
297
+ if (savingsCard) {
298
+ if (input.monthlyExtraPayment > 0) {
299
+ savingsCard.classList.remove('mortgage-savings-inactive');
300
+ savingsCard.classList.add('mortgage-savings-active');
301
+ } else {
302
+ savingsCard.classList.add('mortgage-savings-inactive');
303
+ savingsCard.classList.remove('mortgage-savings-active');
304
+ }
305
+ if (savedInterestEl) savedInterestEl.innerText = formatMoney(result.interestSaved);
306
+ if (savedTimeEl) savedTimeEl.innerText = result.yearsSaved + ' ' + yearPlural;
307
+ }
308
+ }
309
+
310
+ function updateTimeline(result: MortgageResult, input: MortgageInput): void {
311
+ if (timelineOriginalText)
312
+ timelineOriginalText.innerText = input.years + ' ' + yearPlural + ' (' + input.years * 12 + ' months)';
313
+ if (timelineOptimizedText)
314
+ timelineOptimizedText.innerText =
315
+ result.yearsSaved > 0
316
+ ? (result.actualDurationMonths / 12).toFixed(1) +
317
+ ' ' + yearPlural + ' (' +
318
+ result.actualDurationMonths +
319
+ ' months)'
320
+ : '-';
321
+
322
+ if (timelineOptimizedBar) {
323
+ const percent = (result.actualDurationMonths / (input.years * 12)) * 100;
324
+ timelineOptimizedBar.style.width = `${Math.min(percent, 100)}%`;
325
+ updateTimelineBar(result.yearsSaved > 0);
326
+ }
327
+ }
328
+
329
+ function updateTimelineBar(hasSavings: boolean): void {
330
+ if (timelineOptimizedBar) {
331
+ if (hasSavings) {
332
+ timelineOptimizedBar.classList.add('mortgage-timeline-active');
333
+ } else {
334
+ timelineOptimizedBar.classList.remove('mortgage-timeline-active');
335
+ }
336
+ }
337
+ }
338
+
339
+ function updateBreakdown(result: MortgageResult, input: MortgageInput): void {
340
+ if (totalAmountEl) totalAmountEl.innerText = formatMoney(input.amount);
341
+ if (totalInterestEl) totalInterestEl.innerText = formatMoney(result.totalInterest);
342
+
343
+ const grandTotal = input.amount + result.totalInterest;
344
+ const pPercent = grandTotal > 0 ? (input.amount / grandTotal) * 100 : 0;
345
+ const iPercent = grandTotal > 0 ? (result.totalInterest / grandTotal) * 100 : 0;
346
+
347
+ updateBarWidths(pPercent, iPercent, input.amount, result.totalInterest);
348
+ }
349
+
350
+ function updateBarWidths(pPercent: number, iPercent: number, amount: number, totalInterest: number): void {
351
+ if (barPrincipal) {
352
+ barPrincipal.style.width = `${pPercent}%`;
353
+ if (barPrincipalVal) barPrincipalVal.innerText = formatMoney(amount);
354
+ }
355
+ if (barInterest) {
356
+ barInterest.style.width = `${iPercent}%`;
357
+ if (barInterestVal) barInterestVal.innerText = formatMoney(totalInterest);
358
+ }
359
+ }
360
+
361
+ function updateMortgage(): void {
362
+ const input: MortgageInput = {
363
+ amount: Number(amountEl.value) || 0,
364
+ annualInterestRate: Number(rateEl.value) || 0,
365
+ years: Number(yearsEl.value) || 0,
366
+ monthlyExtraPayment: Number(extraEl?.value) || 0,
367
+ };
368
+
369
+ const result = MortgageCalculator.calculate(input);
370
+ currentResult = result;
371
+
372
+ updateSummary(result, input);
373
+ updateTimeline(result, input);
374
+ updateBreakdown(result, input);
375
+ renderTable(result);
376
+ }
377
+
378
+ toggleTableBtn?.addEventListener('click', () => {
379
+ isTableExpanded = !isTableExpanded;
380
+ if (toggleTableBtn)
381
+ toggleTableBtn.innerText = isTableExpanded ? 'Collapse table' : 'View full table';
382
+ if (currentResult) renderTable(currentResult);
383
+ });
384
+
385
+ [amountEl, rateEl, yearsEl, extraEl].forEach((el) => el?.addEventListener('input', updateMortgage));
386
+
387
+ updateMortgage();
388
+ </script>
389
+
390
+ <style is:global>
391
+ :root {
392
+ --mortgage-color-primary: #3b82f6;
393
+ --mortgage-color-primary-dark: #1e40af;
394
+ --mortgage-color-primary-light: #dbeafe;
395
+ --mortgage-color-primary-extra-light: #f0f9ff;
396
+ --mortgage-color-success: #10b981;
397
+ --mortgage-color-success-dark: #047857;
398
+ --mortgage-color-success-light: #d1fae5;
399
+ --mortgage-color-danger: #ef4444;
400
+ --mortgage-color-danger-light: #fee2e2;
401
+ --mortgage-color-text-primary: #1f2937;
402
+ --mortgage-color-text-secondary: #6b7280;
403
+ --mortgage-color-text-light: #9ca3af;
404
+ --mortgage-color-bg-primary: #fff;
405
+ --mortgage-color-bg-secondary: #f9fafb;
406
+ --mortgage-color-bg-tertiary: #f3f4f6;
407
+ --mortgage-color-border: #e5e7eb;
408
+ --mortgage-color-border-light: #f3f4f6;
409
+ }
410
+
411
+ .theme-dark,
412
+ [data-theme='dark'] {
413
+ --mortgage-color-primary: #60a5fa;
414
+ --mortgage-color-primary-dark: #3b82f6;
415
+ --mortgage-color-primary-light: #1e3a8a;
416
+ --mortgage-color-primary-extra-light: #0c1844;
417
+ --mortgage-color-success: #34d399;
418
+ --mortgage-color-success-dark: #10b981;
419
+ --mortgage-color-success-light: #064e3b;
420
+ --mortgage-color-danger: #f87171;
421
+ --mortgage-color-danger-light: #7f1d1d;
422
+ --mortgage-color-text-primary: #f3f4f6;
423
+ --mortgage-color-text-secondary: #d1d5db;
424
+ --mortgage-color-text-light: #9ca3af;
425
+ --mortgage-color-bg-primary: #1f2937;
426
+ --mortgage-color-bg-secondary: #111827;
427
+ --mortgage-color-bg-tertiary: #0f172a;
428
+ --mortgage-color-border: #374151;
429
+ --mortgage-color-border-light: #1f2937;
430
+ }
431
+
432
+ @media (prefers-color-scheme: dark) {
433
+ :root {
434
+ --mortgage-color-primary: #60a5fa;
435
+ --mortgage-color-primary-dark: #3b82f6;
436
+ --mortgage-color-primary-light: #1e3a8a;
437
+ --mortgage-color-primary-extra-light: #0c1844;
438
+ --mortgage-color-success: #34d399;
439
+ --mortgage-color-success-dark: #10b981;
440
+ --mortgage-color-success-light: #064e3b;
441
+ --mortgage-color-danger: #f87171;
442
+ --mortgage-color-danger-light: #7f1d1d;
443
+ --mortgage-color-text-primary: #f3f4f6;
444
+ --mortgage-color-text-secondary: #d1d5db;
445
+ --mortgage-color-text-light: #9ca3af;
446
+ --mortgage-color-bg-primary: #1f2937;
447
+ --mortgage-color-bg-secondary: #111827;
448
+ --mortgage-color-bg-tertiary: #0f172a;
449
+ --mortgage-color-border: #374151;
450
+ --mortgage-color-border-light: #1f2937;
451
+ }
452
+ }
453
+
454
+ .mortgage-container {
455
+ background-color: var(--mortgage-color-bg-primary);
456
+ border-radius: 1.5rem;
457
+ box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1);
458
+ border: 1px solid var(--mortgage-color-border);
459
+ overflow: hidden;
460
+ }
461
+
462
+ .mortgage-header {
463
+ padding: 1.5rem;
464
+ border-bottom: 1px solid var(--mortgage-color-border);
465
+ background-color: rgba(59, 130, 246, 0.05);
466
+ }
467
+
468
+ .mortgage-header-title-group {
469
+ display: flex;
470
+ align-items: center;
471
+ gap: 0.75rem;
472
+ }
473
+
474
+ .mortgage-header-icon {
475
+ width: 2.5rem;
476
+ height: 2.5rem;
477
+ padding: 0.625rem;
478
+ background-color: rgba(59, 130, 246, 0.1);
479
+ border-radius: 0.75rem;
480
+ color: var(--mortgage-color-primary);
481
+ flex-shrink: 0;
482
+ }
483
+
484
+ .mortgage-header-icon svg {
485
+ width: 100%;
486
+ height: 100%;
487
+ }
488
+
489
+ .mortgage-header-title {
490
+ font-size: 1.25rem;
491
+ font-weight: 700;
492
+ color: var(--mortgage-color-text-primary);
493
+ margin: 0;
494
+ }
495
+
496
+ .mortgage-grid {
497
+ display: grid;
498
+ grid-template-columns: 1fr;
499
+ gap: 0;
500
+ padding: 0;
501
+ }
502
+
503
+ @media (min-width: 1024px) {
504
+ .mortgage-grid {
505
+ grid-template-columns: minmax(300px, 1fr) 2fr;
506
+ gap: 0;
507
+ padding: 0;
508
+ }
509
+ }
510
+
511
+ .mortgage-left {
512
+ padding: 1.5rem;
513
+ background-color: rgba(59, 130, 246, 0.02);
514
+ display: flex;
515
+ flex-direction: column;
516
+ gap: 2rem;
517
+ border-right: 1px solid var(--mortgage-color-border);
518
+ }
519
+
520
+ @media (max-width: 1023px) {
521
+ .mortgage-left {
522
+ border-right: none;
523
+ border-bottom: 1px solid var(--mortgage-color-border);
524
+ }
525
+ }
526
+
527
+ .mortgage-right {
528
+ padding: 1.5rem;
529
+ display: flex;
530
+ flex-direction: column;
531
+ gap: 2.5rem;
532
+ }
533
+
534
+ .mortgage-input-group {
535
+ display: flex;
536
+ flex-direction: column;
537
+ gap: 0.5rem;
538
+ }
539
+
540
+ .mortgage-label {
541
+ font-size: 0.75rem;
542
+ font-weight: 700;
543
+ color: var(--mortgage-color-text-light);
544
+ text-transform: uppercase;
545
+ letter-spacing: 0.05em;
546
+ }
547
+
548
+ .mortgage-label-extra {
549
+ display: flex;
550
+ align-items: center;
551
+ gap: 0.5rem;
552
+ color: var(--mortgage-color-success-dark);
553
+ }
554
+
555
+ .mortgage-label-extra svg {
556
+ width: 1rem;
557
+ height: 1rem;
558
+ }
559
+
560
+ .mortgage-input-row {
561
+ display: grid;
562
+ grid-template-columns: 1fr 1fr;
563
+ gap: 1rem;
564
+ }
565
+
566
+ .mortgage-input-wrapper {
567
+ position: relative;
568
+ display: flex;
569
+ align-items: center;
570
+ }
571
+
572
+ .mortgage-input-prefix {
573
+ position: absolute;
574
+ left: 1rem;
575
+ top: 50%;
576
+ transform: translateY(-50%);
577
+ color: var(--mortgage-color-text-light);
578
+ font-weight: 700;
579
+ pointer-events: none;
580
+ }
581
+
582
+ .mortgage-input-suffix {
583
+ position: absolute;
584
+ right: 1rem;
585
+ top: 50%;
586
+ transform: translateY(-50%);
587
+ color: var(--mortgage-color-text-light);
588
+ font-weight: 700;
589
+ pointer-events: none;
590
+ }
591
+
592
+ .mortgage-input {
593
+ width: 100%;
594
+ padding: 0.75rem 1rem;
595
+ background-color: var(--mortgage-color-bg-primary);
596
+ border: 2px solid var(--mortgage-color-border);
597
+ border-radius: 0.75rem;
598
+ font-weight: 700;
599
+ font-size: 1.125rem;
600
+ color: var(--mortgage-color-text-primary);
601
+ transition: all 0.2s ease;
602
+ }
603
+
604
+ .mortgage-input:focus {
605
+ border-color: var(--mortgage-color-primary);
606
+ outline: none;
607
+ }
608
+
609
+ .mortgage-input-prefixed {
610
+ padding-left: 3.5rem;
611
+ }
612
+
613
+ .mortgage-input-suffixed {
614
+ padding-right: 3.5rem;
615
+ }
616
+
617
+ .mortgage-input-extra {
618
+ background-color: rgba(16, 185, 129, 0.05);
619
+ border-color: rgba(16, 185, 129, 0.2);
620
+ }
621
+
622
+ .mortgage-input-extra:focus {
623
+ border-color: var(--mortgage-color-success);
624
+ }
625
+
626
+ .mortgage-input-hint {
627
+ font-size: 0.625rem;
628
+ color: var(--mortgage-color-text-light);
629
+ margin-top: 0.5rem;
630
+ line-height: 1.5;
631
+ }
632
+
633
+ .mortgage-divider {
634
+ height: 1px;
635
+ background-color: var(--mortgage-color-border);
636
+ margin: 1rem 0;
637
+ }
638
+
639
+ .mortgage-payment-card {
640
+ background: linear-gradient(135deg, var(--mortgage-color-primary) 0%, var(--mortgage-color-primary-dark) 100%);
641
+ border-radius: 1rem;
642
+ padding: 1.5rem;
643
+ color: white;
644
+ text-align: center;
645
+ box-shadow: 0 4px 12px rgba(59, 130, 246, 0.2);
646
+ }
647
+
648
+ .mortgage-payment-label {
649
+ display: block;
650
+ font-size: 0.75rem;
651
+ font-weight: 700;
652
+ text-transform: uppercase;
653
+ letter-spacing: 0.05em;
654
+ opacity: 0.9;
655
+ margin-bottom: 0.5rem;
656
+ }
657
+
658
+ .mortgage-payment-value {
659
+ display: block;
660
+ font-size: 3rem;
661
+ font-weight: 900;
662
+ letter-spacing: -0.02em;
663
+ margin-bottom: 0.5rem;
664
+ }
665
+
666
+ .mortgage-payment-duration {
667
+ display: block;
668
+ font-size: 0.75rem;
669
+ opacity: 0.8;
670
+ }
671
+
672
+ .mortgage-savings-card {
673
+ background-color: var(--mortgage-color-bg-secondary);
674
+ border: 2px dashed var(--mortgage-color-border);
675
+ border-radius: 1rem;
676
+ padding: 1.5rem;
677
+ transition: all 0.3s ease;
678
+ }
679
+
680
+ .mortgage-savings-inactive {
681
+ opacity: 0.6;
682
+ filter: grayscale(1);
683
+ }
684
+
685
+ .mortgage-savings-active {
686
+ border-color: var(--mortgage-color-success);
687
+ background-color: rgba(16, 185, 129, 0.05);
688
+ box-shadow: 0 0 16px rgba(16, 185, 129, 0.1);
689
+ }
690
+
691
+ .mortgage-savings-title {
692
+ display: flex;
693
+ align-items: center;
694
+ gap: 0.5rem;
695
+ font-size: 0.875rem;
696
+ font-weight: 700;
697
+ text-transform: uppercase;
698
+ color: var(--mortgage-color-success-dark);
699
+ margin: 0 0 1rem;
700
+ }
701
+
702
+ .mortgage-savings-title svg {
703
+ width: 1rem;
704
+ height: 1rem;
705
+ }
706
+
707
+ .mortgage-savings-grid {
708
+ display: grid;
709
+ grid-template-columns: 1fr 1fr;
710
+ gap: 1rem;
711
+ }
712
+
713
+ .mortgage-savings-item {
714
+ display: flex;
715
+ flex-direction: column;
716
+ }
717
+
718
+ .mortgage-savings-label {
719
+ font-size: 0.75rem;
720
+ color: var(--mortgage-color-text-light);
721
+ margin-bottom: 0.25rem;
722
+ }
723
+
724
+ .mortgage-savings-value {
725
+ font-size: 1.25rem;
726
+ font-weight: 700;
727
+ color: var(--mortgage-color-text-primary);
728
+ }
729
+
730
+ .mortgage-section {
731
+ display: flex;
732
+ flex-direction: column;
733
+ gap: 1rem;
734
+ }
735
+
736
+ .mortgage-section-title {
737
+ font-size: 0.875rem;
738
+ font-weight: 700;
739
+ color: var(--mortgage-color-text-primary);
740
+ margin: 0;
741
+ display: flex;
742
+ align-items: center;
743
+ gap: 0.5rem;
744
+ }
745
+
746
+ .mortgage-section-divider {
747
+ height: 1px;
748
+ background-color: var(--mortgage-color-border);
749
+ }
750
+
751
+ .mortgage-timeline {
752
+ display: flex;
753
+ flex-direction: column;
754
+ gap: 1.5rem;
755
+ }
756
+
757
+ .mortgage-timeline-item {
758
+ display: flex;
759
+ flex-direction: column;
760
+ gap: 0.5rem;
761
+ }
762
+
763
+ .mortgage-timeline-header {
764
+ display: flex;
765
+ justify-content: space-between;
766
+ align-items: center;
767
+ font-size: 0.75rem;
768
+ font-weight: 700;
769
+ text-transform: uppercase;
770
+ letter-spacing: 0.05em;
771
+ color: var(--mortgage-color-text-light);
772
+ }
773
+
774
+ .mortgage-timeline-label-optimized {
775
+ color: var(--mortgage-color-success-dark);
776
+ }
777
+
778
+ .mortgage-timeline-bar {
779
+ height: 1rem;
780
+ width: 100%;
781
+ background-color: var(--mortgage-color-bg-secondary);
782
+ border-radius: 9999px;
783
+ overflow: hidden;
784
+ }
785
+
786
+ .mortgage-timeline-fill {
787
+ height: 100%;
788
+ background-color: var(--mortgage-color-text-light);
789
+ border-radius: 9999px;
790
+ transition: width 1s ease;
791
+ }
792
+
793
+ .mortgage-timeline-fill-optimized {
794
+ background-color: var(--mortgage-color-success);
795
+ width: 100%;
796
+ }
797
+
798
+ .mortgage-timeline-fill.mortgage-timeline-active {
799
+ background-color: var(--mortgage-color-success);
800
+ box-shadow: 0 0 16px rgba(16, 185, 129, 0.5);
801
+ }
802
+
803
+ .mortgage-breakdown-bar {
804
+ display: flex;
805
+ height: 3.5rem;
806
+ width: 100%;
807
+ background-color: var(--mortgage-color-bg-secondary);
808
+ border-radius: 0.75rem;
809
+ overflow: hidden;
810
+ gap: 0;
811
+ margin-bottom: 1rem;
812
+ font-size: 0.75rem;
813
+ font-weight: 700;
814
+ color: white;
815
+ }
816
+
817
+ .mortgage-breakdown-principal {
818
+ background-color: var(--mortgage-color-text-light);
819
+ display: flex;
820
+ align-items: center;
821
+ justify-content: center;
822
+ flex-direction: column;
823
+ gap: 0.25rem;
824
+ transition: width 0.7s ease;
825
+ }
826
+
827
+ .mortgage-breakdown-interest {
828
+ background-color: var(--mortgage-color-danger);
829
+ display: flex;
830
+ align-items: center;
831
+ justify-content: center;
832
+ flex-direction: column;
833
+ gap: 0.25rem;
834
+ transition: width 0.7s ease;
835
+ }
836
+
837
+ .mortgage-breakdown-label {
838
+ display: block;
839
+ }
840
+
841
+ .mortgage-breakdown-value {
842
+ display: none;
843
+ }
844
+
845
+ @media (min-width: 768px) {
846
+ .mortgage-breakdown-value {
847
+ display: block;
848
+ font-weight: 300;
849
+ opacity: 0.8;
850
+ }
851
+ }
852
+
853
+ .mortgage-breakdown-summary {
854
+ display: flex;
855
+ justify-content: space-between;
856
+ gap: 1rem;
857
+ font-size: 0.875rem;
858
+ color: var(--mortgage-color-text-secondary);
859
+ }
860
+
861
+ .mortgage-breakdown-item {
862
+ flex: 1;
863
+ }
864
+
865
+ .mortgage-breakdown-emphasis {
866
+ font-weight: 700;
867
+ color: var(--mortgage-color-text-primary);
868
+ }
869
+
870
+ .mortgage-breakdown-interest-emphasis {
871
+ color: var(--mortgage-color-danger);
872
+ }
873
+
874
+ .mortgage-table-container {
875
+ background-color: var(--mortgage-color-bg-secondary);
876
+ border: 1px solid var(--mortgage-color-border);
877
+ border-radius: 1rem;
878
+ overflow: hidden;
879
+ display: flex;
880
+ flex-direction: column;
881
+ }
882
+
883
+ .mortgage-table-header {
884
+ display: flex;
885
+ justify-content: space-between;
886
+ align-items: center;
887
+ padding: 1rem;
888
+ border-bottom: 1px solid var(--mortgage-color-border);
889
+ background-color: rgba(59, 130, 246, 0.05);
890
+ }
891
+
892
+ .mortgage-table-title {
893
+ font-size: 0.75rem;
894
+ font-weight: 700;
895
+ text-transform: uppercase;
896
+ letter-spacing: 0.05em;
897
+ color: var(--mortgage-color-text-light);
898
+ margin: 0;
899
+ }
900
+
901
+ .mortgage-toggle-btn {
902
+ font-size: 0.625rem;
903
+ background-color: var(--mortgage-color-bg-primary);
904
+ border: 1px solid var(--mortgage-color-border);
905
+ padding: 0.375rem 0.75rem;
906
+ border-radius: 0.5rem;
907
+ color: var(--mortgage-color-text-secondary);
908
+ font-weight: 700;
909
+ cursor: pointer;
910
+ transition: all 0.2s ease;
911
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
912
+ }
913
+
914
+ .mortgage-toggle-btn:hover {
915
+ background-color: var(--mortgage-color-bg-secondary);
916
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
917
+ }
918
+
919
+ .mortgage-toggle-btn:active {
920
+ transform: scale(0.95);
921
+ }
922
+
923
+ .mortgage-table-scroll {
924
+ overflow-x: auto;
925
+ max-height: 500px;
926
+ overflow-y: auto;
927
+ }
928
+
929
+ .mortgage-table {
930
+ width: 100%;
931
+ text-align: left;
932
+ border-collapse: collapse;
933
+ font-size: 0.75rem;
934
+ }
935
+
936
+ .mortgage-table thead {
937
+ position: sticky;
938
+ top: 0;
939
+ background-color: var(--mortgage-color-bg-secondary);
940
+ z-index: 10;
941
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
942
+ }
943
+
944
+ .mortgage-table-cell {
945
+ padding: 0.75rem 0.5rem;
946
+ color: var(--mortgage-color-text-secondary);
947
+ font-weight: 500;
948
+ }
949
+
950
+ .mortgage-table thead .mortgage-table-cell {
951
+ color: var(--mortgage-color-text-light);
952
+ font-weight: 700;
953
+ text-transform: uppercase;
954
+ letter-spacing: 0.05em;
955
+ }
956
+
957
+ .mortgage-table-interest {
958
+ text-align: right;
959
+ color: var(--mortgage-color-danger);
960
+ }
961
+
962
+ .mortgage-table thead .mortgage-table-interest {
963
+ color: var(--mortgage-color-danger);
964
+ }
965
+
966
+ .mortgage-table-principal {
967
+ text-align: right;
968
+ color: var(--mortgage-color-success);
969
+ }
970
+
971
+ .mortgage-table thead .mortgage-table-principal {
972
+ color: var(--mortgage-color-success);
973
+ }
974
+
975
+ .mortgage-table-extra {
976
+ text-align: right;
977
+ color: var(--mortgage-color-primary);
978
+ }
979
+
980
+ .mortgage-table thead .mortgage-table-extra {
981
+ color: var(--mortgage-color-primary);
982
+ }
983
+
984
+ .mortgage-table-balance {
985
+ text-align: right;
986
+ color: var(--mortgage-color-text-light);
987
+ padding-right: 0.5rem;
988
+ }
989
+
990
+ .mortgage-table tbody .mortgage-table-row {
991
+ border-bottom: 1px solid var(--mortgage-color-border);
992
+ transition: background-color 0.2s ease;
993
+ }
994
+
995
+ .mortgage-table tbody .mortgage-table-row:hover {
996
+ background-color: rgba(59, 130, 246, 0.05);
997
+ }
998
+
999
+ .mortgage-table-dash {
1000
+ opacity: 0.2;
1001
+ }
1002
+
1003
+ .mortgage-table-footer {
1004
+ padding: 1.5rem 0.5rem;
1005
+ text-align: center;
1006
+ font-size: 0.625rem;
1007
+ color: var(--mortgage-color-text-light);
1008
+ font-style: italic;
1009
+ background-color: rgba(59, 130, 246, 0.02);
1010
+ }
1011
+
1012
+ .mortgage-table-footer-btn {
1013
+ margin-left: 0.25rem;
1014
+ text-decoration: underline;
1015
+ font-weight: 700;
1016
+ color: var(--mortgage-color-primary);
1017
+ cursor: pointer;
1018
+ background: none;
1019
+ border: none;
1020
+ padding: 0;
1021
+ }
1022
+
1023
+ .mortgage-table-paid {
1024
+ font-style: normal;
1025
+ font-weight: 700;
1026
+ color: var(--mortgage-color-success);
1027
+ text-transform: uppercase;
1028
+ letter-spacing: 0.05em;
1029
+ }
1030
+ </style>