@retiregolden/engine 0.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 (199) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +79 -0
  3. package/dist/allocation/assetClasses.d.ts +94 -0
  4. package/dist/allocation/assetClasses.js +215 -0
  5. package/dist/decisions/annuitization.d.ts +72 -0
  6. package/dist/decisions/annuitization.js +184 -0
  7. package/dist/decisions/decisionFixtures.d.ts +48 -0
  8. package/dist/decisions/decisionFixtures.js +253 -0
  9. package/dist/decisions/evaluateCandidate.d.ts +61 -0
  10. package/dist/decisions/evaluateCandidate.js +193 -0
  11. package/dist/decisions/generators.d.ts +110 -0
  12. package/dist/decisions/generators.js +667 -0
  13. package/dist/decisions/index.d.ts +17 -0
  14. package/dist/decisions/index.js +17 -0
  15. package/dist/decisions/insightsAdapter.d.ts +27 -0
  16. package/dist/decisions/insightsAdapter.js +51 -0
  17. package/dist/decisions/objectives.d.ts +96 -0
  18. package/dist/decisions/objectives.js +319 -0
  19. package/dist/decisions/pensionElection.d.ts +112 -0
  20. package/dist/decisions/pensionElection.js +239 -0
  21. package/dist/decisions/search.d.ts +51 -0
  22. package/dist/decisions/search.js +110 -0
  23. package/dist/decisions/spendingSolver.d.ts +70 -0
  24. package/dist/decisions/spendingSolver.js +180 -0
  25. package/dist/decisions/spiaQuotes.d.ts +21 -0
  26. package/dist/decisions/spiaQuotes.js +43 -0
  27. package/dist/decisions/stochastic.d.ts +11 -0
  28. package/dist/decisions/stochastic.js +66 -0
  29. package/dist/decisions/swrComparator.d.ts +54 -0
  30. package/dist/decisions/swrComparator.js +89 -0
  31. package/dist/decisions/tournament.d.ts +53 -0
  32. package/dist/decisions/tournament.js +104 -0
  33. package/dist/decisions/types.d.ts +125 -0
  34. package/dist/decisions/types.js +12 -0
  35. package/dist/index.d.ts +13 -0
  36. package/dist/index.js +12 -0
  37. package/dist/insights/detectors/annuitizationHeadroom.d.ts +11 -0
  38. package/dist/insights/detectors/annuitizationHeadroom.js +77 -0
  39. package/dist/insights/detectors/assetLocation.d.ts +9 -0
  40. package/dist/insights/detectors/assetLocation.js +86 -0
  41. package/dist/insights/detectors/hecmBufferCandidate.d.ts +12 -0
  42. package/dist/insights/detectors/hecmBufferCandidate.js +76 -0
  43. package/dist/insights/detectors/incomeFloorFunded.d.ts +8 -0
  44. package/dist/insights/detectors/incomeFloorFunded.js +52 -0
  45. package/dist/insights/detectors/irmaaTierEdge.d.ts +2 -0
  46. package/dist/insights/detectors/irmaaTierEdge.js +102 -0
  47. package/dist/insights/detectors/pensionElectionPending.d.ts +11 -0
  48. package/dist/insights/detectors/pensionElectionPending.js +62 -0
  49. package/dist/insights/detectors/qcdEfficiency.d.ts +2 -0
  50. package/dist/insights/detectors/qcdEfficiency.js +62 -0
  51. package/dist/insights/detectors/rothBridgeHeadroom.d.ts +2 -0
  52. package/dist/insights/detectors/rothBridgeHeadroom.js +77 -0
  53. package/dist/insights/detectors/spendingGuardrails.d.ts +2 -0
  54. package/dist/insights/detectors/spendingGuardrails.js +77 -0
  55. package/dist/insights/detectors/spendingHeadroom.d.ts +12 -0
  56. package/dist/insights/detectors/spendingHeadroom.js +92 -0
  57. package/dist/insights/detectors/ssBridgeGap.d.ts +9 -0
  58. package/dist/insights/detectors/ssBridgeGap.js +92 -0
  59. package/dist/insights/detectors/stateRelocation.d.ts +12 -0
  60. package/dist/insights/detectors/stateRelocation.js +99 -0
  61. package/dist/insights/detectors/widowsPenalty.d.ts +12 -0
  62. package/dist/insights/detectors/widowsPenalty.js +145 -0
  63. package/dist/insights/registry.d.ts +4 -0
  64. package/dist/insights/registry.js +62 -0
  65. package/dist/insights/runInsights.d.ts +6 -0
  66. package/dist/insights/runInsights.js +15 -0
  67. package/dist/insights/types.d.ts +70 -0
  68. package/dist/insights/types.js +1 -0
  69. package/dist/ladder/bridge.d.ts +72 -0
  70. package/dist/ladder/bridge.js +59 -0
  71. package/dist/ladder/fedInvest.d.ts +55 -0
  72. package/dist/ladder/fedInvest.js +77 -0
  73. package/dist/ladder/fundedRatio.d.ts +41 -0
  74. package/dist/ladder/fundedRatio.js +46 -0
  75. package/dist/ladder/ladderMath.d.ts +88 -0
  76. package/dist/ladder/ladderMath.js +140 -0
  77. package/dist/longevity/ssaPeriod2022.d.ts +10 -0
  78. package/dist/longevity/ssaPeriod2022.js +51 -0
  79. package/dist/longevity/types.d.ts +39 -0
  80. package/dist/longevity/types.js +1 -0
  81. package/dist/model/migrations.d.ts +20 -0
  82. package/dist/model/migrations.js +67 -0
  83. package/dist/model/plan.d.ts +3108 -0
  84. package/dist/model/plan.js +1514 -0
  85. package/dist/montecarlo/frontiers.d.ts +22 -0
  86. package/dist/montecarlo/frontiers.js +61 -0
  87. package/dist/montecarlo/historicalReturns.d.ts +27 -0
  88. package/dist/montecarlo/historicalReturns.js +124 -0
  89. package/dist/montecarlo/historicalSuites.d.ts +45 -0
  90. package/dist/montecarlo/historicalSuites.js +102 -0
  91. package/dist/montecarlo/ltcShock.d.ts +42 -0
  92. package/dist/montecarlo/ltcShock.js +62 -0
  93. package/dist/montecarlo/marketModels.d.ts +248 -0
  94. package/dist/montecarlo/marketModels.js +672 -0
  95. package/dist/montecarlo/mortality.d.ts +31 -0
  96. package/dist/montecarlo/mortality.js +61 -0
  97. package/dist/montecarlo/riskBasedGuardrails.d.ts +93 -0
  98. package/dist/montecarlo/riskBasedGuardrails.js +200 -0
  99. package/dist/montecarlo/rng.d.ts +18 -0
  100. package/dist/montecarlo/rng.js +44 -0
  101. package/dist/montecarlo/run.d.ts +210 -0
  102. package/dist/montecarlo/run.js +353 -0
  103. package/dist/montecarlo/sharedPaths.d.ts +43 -0
  104. package/dist/montecarlo/sharedPaths.js +28 -0
  105. package/dist/montecarlo/survival.d.ts +49 -0
  106. package/dist/montecarlo/survival.js +126 -0
  107. package/dist/params/data/realYieldCurve2026.d.ts +17 -0
  108. package/dist/params/data/realYieldCurve2026.js +26 -0
  109. package/dist/params/data/year2026.d.ts +14 -0
  110. package/dist/params/data/year2026.js +199 -0
  111. package/dist/params/index.d.ts +63 -0
  112. package/dist/params/index.js +134 -0
  113. package/dist/params/provenance.d.ts +29 -0
  114. package/dist/params/provenance.js +123 -0
  115. package/dist/params/state/data/year2026.d.ts +18 -0
  116. package/dist/params/state/data/year2026.js +581 -0
  117. package/dist/params/state/index.d.ts +13 -0
  118. package/dist/params/state/index.js +26 -0
  119. package/dist/params/state/types.d.ts +73 -0
  120. package/dist/params/state/types.js +10 -0
  121. package/dist/params/types.d.ts +201 -0
  122. package/dist/params/types.js +7 -0
  123. package/dist/projection/annuityForms.d.ts +62 -0
  124. package/dist/projection/annuityForms.js +87 -0
  125. package/dist/projection/compare.d.ts +96 -0
  126. package/dist/projection/compare.js +216 -0
  127. package/dist/projection/flatTax.d.ts +7 -0
  128. package/dist/projection/flatTax.js +14 -0
  129. package/dist/projection/optimizePlan.d.ts +328 -0
  130. package/dist/projection/optimizePlan.js +1039 -0
  131. package/dist/projection/relocation.d.ts +149 -0
  132. package/dist/projection/relocation.js +317 -0
  133. package/dist/projection/simulate.d.ts +61 -0
  134. package/dist/projection/simulate.js +2754 -0
  135. package/dist/projection/types.d.ts +381 -0
  136. package/dist/projection/types.js +9 -0
  137. package/dist/rmd/jointLifeTable.d.ts +8 -0
  138. package/dist/rmd/jointLifeTable.js +663 -0
  139. package/dist/rmd/rmd.d.ts +29 -0
  140. package/dist/rmd/rmd.js +34 -0
  141. package/dist/scenarios/scenarios.d.ts +70 -0
  142. package/dist/scenarios/scenarios.js +130 -0
  143. package/dist/socialSecurity/benefitFactor.d.ts +13 -0
  144. package/dist/socialSecurity/benefitFactor.js +40 -0
  145. package/dist/socialSecurity/claimFactor.d.ts +33 -0
  146. package/dist/socialSecurity/claimFactor.js +58 -0
  147. package/dist/socialSecurity/disability.d.ts +36 -0
  148. package/dist/socialSecurity/disability.js +42 -0
  149. package/dist/socialSecurity/familyMaximum.d.ts +38 -0
  150. package/dist/socialSecurity/familyMaximum.js +48 -0
  151. package/dist/socialSecurity/maritalBenefits.d.ts +57 -0
  152. package/dist/socialSecurity/maritalBenefits.js +87 -0
  153. package/dist/socialSecurity/nra.d.ts +30 -0
  154. package/dist/socialSecurity/nra.js +83 -0
  155. package/dist/socialSecurity/piaFromEarnings.d.ts +100 -0
  156. package/dist/socialSecurity/piaFromEarnings.js +202 -0
  157. package/dist/socialSecurity/ssaWageData.d.ts +68 -0
  158. package/dist/socialSecurity/ssaWageData.js +306 -0
  159. package/dist/socialSecurity/survivorBenefit.d.ts +61 -0
  160. package/dist/socialSecurity/survivorBenefit.js +61 -0
  161. package/dist/spending/abw.d.ts +57 -0
  162. package/dist/spending/abw.js +80 -0
  163. package/dist/spending/flexibleGoals.d.ts +62 -0
  164. package/dist/spending/flexibleGoals.js +136 -0
  165. package/dist/spending/guardrails.d.ts +78 -0
  166. package/dist/spending/guardrails.js +100 -0
  167. package/dist/spending/layers.d.ts +81 -0
  168. package/dist/spending/layers.js +67 -0
  169. package/dist/spending/shapePresets.d.ts +42 -0
  170. package/dist/spending/shapePresets.js +77 -0
  171. package/dist/strategies/accountEligibility.d.ts +81 -0
  172. package/dist/strategies/accountEligibility.js +98 -0
  173. package/dist/strategies/inheritedIra.d.ts +45 -0
  174. package/dist/strategies/inheritedIra.js +43 -0
  175. package/dist/strategies/iraBasis.d.ts +48 -0
  176. package/dist/strategies/iraBasis.js +46 -0
  177. package/dist/strategies/optimizer.d.ts +307 -0
  178. package/dist/strategies/optimizer.js +551 -0
  179. package/dist/strategies/rothBasis.d.ts +67 -0
  180. package/dist/strategies/rothBasis.js +83 -0
  181. package/dist/strategies/rothConversion.d.ts +55 -0
  182. package/dist/strategies/rothConversion.js +83 -0
  183. package/dist/strategies/sepp.d.ts +38 -0
  184. package/dist/strategies/sepp.js +53 -0
  185. package/dist/tax/aca.d.ts +40 -0
  186. package/dist/tax/aca.js +82 -0
  187. package/dist/tax/federalTax.d.ts +110 -0
  188. package/dist/tax/federalTax.js +249 -0
  189. package/dist/tax/medicare.d.ts +20 -0
  190. package/dist/tax/medicare.js +36 -0
  191. package/dist/tax/propertySale.d.ts +46 -0
  192. package/dist/tax/propertySale.js +35 -0
  193. package/dist/tax/stateTax.d.ts +66 -0
  194. package/dist/tax/stateTax.js +198 -0
  195. package/dist/testing/money.d.ts +7 -0
  196. package/dist/testing/money.js +17 -0
  197. package/dist/testing/planFixtures.d.ts +29 -0
  198. package/dist/testing/planFixtures.js +141 -0
  199. package/package.json +75 -0
@@ -0,0 +1,581 @@
1
+ /**
2
+ * State income tax pack — all 50 states + DC ("big levers", V5 PR B).
3
+ *
4
+ * Transcribed from the cited per-state research docs in
5
+ * DOCS/domain/state-tax-research/ (see each <CODE>.md for sources and the
6
+ * simplifications each state's mapping makes). Values are latest-published
7
+ * (≈2025) individual-income-tax figures; like the federal pack, nominal
8
+ * brackets are carried forward for future years (bracket creep modeled).
9
+ *
10
+ * State taxable income in the engine starts from gross ordinary income (plus
11
+ * gains, plus the federally taxable SS amount where the state taxes SS), minus
12
+ * the retirement exclusion and the standard deduction below. For states that
13
+ * tax *federal taxable income* and have no separate deduction (e.g. CO), the
14
+ * standard deduction is set to the federal-equivalent so the gross→taxable
15
+ * conversion matches; noted inline.
16
+ */
17
+ const PUBLIC_PENSION_OVERRIDES = {
18
+ AL: { kind: 'full' },
19
+ AR: { kind: 'full' },
20
+ HI: { kind: 'full' },
21
+ IN: { kind: 'full' },
22
+ KS: { kind: 'full' },
23
+ LA: { kind: 'full' },
24
+ MA: { kind: 'full' },
25
+ NE: { kind: 'full' },
26
+ NY: { kind: 'full' },
27
+ OH: { kind: 'full' },
28
+ SC: { kind: 'full' },
29
+ };
30
+ function splitRetirementBuckets(raw) {
31
+ const states = {};
32
+ for (const [code, { retirement, ...params }] of Object.entries(raw.states)) {
33
+ states[code] = {
34
+ ...params,
35
+ retirementPrivate: retirement,
36
+ retirementPublic: PUBLIC_PENSION_OVERRIDES[code] ?? retirement,
37
+ // Without a separate public-pension law, the copied rule is one cap on
38
+ // all retirement income — the engine must not grant it per bucket.
39
+ retirementRuleShared: PUBLIC_PENSION_OVERRIDES[code] === undefined,
40
+ };
41
+ }
42
+ return { year: raw.year, states };
43
+ }
44
+ const rawStateYear2026 = {
45
+ year: 2026,
46
+ states: {
47
+ AL: {
48
+ code: 'AL', name: 'Alabama', hasIncomeTax: true, taxesSocialSecurity: false, capitalGainsAsOrdinary: true,
49
+ standardDeduction: { single: 3000, marriedFilingJointly: 8500 },
50
+ brackets: {
51
+ single: [{ lowerBound: 0, ratePct: 2 }, { lowerBound: 500, ratePct: 4 }, { lowerBound: 3000, ratePct: 5 }],
52
+ marriedFilingJointly: [{ lowerBound: 0, ratePct: 2 }, { lowerBound: 1000, ratePct: 4 }, { lowerBound: 6000, ratePct: 5 }],
53
+ },
54
+ retirement: { kind: 'capped', capPerPerson: 6000, minAge: 65 },
55
+ },
56
+ AK: {
57
+ code: 'AK', name: 'Alaska', hasIncomeTax: false, taxesSocialSecurity: false, capitalGainsAsOrdinary: true,
58
+ standardDeduction: { single: 0, marriedFilingJointly: 0 },
59
+ brackets: { single: [], marriedFilingJointly: [] }, retirement: { kind: 'none' },
60
+ },
61
+ AZ: {
62
+ code: 'AZ', name: 'Arizona', hasIncomeTax: true, taxesSocialSecurity: false, capitalGainsAsOrdinary: true,
63
+ standardDeduction: { single: 15000, marriedFilingJointly: 30000 },
64
+ brackets: { single: [{ lowerBound: 0, ratePct: 2.5 }], marriedFilingJointly: [{ lowerBound: 0, ratePct: 2.5 }] },
65
+ retirement: { kind: 'none' },
66
+ },
67
+ AR: {
68
+ code: 'AR', name: 'Arkansas', hasIncomeTax: true, taxesSocialSecurity: false, capitalGainsAsOrdinary: true,
69
+ standardDeduction: { single: 2410, marriedFilingJointly: 4820 },
70
+ brackets: {
71
+ single: [{ lowerBound: 0, ratePct: 2 }, { lowerBound: 4500, ratePct: 3.9 }],
72
+ marriedFilingJointly: [{ lowerBound: 0, ratePct: 2 }, { lowerBound: 4500, ratePct: 3.9 }],
73
+ },
74
+ retirement: { kind: 'capped', capPerPerson: 6000 },
75
+ },
76
+ CA: {
77
+ code: 'CA', name: 'California', hasIncomeTax: true, taxesSocialSecurity: false, capitalGainsAsOrdinary: true,
78
+ capitalGainsNotes: 'Long-term capital gains are taxed at ordinary California rates; no preferential state rate modeled.',
79
+ capitalGainsSources: [
80
+ 'DOCS/domain/state-tax-research/CA.md',
81
+ 'https://www.ftb.ca.gov/forms/2025/2025-540-tax-rate-schedules.pdf',
82
+ ],
83
+ standardDeduction: { single: 5540, marriedFilingJointly: 11080 },
84
+ brackets: {
85
+ single: [
86
+ { lowerBound: 0, ratePct: 1 }, { lowerBound: 11079, ratePct: 2 }, { lowerBound: 26264, ratePct: 4 },
87
+ { lowerBound: 41452, ratePct: 6 }, { lowerBound: 57542, ratePct: 8 }, { lowerBound: 72724, ratePct: 9.3 },
88
+ { lowerBound: 371479, ratePct: 10.3 }, { lowerBound: 445771, ratePct: 11.3 }, { lowerBound: 742953, ratePct: 12.3 },
89
+ ],
90
+ marriedFilingJointly: [
91
+ { lowerBound: 0, ratePct: 1 }, { lowerBound: 22158, ratePct: 2 }, { lowerBound: 52528, ratePct: 4 },
92
+ { lowerBound: 82904, ratePct: 6 }, { lowerBound: 115084, ratePct: 8 }, { lowerBound: 145448, ratePct: 9.3 },
93
+ { lowerBound: 742958, ratePct: 10.3 }, { lowerBound: 891542, ratePct: 11.3 }, { lowerBound: 1485906, ratePct: 12.3 },
94
+ ],
95
+ },
96
+ retirement: { kind: 'none' },
97
+ },
98
+ CO: {
99
+ // Flat 4.4% on federal taxable income; no separate state deduction, so the
100
+ // standard deduction is the federal-equivalent to convert gross→taxable.
101
+ code: 'CO', name: 'Colorado', hasIncomeTax: true, taxesSocialSecurity: true, capitalGainsAsOrdinary: true,
102
+ standardDeduction: { single: 15750, marriedFilingJointly: 31500 },
103
+ brackets: { single: [{ lowerBound: 0, ratePct: 4.4 }], marriedFilingJointly: [{ lowerBound: 0, ratePct: 4.4 }] },
104
+ retirement: { kind: 'capped', capPerPerson: 24000, minAge: 65 },
105
+ },
106
+ CT: {
107
+ code: 'CT', name: 'Connecticut', hasIncomeTax: true, taxesSocialSecurity: true, capitalGainsAsOrdinary: true,
108
+ standardDeduction: { single: 0, marriedFilingJointly: 0 },
109
+ brackets: {
110
+ single: [
111
+ { lowerBound: 0, ratePct: 2 }, { lowerBound: 10000, ratePct: 4.5 }, { lowerBound: 50000, ratePct: 5.5 },
112
+ { lowerBound: 100000, ratePct: 6 }, { lowerBound: 200000, ratePct: 6.5 }, { lowerBound: 250000, ratePct: 6.9 },
113
+ { lowerBound: 500000, ratePct: 6.99 },
114
+ ],
115
+ marriedFilingJointly: [
116
+ { lowerBound: 0, ratePct: 2 }, { lowerBound: 20000, ratePct: 4.5 }, { lowerBound: 100000, ratePct: 5.5 },
117
+ { lowerBound: 200000, ratePct: 6 }, { lowerBound: 400000, ratePct: 6.5 }, { lowerBound: 500000, ratePct: 6.9 },
118
+ { lowerBound: 1000000, ratePct: 6.99 },
119
+ ],
120
+ },
121
+ retirement: { kind: 'full' }, // pension/IRA reach full exemption by 2026
122
+ },
123
+ DE: {
124
+ code: 'DE', name: 'Delaware', hasIncomeTax: true, taxesSocialSecurity: false, capitalGainsAsOrdinary: true,
125
+ standardDeduction: { single: 5700, marriedFilingJointly: 11400 },
126
+ brackets: {
127
+ single: [
128
+ { lowerBound: 0, ratePct: 0 }, { lowerBound: 2000, ratePct: 2.2 }, { lowerBound: 5000, ratePct: 3.9 },
129
+ { lowerBound: 10000, ratePct: 4.8 }, { lowerBound: 20000, ratePct: 5.2 }, { lowerBound: 25000, ratePct: 5.5 },
130
+ { lowerBound: 60000, ratePct: 6.6 },
131
+ ],
132
+ marriedFilingJointly: [
133
+ { lowerBound: 0, ratePct: 0 }, { lowerBound: 2000, ratePct: 2.2 }, { lowerBound: 5000, ratePct: 3.9 },
134
+ { lowerBound: 10000, ratePct: 4.8 }, { lowerBound: 20000, ratePct: 5.2 }, { lowerBound: 25000, ratePct: 5.5 },
135
+ { lowerBound: 60000, ratePct: 6.6 },
136
+ ],
137
+ },
138
+ retirement: { kind: 'capped', capPerPerson: 12500, minAge: 60 },
139
+ },
140
+ DC: {
141
+ code: 'DC', name: 'District of Columbia', hasIncomeTax: true, taxesSocialSecurity: false, capitalGainsAsOrdinary: true,
142
+ standardDeduction: { single: 15000, marriedFilingJointly: 30000 },
143
+ brackets: {
144
+ single: [
145
+ { lowerBound: 0, ratePct: 4 }, { lowerBound: 10000, ratePct: 6 }, { lowerBound: 40000, ratePct: 6.5 },
146
+ { lowerBound: 60000, ratePct: 8.5 }, { lowerBound: 250000, ratePct: 9.25 }, { lowerBound: 500000, ratePct: 9.75 },
147
+ { lowerBound: 1000000, ratePct: 10.75 },
148
+ ],
149
+ marriedFilingJointly: [
150
+ { lowerBound: 0, ratePct: 4 }, { lowerBound: 10000, ratePct: 6 }, { lowerBound: 40000, ratePct: 6.5 },
151
+ { lowerBound: 60000, ratePct: 8.5 }, { lowerBound: 250000, ratePct: 9.25 }, { lowerBound: 500000, ratePct: 9.75 },
152
+ { lowerBound: 1000000, ratePct: 10.75 },
153
+ ],
154
+ },
155
+ retirement: { kind: 'none' },
156
+ },
157
+ FL: {
158
+ code: 'FL', name: 'Florida', hasIncomeTax: false, taxesSocialSecurity: false, capitalGainsAsOrdinary: true,
159
+ standardDeduction: { single: 0, marriedFilingJointly: 0 },
160
+ brackets: { single: [], marriedFilingJointly: [] }, retirement: { kind: 'none' },
161
+ },
162
+ GA: {
163
+ code: 'GA', name: 'Georgia', hasIncomeTax: true, taxesSocialSecurity: false, capitalGainsAsOrdinary: true,
164
+ standardDeduction: { single: 12000, marriedFilingJointly: 24000 },
165
+ brackets: { single: [{ lowerBound: 0, ratePct: 5.39 }], marriedFilingJointly: [{ lowerBound: 0, ratePct: 5.39 }] },
166
+ retirement: { kind: 'capped', capPerPerson: 65000, minAge: 65 },
167
+ },
168
+ HI: {
169
+ code: 'HI', name: 'Hawaii', hasIncomeTax: true, taxesSocialSecurity: false, capitalGainsAsOrdinary: true,
170
+ standardDeduction: { single: 4400, marriedFilingJointly: 8800 },
171
+ brackets: {
172
+ single: [
173
+ { lowerBound: 0, ratePct: 1.4 }, { lowerBound: 9600, ratePct: 3.2 }, { lowerBound: 14400, ratePct: 5.5 },
174
+ { lowerBound: 19200, ratePct: 6.4 }, { lowerBound: 24000, ratePct: 6.8 }, { lowerBound: 36000, ratePct: 7.2 },
175
+ { lowerBound: 48000, ratePct: 7.6 }, { lowerBound: 125000, ratePct: 7.9 }, { lowerBound: 175000, ratePct: 8.25 },
176
+ { lowerBound: 225000, ratePct: 9 }, { lowerBound: 275000, ratePct: 10 }, { lowerBound: 325000, ratePct: 11 },
177
+ ],
178
+ marriedFilingJointly: [
179
+ { lowerBound: 0, ratePct: 1.4 }, { lowerBound: 19200, ratePct: 3.2 }, { lowerBound: 28800, ratePct: 5.5 },
180
+ { lowerBound: 38400, ratePct: 6.4 }, { lowerBound: 48000, ratePct: 6.8 }, { lowerBound: 72000, ratePct: 7.2 },
181
+ { lowerBound: 96000, ratePct: 7.6 }, { lowerBound: 250000, ratePct: 7.9 }, { lowerBound: 350000, ratePct: 8.25 },
182
+ { lowerBound: 450000, ratePct: 9 }, { lowerBound: 550000, ratePct: 10 }, { lowerBound: 650000, ratePct: 11 },
183
+ ],
184
+ },
185
+ retirement: { kind: 'none' },
186
+ },
187
+ ID: {
188
+ code: 'ID', name: 'Idaho', hasIncomeTax: true, taxesSocialSecurity: false, capitalGainsAsOrdinary: true,
189
+ standardDeduction: { single: 15000, marriedFilingJointly: 30000 },
190
+ brackets: {
191
+ single: [{ lowerBound: 0, ratePct: 0 }, { lowerBound: 4811, ratePct: 5.3 }],
192
+ marriedFilingJointly: [{ lowerBound: 0, ratePct: 0 }, { lowerBound: 9622, ratePct: 5.3 }],
193
+ },
194
+ retirement: { kind: 'none' },
195
+ },
196
+ IL: {
197
+ code: 'IL', name: 'Illinois', hasIncomeTax: true, taxesSocialSecurity: false, capitalGainsAsOrdinary: true,
198
+ standardDeduction: { single: 0, marriedFilingJointly: 0 },
199
+ brackets: { single: [{ lowerBound: 0, ratePct: 4.95 }], marriedFilingJointly: [{ lowerBound: 0, ratePct: 4.95 }] },
200
+ retirement: { kind: 'full' },
201
+ },
202
+ IN: {
203
+ code: 'IN', name: 'Indiana', hasIncomeTax: true, taxesSocialSecurity: false, capitalGainsAsOrdinary: true,
204
+ standardDeduction: { single: 0, marriedFilingJointly: 0 },
205
+ brackets: { single: [{ lowerBound: 0, ratePct: 3 }], marriedFilingJointly: [{ lowerBound: 0, ratePct: 3 }] },
206
+ retirement: { kind: 'none' },
207
+ },
208
+ IA: {
209
+ code: 'IA', name: 'Iowa', hasIncomeTax: true, taxesSocialSecurity: false, capitalGainsAsOrdinary: true,
210
+ standardDeduction: { single: 15000, marriedFilingJointly: 30000 },
211
+ brackets: { single: [{ lowerBound: 0, ratePct: 3.8 }], marriedFilingJointly: [{ lowerBound: 0, ratePct: 3.8 }] },
212
+ retirement: { kind: 'full', minAge: 55 },
213
+ },
214
+ KS: {
215
+ code: 'KS', name: 'Kansas', hasIncomeTax: true, taxesSocialSecurity: false, capitalGainsAsOrdinary: true,
216
+ standardDeduction: { single: 3605, marriedFilingJointly: 8240 },
217
+ brackets: {
218
+ single: [{ lowerBound: 0, ratePct: 5.2 }, { lowerBound: 23000, ratePct: 5.58 }],
219
+ marriedFilingJointly: [{ lowerBound: 0, ratePct: 5.2 }, { lowerBound: 46000, ratePct: 5.58 }],
220
+ },
221
+ retirement: { kind: 'none' },
222
+ },
223
+ KY: {
224
+ code: 'KY', name: 'Kentucky', hasIncomeTax: true, taxesSocialSecurity: false, capitalGainsAsOrdinary: true,
225
+ standardDeduction: { single: 3360, marriedFilingJointly: 6720 },
226
+ brackets: { single: [{ lowerBound: 0, ratePct: 3.5 }], marriedFilingJointly: [{ lowerBound: 0, ratePct: 3.5 }] },
227
+ retirement: { kind: 'capped', capPerPerson: 31110 },
228
+ },
229
+ LA: {
230
+ code: 'LA', name: 'Louisiana', hasIncomeTax: true, taxesSocialSecurity: false, capitalGainsAsOrdinary: true,
231
+ standardDeduction: { single: 12500, marriedFilingJointly: 25000 },
232
+ brackets: { single: [{ lowerBound: 0, ratePct: 3 }], marriedFilingJointly: [{ lowerBound: 0, ratePct: 3 }] },
233
+ retirement: { kind: 'capped', capPerPerson: 12000, minAge: 65 },
234
+ },
235
+ ME: {
236
+ code: 'ME', name: 'Maine', hasIncomeTax: true, taxesSocialSecurity: false, capitalGainsAsOrdinary: true,
237
+ standardDeduction: { single: 15000, marriedFilingJointly: 30000 },
238
+ brackets: {
239
+ single: [{ lowerBound: 0, ratePct: 5.8 }, { lowerBound: 26800, ratePct: 6.75 }, { lowerBound: 63450, ratePct: 7.15 }],
240
+ marriedFilingJointly: [{ lowerBound: 0, ratePct: 5.8 }, { lowerBound: 53600, ratePct: 6.75 }, { lowerBound: 126900, ratePct: 7.15 }],
241
+ },
242
+ retirement: { kind: 'capped', capPerPerson: 48216 },
243
+ },
244
+ MD: {
245
+ code: 'MD', name: 'Maryland', hasIncomeTax: true, taxesSocialSecurity: false, capitalGainsAsOrdinary: true,
246
+ standardDeduction: { single: 3350, marriedFilingJointly: 6700 },
247
+ brackets: {
248
+ single: [
249
+ { lowerBound: 0, ratePct: 2 }, { lowerBound: 1000, ratePct: 3 }, { lowerBound: 2000, ratePct: 4 },
250
+ { lowerBound: 3000, ratePct: 4.75 }, { lowerBound: 100000, ratePct: 5 }, { lowerBound: 125000, ratePct: 5.25 },
251
+ { lowerBound: 150000, ratePct: 5.5 }, { lowerBound: 250000, ratePct: 5.75 }, { lowerBound: 500000, ratePct: 6.25 },
252
+ { lowerBound: 1000000, ratePct: 6.5 },
253
+ ],
254
+ marriedFilingJointly: [
255
+ { lowerBound: 0, ratePct: 2 }, { lowerBound: 1000, ratePct: 3 }, { lowerBound: 2000, ratePct: 4 },
256
+ { lowerBound: 3000, ratePct: 4.75 }, { lowerBound: 150000, ratePct: 5 }, { lowerBound: 175000, ratePct: 5.25 },
257
+ { lowerBound: 225000, ratePct: 5.5 }, { lowerBound: 300000, ratePct: 5.75 }, { lowerBound: 600000, ratePct: 6.25 },
258
+ { lowerBound: 1200000, ratePct: 6.5 },
259
+ ],
260
+ },
261
+ retirement: { kind: 'capped', capPerPerson: 41200, minAge: 65 },
262
+ },
263
+ MA: {
264
+ code: 'MA', name: 'Massachusetts', hasIncomeTax: true, taxesSocialSecurity: false, capitalGainsAsOrdinary: true,
265
+ standardDeduction: { single: 0, marriedFilingJointly: 0 },
266
+ brackets: { single: [{ lowerBound: 0, ratePct: 5 }], marriedFilingJointly: [{ lowerBound: 0, ratePct: 5 }] },
267
+ retirement: { kind: 'none' },
268
+ },
269
+ MI: {
270
+ code: 'MI', name: 'Michigan', hasIncomeTax: true, taxesSocialSecurity: false, capitalGainsAsOrdinary: true,
271
+ standardDeduction: { single: 0, marriedFilingJointly: 0 },
272
+ brackets: { single: [{ lowerBound: 0, ratePct: 4.25 }], marriedFilingJointly: [{ lowerBound: 0, ratePct: 4.25 }] },
273
+ retirement: { kind: 'capped', capPerPerson: 49423 },
274
+ },
275
+ MN: {
276
+ code: 'MN', name: 'Minnesota', hasIncomeTax: true, taxesSocialSecurity: true, capitalGainsAsOrdinary: true,
277
+ capitalGainsNotes: 'Long-term capital gains are taxed as ordinary income; high-income NIIT-like surtax omitted.',
278
+ capitalGainsSources: ['DOCS/domain/state-tax-research/MN.md', 'https://www.revenue.state.mn.us/'],
279
+ standardDeduction: { single: 14575, marriedFilingJointly: 29150 },
280
+ brackets: {
281
+ single: [
282
+ { lowerBound: 0, ratePct: 5.35 }, { lowerBound: 31690, ratePct: 6.8 }, { lowerBound: 104090, ratePct: 7.85 },
283
+ { lowerBound: 193240, ratePct: 9.85 },
284
+ ],
285
+ marriedFilingJointly: [
286
+ { lowerBound: 0, ratePct: 5.35 }, { lowerBound: 46330, ratePct: 6.8 }, { lowerBound: 184040, ratePct: 7.85 },
287
+ { lowerBound: 321450, ratePct: 9.85 },
288
+ ],
289
+ },
290
+ retirement: { kind: 'none' },
291
+ },
292
+ MS: {
293
+ code: 'MS', name: 'Mississippi', hasIncomeTax: true, taxesSocialSecurity: false, capitalGainsAsOrdinary: true,
294
+ standardDeduction: { single: 2300, marriedFilingJointly: 4600 },
295
+ brackets: {
296
+ single: [{ lowerBound: 0, ratePct: 0 }, { lowerBound: 10000, ratePct: 4.4 }],
297
+ marriedFilingJointly: [{ lowerBound: 0, ratePct: 0 }, { lowerBound: 10000, ratePct: 4.4 }],
298
+ },
299
+ retirement: { kind: 'full' },
300
+ },
301
+ MO: {
302
+ code: 'MO', name: 'Missouri', hasIncomeTax: true, taxesSocialSecurity: false, capitalGainsAsOrdinary: true,
303
+ standardDeduction: { single: 15750, marriedFilingJointly: 31500 },
304
+ brackets: {
305
+ single: [
306
+ { lowerBound: 0, ratePct: 0 }, { lowerBound: 1313, ratePct: 2 }, { lowerBound: 2626, ratePct: 2.5 },
307
+ { lowerBound: 3939, ratePct: 3 }, { lowerBound: 5252, ratePct: 3.5 }, { lowerBound: 6565, ratePct: 4 },
308
+ { lowerBound: 7878, ratePct: 4.5 }, { lowerBound: 9191, ratePct: 4.7 },
309
+ ],
310
+ marriedFilingJointly: [
311
+ { lowerBound: 0, ratePct: 0 }, { lowerBound: 1313, ratePct: 2 }, { lowerBound: 2626, ratePct: 2.5 },
312
+ { lowerBound: 3939, ratePct: 3 }, { lowerBound: 5252, ratePct: 3.5 }, { lowerBound: 6565, ratePct: 4 },
313
+ { lowerBound: 7878, ratePct: 4.5 }, { lowerBound: 9191, ratePct: 4.7 },
314
+ ],
315
+ },
316
+ retirement: { kind: 'capped', capPerPerson: 6000 },
317
+ },
318
+ MT: {
319
+ code: 'MT', name: 'Montana', hasIncomeTax: true, taxesSocialSecurity: true, capitalGainsAsOrdinary: true,
320
+ standardDeduction: { single: 15750, marriedFilingJointly: 31500 },
321
+ brackets: {
322
+ single: [{ lowerBound: 0, ratePct: 4.7 }, { lowerBound: 20500, ratePct: 5.9 }],
323
+ marriedFilingJointly: [{ lowerBound: 0, ratePct: 4.7 }, { lowerBound: 41000, ratePct: 5.9 }],
324
+ },
325
+ retirement: { kind: 'none' },
326
+ },
327
+ NE: {
328
+ code: 'NE', name: 'Nebraska', hasIncomeTax: true, taxesSocialSecurity: false, capitalGainsAsOrdinary: true,
329
+ standardDeduction: { single: 8600, marriedFilingJointly: 17200 },
330
+ brackets: {
331
+ single: [
332
+ { lowerBound: 0, ratePct: 2.46 }, { lowerBound: 4030, ratePct: 3.51 }, { lowerBound: 24120, ratePct: 5.01 },
333
+ { lowerBound: 38870, ratePct: 5.2 },
334
+ ],
335
+ marriedFilingJointly: [
336
+ { lowerBound: 0, ratePct: 2.46 }, { lowerBound: 8040, ratePct: 3.51 }, { lowerBound: 48250, ratePct: 5.01 },
337
+ { lowerBound: 77730, ratePct: 5.2 },
338
+ ],
339
+ },
340
+ retirement: { kind: 'none' },
341
+ },
342
+ NV: {
343
+ code: 'NV', name: 'Nevada', hasIncomeTax: false, taxesSocialSecurity: false, capitalGainsAsOrdinary: false,
344
+ standardDeduction: { single: 0, marriedFilingJointly: 0 },
345
+ brackets: { single: [], marriedFilingJointly: [] }, retirement: { kind: 'none' },
346
+ },
347
+ NH: {
348
+ // Wages/retirement never taxed; interest & dividends tax repealed from 2025.
349
+ code: 'NH', name: 'New Hampshire', hasIncomeTax: false, taxesSocialSecurity: false, capitalGainsAsOrdinary: false,
350
+ standardDeduction: { single: 0, marriedFilingJointly: 0 },
351
+ brackets: { single: [], marriedFilingJointly: [] }, retirement: { kind: 'none' },
352
+ },
353
+ NJ: {
354
+ code: 'NJ', name: 'New Jersey', hasIncomeTax: true, taxesSocialSecurity: false, capitalGainsAsOrdinary: true,
355
+ capitalGainsNotes: 'Capital gains are included in New Jersey gross income and taxed at ordinary NJ rates.',
356
+ capitalGainsSources: ['DOCS/domain/state-tax-research/NJ.md', 'https://www.nj.gov/treasury/taxation/'],
357
+ standardDeduction: { single: 0, marriedFilingJointly: 0 },
358
+ brackets: {
359
+ single: [
360
+ { lowerBound: 0, ratePct: 1.4 }, { lowerBound: 20000, ratePct: 1.75 }, { lowerBound: 35000, ratePct: 3.5 },
361
+ { lowerBound: 40000, ratePct: 5.525 }, { lowerBound: 75000, ratePct: 6.37 }, { lowerBound: 500000, ratePct: 8.97 },
362
+ { lowerBound: 1000000, ratePct: 10.75 },
363
+ ],
364
+ marriedFilingJointly: [
365
+ { lowerBound: 0, ratePct: 1.4 }, { lowerBound: 20000, ratePct: 1.75 }, { lowerBound: 50000, ratePct: 2.45 },
366
+ { lowerBound: 70000, ratePct: 3.5 }, { lowerBound: 80000, ratePct: 5.525 }, { lowerBound: 150000, ratePct: 6.37 },
367
+ { lowerBound: 500000, ratePct: 8.97 }, { lowerBound: 1000000, ratePct: 10.75 },
368
+ ],
369
+ },
370
+ retirement: { kind: 'capped', capPerPerson: 50000, minAge: 62 },
371
+ },
372
+ NM: {
373
+ code: 'NM', name: 'New Mexico', hasIncomeTax: true, taxesSocialSecurity: false, capitalGainsAsOrdinary: true,
374
+ standardDeduction: { single: 15750, marriedFilingJointly: 31500 },
375
+ brackets: {
376
+ single: [
377
+ { lowerBound: 0, ratePct: 1.5 }, { lowerBound: 5500, ratePct: 3.2 }, { lowerBound: 16500, ratePct: 4.3 },
378
+ { lowerBound: 33500, ratePct: 4.7 }, { lowerBound: 66500, ratePct: 4.9 }, { lowerBound: 210000, ratePct: 5.9 },
379
+ ],
380
+ marriedFilingJointly: [
381
+ { lowerBound: 0, ratePct: 1.5 }, { lowerBound: 8000, ratePct: 3.2 }, { lowerBound: 25000, ratePct: 4.3 },
382
+ { lowerBound: 50000, ratePct: 4.7 }, { lowerBound: 100000, ratePct: 4.9 }, { lowerBound: 315000, ratePct: 5.9 },
383
+ ],
384
+ },
385
+ retirement: { kind: 'none' },
386
+ },
387
+ NY: {
388
+ code: 'NY', name: 'New York', hasIncomeTax: true, taxesSocialSecurity: false, capitalGainsAsOrdinary: true,
389
+ standardDeduction: { single: 8000, marriedFilingJointly: 16050 },
390
+ brackets: {
391
+ single: [
392
+ { lowerBound: 0, ratePct: 4 }, { lowerBound: 8500, ratePct: 4.5 }, { lowerBound: 11700, ratePct: 5.25 },
393
+ { lowerBound: 13900, ratePct: 5.5 }, { lowerBound: 80650, ratePct: 6 }, { lowerBound: 215400, ratePct: 6.85 },
394
+ { lowerBound: 1077550, ratePct: 9.65 },
395
+ ],
396
+ marriedFilingJointly: [
397
+ { lowerBound: 0, ratePct: 4 }, { lowerBound: 17150, ratePct: 4.5 }, { lowerBound: 23600, ratePct: 5.25 },
398
+ { lowerBound: 27900, ratePct: 5.5 }, { lowerBound: 161550, ratePct: 6 }, { lowerBound: 323200, ratePct: 6.85 },
399
+ { lowerBound: 2155350, ratePct: 9.65 },
400
+ ],
401
+ },
402
+ retirement: { kind: 'capped', capPerPerson: 20000, minAge: 59 },
403
+ },
404
+ NC: {
405
+ code: 'NC', name: 'North Carolina', hasIncomeTax: true, taxesSocialSecurity: false, capitalGainsAsOrdinary: true,
406
+ standardDeduction: { single: 12750, marriedFilingJointly: 25500 },
407
+ brackets: { single: [{ lowerBound: 0, ratePct: 4.25 }], marriedFilingJointly: [{ lowerBound: 0, ratePct: 4.25 }] },
408
+ retirement: { kind: 'none' },
409
+ },
410
+ ND: {
411
+ // Brackets defined on federal taxable income; std deduction ≈ federal base.
412
+ code: 'ND', name: 'North Dakota', hasIncomeTax: true, taxesSocialSecurity: false, capitalGainsAsOrdinary: true,
413
+ standardDeduction: { single: 15000, marriedFilingJointly: 30000 },
414
+ brackets: {
415
+ single: [{ lowerBound: 0, ratePct: 0 }, { lowerBound: 48475, ratePct: 1.95 }, { lowerBound: 244825, ratePct: 2.5 }],
416
+ marriedFilingJointly: [{ lowerBound: 0, ratePct: 0 }, { lowerBound: 80975, ratePct: 1.95 }, { lowerBound: 298075, ratePct: 2.5 }],
417
+ },
418
+ retirement: { kind: 'none' },
419
+ },
420
+ OH: {
421
+ code: 'OH', name: 'Ohio', hasIncomeTax: true, taxesSocialSecurity: false, capitalGainsAsOrdinary: true,
422
+ standardDeduction: { single: 0, marriedFilingJointly: 0 },
423
+ brackets: {
424
+ single: [{ lowerBound: 0, ratePct: 0 }, { lowerBound: 26050, ratePct: 2.75 }, { lowerBound: 100000, ratePct: 3.5 }],
425
+ marriedFilingJointly: [{ lowerBound: 0, ratePct: 0 }, { lowerBound: 26050, ratePct: 2.75 }, { lowerBound: 100000, ratePct: 3.5 }],
426
+ },
427
+ retirement: { kind: 'none' },
428
+ },
429
+ OK: {
430
+ code: 'OK', name: 'Oklahoma', hasIncomeTax: true, taxesSocialSecurity: false, capitalGainsAsOrdinary: true,
431
+ standardDeduction: { single: 6350, marriedFilingJointly: 12700 },
432
+ brackets: {
433
+ single: [
434
+ { lowerBound: 0, ratePct: 0.25 }, { lowerBound: 1000, ratePct: 0.75 }, { lowerBound: 2500, ratePct: 1.75 },
435
+ { lowerBound: 3750, ratePct: 2.75 }, { lowerBound: 4900, ratePct: 3.75 }, { lowerBound: 7200, ratePct: 4.75 },
436
+ ],
437
+ marriedFilingJointly: [
438
+ { lowerBound: 0, ratePct: 0.25 }, { lowerBound: 2000, ratePct: 0.75 }, { lowerBound: 5000, ratePct: 1.75 },
439
+ { lowerBound: 7500, ratePct: 2.75 }, { lowerBound: 9800, ratePct: 3.75 }, { lowerBound: 14400, ratePct: 4.75 },
440
+ ],
441
+ },
442
+ retirement: { kind: 'capped', capPerPerson: 10000 },
443
+ },
444
+ OR: {
445
+ code: 'OR', name: 'Oregon', hasIncomeTax: true, taxesSocialSecurity: false, capitalGainsAsOrdinary: true,
446
+ standardDeduction: { single: 2835, marriedFilingJointly: 5670 },
447
+ brackets: {
448
+ single: [
449
+ { lowerBound: 0, ratePct: 4.75 }, { lowerBound: 4050, ratePct: 6.75 }, { lowerBound: 10200, ratePct: 8.75 },
450
+ { lowerBound: 125000, ratePct: 9.9 },
451
+ ],
452
+ marriedFilingJointly: [
453
+ { lowerBound: 0, ratePct: 4.75 }, { lowerBound: 8100, ratePct: 6.75 }, { lowerBound: 20400, ratePct: 8.75 },
454
+ { lowerBound: 250000, ratePct: 9.9 },
455
+ ],
456
+ },
457
+ retirement: { kind: 'none' },
458
+ },
459
+ PA: {
460
+ code: 'PA', name: 'Pennsylvania', hasIncomeTax: true, taxesSocialSecurity: false, capitalGainsAsOrdinary: true,
461
+ capitalLossCarryforwardConformity: 'currentYearOnly',
462
+ capitalGainsNotes: 'Net gains are taxable at the flat PA rate; federal prior-year capital-loss carryforwards are not modeled as PA offsets.',
463
+ capitalGainsSources: ['DOCS/domain/state-tax-research/PA.md', 'https://www.revenue.pa.gov/'],
464
+ standardDeduction: { single: 0, marriedFilingJointly: 0 },
465
+ brackets: { single: [{ lowerBound: 0, ratePct: 3.07 }], marriedFilingJointly: [{ lowerBound: 0, ratePct: 3.07 }] },
466
+ retirement: { kind: 'full', minAge: 60 },
467
+ },
468
+ RI: {
469
+ code: 'RI', name: 'Rhode Island', hasIncomeTax: true, taxesSocialSecurity: true, capitalGainsAsOrdinary: true,
470
+ standardDeduction: { single: 10900, marriedFilingJointly: 21800 },
471
+ brackets: {
472
+ single: [{ lowerBound: 0, ratePct: 3.75 }, { lowerBound: 79900, ratePct: 4.75 }, { lowerBound: 181650, ratePct: 5.99 }],
473
+ marriedFilingJointly: [{ lowerBound: 0, ratePct: 3.75 }, { lowerBound: 79900, ratePct: 4.75 }, { lowerBound: 181650, ratePct: 5.99 }],
474
+ },
475
+ retirement: { kind: 'capped', capPerPerson: 20000, minAge: 67 },
476
+ },
477
+ SC: {
478
+ code: 'SC', name: 'South Carolina', hasIncomeTax: true, taxesSocialSecurity: false, capitalGainsAsOrdinary: true,
479
+ standardDeduction: { single: 15000, marriedFilingJointly: 30000 },
480
+ brackets: {
481
+ single: [{ lowerBound: 0, ratePct: 0 }, { lowerBound: 3560, ratePct: 3 }, { lowerBound: 17830, ratePct: 6 }],
482
+ marriedFilingJointly: [{ lowerBound: 0, ratePct: 0 }, { lowerBound: 3560, ratePct: 3 }, { lowerBound: 17830, ratePct: 6 }],
483
+ },
484
+ retirement: { kind: 'capped', capPerPerson: 10000, minAge: 65 },
485
+ },
486
+ SD: {
487
+ code: 'SD', name: 'South Dakota', hasIncomeTax: false, taxesSocialSecurity: false, capitalGainsAsOrdinary: false,
488
+ standardDeduction: { single: 0, marriedFilingJointly: 0 },
489
+ brackets: { single: [], marriedFilingJointly: [] }, retirement: { kind: 'none' },
490
+ },
491
+ TN: {
492
+ code: 'TN', name: 'Tennessee', hasIncomeTax: false, taxesSocialSecurity: false, capitalGainsAsOrdinary: false,
493
+ standardDeduction: { single: 0, marriedFilingJointly: 0 },
494
+ brackets: { single: [], marriedFilingJointly: [] }, retirement: { kind: 'none' },
495
+ },
496
+ TX: {
497
+ code: 'TX', name: 'Texas', hasIncomeTax: false, taxesSocialSecurity: false, capitalGainsAsOrdinary: false,
498
+ standardDeduction: { single: 0, marriedFilingJointly: 0 },
499
+ brackets: { single: [], marriedFilingJointly: [] }, retirement: { kind: 'none' },
500
+ },
501
+ UT: {
502
+ code: 'UT', name: 'Utah', hasIncomeTax: true, taxesSocialSecurity: true, capitalGainsAsOrdinary: true,
503
+ standardDeduction: { single: 0, marriedFilingJointly: 0 },
504
+ brackets: { single: [{ lowerBound: 0, ratePct: 4.5 }], marriedFilingJointly: [{ lowerBound: 0, ratePct: 4.5 }] },
505
+ retirement: { kind: 'none' },
506
+ },
507
+ VT: {
508
+ code: 'VT', name: 'Vermont', hasIncomeTax: true, taxesSocialSecurity: true, capitalGainsAsOrdinary: true,
509
+ standardDeduction: { single: 7400, marriedFilingJointly: 14850 },
510
+ brackets: {
511
+ single: [
512
+ { lowerBound: 0, ratePct: 3.35 }, { lowerBound: 47900, ratePct: 6.6 }, { lowerBound: 116000, ratePct: 7.6 },
513
+ { lowerBound: 242000, ratePct: 8.75 },
514
+ ],
515
+ marriedFilingJointly: [
516
+ { lowerBound: 0, ratePct: 3.35 }, { lowerBound: 79950, ratePct: 6.6 }, { lowerBound: 193300, ratePct: 7.6 },
517
+ { lowerBound: 294600, ratePct: 8.75 },
518
+ ],
519
+ },
520
+ retirement: { kind: 'none' },
521
+ },
522
+ VA: {
523
+ code: 'VA', name: 'Virginia', hasIncomeTax: true, taxesSocialSecurity: false, capitalGainsAsOrdinary: true,
524
+ standardDeduction: { single: 8750, marriedFilingJointly: 17500 },
525
+ brackets: {
526
+ single: [
527
+ { lowerBound: 0, ratePct: 2 }, { lowerBound: 3000, ratePct: 3 }, { lowerBound: 5000, ratePct: 5 },
528
+ { lowerBound: 17000, ratePct: 5.75 },
529
+ ],
530
+ marriedFilingJointly: [
531
+ { lowerBound: 0, ratePct: 2 }, { lowerBound: 3000, ratePct: 3 }, { lowerBound: 5000, ratePct: 5 },
532
+ { lowerBound: 17000, ratePct: 5.75 },
533
+ ],
534
+ },
535
+ retirement: { kind: 'capped', capPerPerson: 12000, minAge: 65 },
536
+ },
537
+ WA: {
538
+ // No broad income tax; a 7% tax on large long-term gains is out of scope.
539
+ code: 'WA', name: 'Washington', hasIncomeTax: false, taxesSocialSecurity: false, capitalGainsAsOrdinary: true,
540
+ standardDeduction: { single: 0, marriedFilingJointly: 0 },
541
+ brackets: { single: [], marriedFilingJointly: [] }, retirement: { kind: 'none' },
542
+ },
543
+ WV: {
544
+ // Social Security fully exempt from 2026 (phase-out complete).
545
+ code: 'WV', name: 'West Virginia', hasIncomeTax: true, taxesSocialSecurity: false, capitalGainsAsOrdinary: true,
546
+ standardDeduction: { single: 0, marriedFilingJointly: 0 },
547
+ brackets: {
548
+ single: [
549
+ { lowerBound: 0, ratePct: 2.22 }, { lowerBound: 10000, ratePct: 2.96 }, { lowerBound: 25000, ratePct: 3.33 },
550
+ { lowerBound: 40000, ratePct: 4.44 }, { lowerBound: 60000, ratePct: 4.82 },
551
+ ],
552
+ marriedFilingJointly: [
553
+ { lowerBound: 0, ratePct: 2.22 }, { lowerBound: 10000, ratePct: 2.96 }, { lowerBound: 25000, ratePct: 3.33 },
554
+ { lowerBound: 40000, ratePct: 4.44 }, { lowerBound: 60000, ratePct: 4.82 },
555
+ ],
556
+ },
557
+ retirement: { kind: 'capped', capPerPerson: 8000, minAge: 65 },
558
+ },
559
+ WI: {
560
+ code: 'WI', name: 'Wisconsin', hasIncomeTax: true, taxesSocialSecurity: false, capitalGainsAsOrdinary: true,
561
+ standardDeduction: { single: 13560, marriedFilingJointly: 25110 },
562
+ brackets: {
563
+ single: [
564
+ { lowerBound: 0, ratePct: 3.5 }, { lowerBound: 14680, ratePct: 4.4 }, { lowerBound: 50480, ratePct: 5.3 },
565
+ { lowerBound: 323290, ratePct: 7.65 },
566
+ ],
567
+ marriedFilingJointly: [
568
+ { lowerBound: 0, ratePct: 3.5 }, { lowerBound: 19580, ratePct: 4.4 }, { lowerBound: 67300, ratePct: 5.3 },
569
+ { lowerBound: 431060, ratePct: 7.65 },
570
+ ],
571
+ },
572
+ retirement: { kind: 'capped', capPerPerson: 24000, minAge: 67 },
573
+ },
574
+ WY: {
575
+ code: 'WY', name: 'Wyoming', hasIncomeTax: false, taxesSocialSecurity: false, capitalGainsAsOrdinary: true,
576
+ standardDeduction: { single: 0, marriedFilingJointly: 0 },
577
+ brackets: { single: [], marriedFilingJointly: [] }, retirement: { kind: 'none' },
578
+ },
579
+ },
580
+ };
581
+ export const stateYear2026 = splitRetirementBuckets(rawStateYear2026);
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Typed access to per-state tax packs. Mirrors the federal `packForYear`
3
+ * pattern: future years use the latest published pack (nominal brackets, so
4
+ * bracket creep is modeled). States with no entry return undefined — the
5
+ * caller falls back to the flat effective-rate override.
6
+ */
7
+ import type { StateTaxParams } from './types.js';
8
+ export declare const LATEST_STATE_PACK_YEAR: number;
9
+ /** Tax parameters for a state in a year, or undefined if that state isn't modeled yet. */
10
+ export declare function stateParamsFor(code: string, year: number): StateTaxParams | undefined;
11
+ /** Two-letter codes with a modeled pack in the latest year (for UI hints). */
12
+ export declare function modeledStateCodes(): string[];
13
+ export type { StateTaxParams, StateTaxBracket, StateRetirementExclusion, StateTaxPack } from './types.js';
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Typed access to per-state tax packs. Mirrors the federal `packForYear`
3
+ * pattern: future years use the latest published pack (nominal brackets, so
4
+ * bracket creep is modeled). States with no entry return undefined — the
5
+ * caller falls back to the flat effective-rate override.
6
+ */
7
+ import { stateYear2026 } from './data/year2026.js';
8
+ const packs = [stateYear2026];
9
+ // Keep sorted ascending by year as packs are added each fall.
10
+ export const LATEST_STATE_PACK_YEAR = packs[packs.length - 1].year;
11
+ function statesPackForYear(year) {
12
+ const exact = packs.find((p) => p.year === year);
13
+ if (exact)
14
+ return exact;
15
+ if (year > LATEST_STATE_PACK_YEAR)
16
+ return packs[packs.length - 1];
17
+ return packs[0];
18
+ }
19
+ /** Tax parameters for a state in a year, or undefined if that state isn't modeled yet. */
20
+ export function stateParamsFor(code, year) {
21
+ return statesPackForYear(year).states[code.toUpperCase()];
22
+ }
23
+ /** Two-letter codes with a modeled pack in the latest year (for UI hints). */
24
+ export function modeledStateCodes() {
25
+ return Object.keys(packs[packs.length - 1].states).sort();
26
+ }