Compare commits
1 Commits
extra-seo-
...
3f296689b2
Author | SHA1 | Date | |
---|---|---|---|
3f296689b2 |
10
package.json
10
package.json
@@ -23,7 +23,7 @@
|
|||||||
"@t3-oss/env-nextjs": "^0.13.0",
|
"@t3-oss/env-nextjs": "^0.13.0",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"lucide-react": "^0.539.0",
|
"lucide-react": "^0.536.0",
|
||||||
"next": "^15.4.1",
|
"next": "^15.4.1",
|
||||||
"next-plausible": "^3.12.4",
|
"next-plausible": "^3.12.4",
|
||||||
"react": "^19.0.0",
|
"react": "^19.0.0",
|
||||||
@@ -36,10 +36,10 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/eslintrc": "3.3.1",
|
"@eslint/eslintrc": "3.3.1",
|
||||||
"@tailwindcss/postcss": "4.1.11",
|
"@tailwindcss/postcss": "4.1.11",
|
||||||
"@types/node": "22.17.1",
|
"@types/node": "22.17.0",
|
||||||
"@types/react": "19.1.8",
|
"@types/react": "19.1.8",
|
||||||
"@types/react-dom": "19.1.6",
|
"@types/react-dom": "19.1.6",
|
||||||
"eslint": "9.33.0",
|
"eslint": "9.32.0",
|
||||||
"eslint-config-next": "15.4.4",
|
"eslint-config-next": "15.4.4",
|
||||||
"eslint-plugin-react-hooks": "5.2.0",
|
"eslint-plugin-react-hooks": "5.2.0",
|
||||||
"postcss": "8.5.6",
|
"postcss": "8.5.6",
|
||||||
@@ -48,10 +48,10 @@
|
|||||||
"tailwindcss": "4.1.11",
|
"tailwindcss": "4.1.11",
|
||||||
"tw-animate-css": "1.3.6",
|
"tw-animate-css": "1.3.6",
|
||||||
"typescript": "5.9.2",
|
"typescript": "5.9.2",
|
||||||
"typescript-eslint": "8.39.0"
|
"typescript-eslint": "8.38.0"
|
||||||
},
|
},
|
||||||
"ct3aMetadata": {
|
"ct3aMetadata": {
|
||||||
"initVersion": "7.39.3"
|
"initVersion": "7.39.3"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@10.14.0"
|
"packageManager": "pnpm@10.13.1"
|
||||||
}
|
}
|
||||||
|
359
pnpm-lock.yaml
generated
359
pnpm-lock.yaml
generated
@@ -10,7 +10,7 @@ importers:
|
|||||||
dependencies:
|
dependencies:
|
||||||
'@hookform/resolvers':
|
'@hookform/resolvers':
|
||||||
specifier: ^5.0.1
|
specifier: ^5.0.1
|
||||||
version: 5.2.1(react-hook-form@7.62.0(react@19.1.0))
|
version: 5.2.1(react-hook-form@7.61.1(react@19.1.0))
|
||||||
'@radix-ui/react-accordion':
|
'@radix-ui/react-accordion':
|
||||||
specifier: ^1.2.8
|
specifier: ^1.2.8
|
||||||
version: 1.2.11(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
version: 1.2.11(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||||
@@ -28,7 +28,7 @@ importers:
|
|||||||
version: 1.2.3(@types/react@19.1.8)(react@19.1.0)
|
version: 1.2.3(@types/react@19.1.8)(react@19.1.0)
|
||||||
'@t3-oss/env-nextjs':
|
'@t3-oss/env-nextjs':
|
||||||
specifier: ^0.13.0
|
specifier: ^0.13.0
|
||||||
version: 0.13.8(typescript@5.9.2)(zod@4.0.16)
|
version: 0.13.8(typescript@5.9.2)(zod@4.0.14)
|
||||||
class-variance-authority:
|
class-variance-authority:
|
||||||
specifier: ^0.7.1
|
specifier: ^0.7.1
|
||||||
version: 0.7.1
|
version: 0.7.1
|
||||||
@@ -36,8 +36,8 @@ importers:
|
|||||||
specifier: ^2.1.1
|
specifier: ^2.1.1
|
||||||
version: 2.1.1
|
version: 2.1.1
|
||||||
lucide-react:
|
lucide-react:
|
||||||
specifier: ^0.539.0
|
specifier: ^0.536.0
|
||||||
version: 0.539.0(react@19.1.0)
|
version: 0.536.0(react@19.1.0)
|
||||||
next:
|
next:
|
||||||
specifier: ^15.4.1
|
specifier: ^15.4.1
|
||||||
version: 15.4.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
version: 15.4.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||||
@@ -52,7 +52,7 @@ importers:
|
|||||||
version: 19.1.0(react@19.1.0)
|
version: 19.1.0(react@19.1.0)
|
||||||
react-hook-form:
|
react-hook-form:
|
||||||
specifier: ^7.56.1
|
specifier: ^7.56.1
|
||||||
version: 7.62.0(react@19.1.0)
|
version: 7.61.1(react@19.1.0)
|
||||||
recharts:
|
recharts:
|
||||||
specifier: ^2.15.3
|
specifier: ^2.15.3
|
||||||
version: 2.15.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
version: 2.15.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||||
@@ -61,7 +61,7 @@ importers:
|
|||||||
version: 3.3.1
|
version: 3.3.1
|
||||||
zod:
|
zod:
|
||||||
specifier: ^4.0.0
|
specifier: ^4.0.0
|
||||||
version: 4.0.16
|
version: 4.0.14
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@eslint/eslintrc':
|
'@eslint/eslintrc':
|
||||||
specifier: 3.3.1
|
specifier: 3.3.1
|
||||||
@@ -70,8 +70,8 @@ importers:
|
|||||||
specifier: 4.1.11
|
specifier: 4.1.11
|
||||||
version: 4.1.11
|
version: 4.1.11
|
||||||
'@types/node':
|
'@types/node':
|
||||||
specifier: 22.17.1
|
specifier: 22.17.0
|
||||||
version: 22.17.1
|
version: 22.17.0
|
||||||
'@types/react':
|
'@types/react':
|
||||||
specifier: 19.1.8
|
specifier: 19.1.8
|
||||||
version: 19.1.8
|
version: 19.1.8
|
||||||
@@ -79,14 +79,14 @@ importers:
|
|||||||
specifier: 19.1.6
|
specifier: 19.1.6
|
||||||
version: 19.1.6(@types/react@19.1.8)
|
version: 19.1.6(@types/react@19.1.8)
|
||||||
eslint:
|
eslint:
|
||||||
specifier: 9.33.0
|
specifier: 9.32.0
|
||||||
version: 9.33.0(jiti@2.4.2)
|
version: 9.32.0(jiti@2.4.2)
|
||||||
eslint-config-next:
|
eslint-config-next:
|
||||||
specifier: 15.4.4
|
specifier: 15.4.4
|
||||||
version: 15.4.4(eslint@9.33.0(jiti@2.4.2))(typescript@5.9.2)
|
version: 15.4.4(eslint@9.32.0(jiti@2.4.2))(typescript@5.9.2)
|
||||||
eslint-plugin-react-hooks:
|
eslint-plugin-react-hooks:
|
||||||
specifier: 5.2.0
|
specifier: 5.2.0
|
||||||
version: 5.2.0(eslint@9.33.0(jiti@2.4.2))
|
version: 5.2.0(eslint@9.32.0(jiti@2.4.2))
|
||||||
postcss:
|
postcss:
|
||||||
specifier: 8.5.6
|
specifier: 8.5.6
|
||||||
version: 8.5.6
|
version: 8.5.6
|
||||||
@@ -106,8 +106,8 @@ importers:
|
|||||||
specifier: 5.9.2
|
specifier: 5.9.2
|
||||||
version: 5.9.2
|
version: 5.9.2
|
||||||
typescript-eslint:
|
typescript-eslint:
|
||||||
specifier: 8.39.0
|
specifier: 8.38.0
|
||||||
version: 8.39.0(eslint@9.33.0(jiti@2.4.2))(typescript@5.9.2)
|
version: 8.38.0(eslint@9.32.0(jiti@2.4.2))(typescript@5.9.2)
|
||||||
|
|
||||||
packages:
|
packages:
|
||||||
|
|
||||||
@@ -146,28 +146,28 @@ packages:
|
|||||||
resolution: {integrity: sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==}
|
resolution: {integrity: sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==}
|
||||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||||
|
|
||||||
'@eslint/config-helpers@0.3.1':
|
'@eslint/config-helpers@0.3.0':
|
||||||
resolution: {integrity: sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==}
|
resolution: {integrity: sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==}
|
||||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||||
|
|
||||||
'@eslint/core@0.15.2':
|
'@eslint/core@0.15.1':
|
||||||
resolution: {integrity: sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==}
|
resolution: {integrity: sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==}
|
||||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||||
|
|
||||||
'@eslint/eslintrc@3.3.1':
|
'@eslint/eslintrc@3.3.1':
|
||||||
resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==}
|
resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==}
|
||||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||||
|
|
||||||
'@eslint/js@9.33.0':
|
'@eslint/js@9.32.0':
|
||||||
resolution: {integrity: sha512-5K1/mKhWaMfreBGJTwval43JJmkip0RmM+3+IuqupeSKNC/Th2Kc7ucaq5ovTSra/OOKB9c58CGSz3QMVbWt0A==}
|
resolution: {integrity: sha512-BBpRFZK3eX6uMLKz8WxFOBIFFcGFJ/g8XuwjTHCqHROSIsopI+ddn/d5Cfh36+7+e5edVS8dbSHnBNhrLEX0zg==}
|
||||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||||
|
|
||||||
'@eslint/object-schema@2.1.6':
|
'@eslint/object-schema@2.1.6':
|
||||||
resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==}
|
resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==}
|
||||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||||
|
|
||||||
'@eslint/plugin-kit@0.3.5':
|
'@eslint/plugin-kit@0.3.4':
|
||||||
resolution: {integrity: sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==}
|
resolution: {integrity: sha512-Ul5l+lHEcw3L5+k8POx6r74mxEYKG5kOb6Xpy2gCRW6zweT6TEhAf8vhxGgjhqrd/VO/Dirhsb+1hNpD1ue9hw==}
|
||||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||||
|
|
||||||
'@floating-ui/core@1.6.9':
|
'@floating-ui/core@1.6.9':
|
||||||
@@ -917,8 +917,8 @@ packages:
|
|||||||
'@types/json5@0.0.29':
|
'@types/json5@0.0.29':
|
||||||
resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==}
|
resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==}
|
||||||
|
|
||||||
'@types/node@22.17.1':
|
'@types/node@22.17.0':
|
||||||
resolution: {integrity: sha512-y3tBaz+rjspDTylNjAX37jEC3TETEFGNJL6uQDxwF9/8GLLIjW1rvVHlynyuUKMnMr1Roq8jOv3vkopBjC4/VA==}
|
resolution: {integrity: sha512-bbAKTCqX5aNVryi7qXVMi+OkB3w/OyblodicMbvE38blyAz7GxXf6XYhklokijuPwwVg9sDLKRxt0ZHXQwZVfQ==}
|
||||||
|
|
||||||
'@types/react-dom@19.1.6':
|
'@types/react-dom@19.1.6':
|
||||||
resolution: {integrity: sha512-4hOiT/dwO8Ko0gV1m/TJZYk3y0KBnY9vzDh7W+DH17b2HFSOGgdj33dhihPeuy3l0q23+4e+hoXHV6hCC4dCXw==}
|
resolution: {integrity: sha512-4hOiT/dwO8Ko0gV1m/TJZYk3y0KBnY9vzDh7W+DH17b2HFSOGgdj33dhihPeuy3l0q23+4e+hoXHV6hCC4dCXw==}
|
||||||
@@ -936,14 +936,6 @@ packages:
|
|||||||
eslint: ^8.57.0 || ^9.0.0
|
eslint: ^8.57.0 || ^9.0.0
|
||||||
typescript: '>=4.8.4 <5.9.0'
|
typescript: '>=4.8.4 <5.9.0'
|
||||||
|
|
||||||
'@typescript-eslint/eslint-plugin@8.39.0':
|
|
||||||
resolution: {integrity: sha512-bhEz6OZeUR+O/6yx9Jk6ohX6H9JSFTaiY0v9/PuKT3oGK0rn0jNplLmyFUGV+a9gfYnVNwGDwS/UkLIuXNb2Rw==}
|
|
||||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
|
||||||
peerDependencies:
|
|
||||||
'@typescript-eslint/parser': ^8.39.0
|
|
||||||
eslint: ^8.57.0 || ^9.0.0
|
|
||||||
typescript: '>=4.8.4 <6.0.0'
|
|
||||||
|
|
||||||
'@typescript-eslint/parser@8.38.0':
|
'@typescript-eslint/parser@8.38.0':
|
||||||
resolution: {integrity: sha512-Zhy8HCvBUEfBECzIl1PKqF4p11+d0aUJS1GeUiuqK9WmOug8YCmC4h4bjyBvMyAMI9sbRczmrYL5lKg/YMbrcQ==}
|
resolution: {integrity: sha512-Zhy8HCvBUEfBECzIl1PKqF4p11+d0aUJS1GeUiuqK9WmOug8YCmC4h4bjyBvMyAMI9sbRczmrYL5lKg/YMbrcQ==}
|
||||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||||
@@ -951,45 +943,22 @@ packages:
|
|||||||
eslint: ^8.57.0 || ^9.0.0
|
eslint: ^8.57.0 || ^9.0.0
|
||||||
typescript: '>=4.8.4 <5.9.0'
|
typescript: '>=4.8.4 <5.9.0'
|
||||||
|
|
||||||
'@typescript-eslint/parser@8.39.0':
|
|
||||||
resolution: {integrity: sha512-g3WpVQHngx0aLXn6kfIYCZxM6rRJlWzEkVpqEFLT3SgEDsp9cpCbxxgwnE504q4H+ruSDh/VGS6nqZIDynP+vg==}
|
|
||||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
|
||||||
peerDependencies:
|
|
||||||
eslint: ^8.57.0 || ^9.0.0
|
|
||||||
typescript: '>=4.8.4 <6.0.0'
|
|
||||||
|
|
||||||
'@typescript-eslint/project-service@8.38.0':
|
'@typescript-eslint/project-service@8.38.0':
|
||||||
resolution: {integrity: sha512-dbK7Jvqcb8c9QfH01YB6pORpqX1mn5gDZc9n63Ak/+jD67oWXn3Gs0M6vddAN+eDXBCS5EmNWzbSxsn9SzFWWg==}
|
resolution: {integrity: sha512-dbK7Jvqcb8c9QfH01YB6pORpqX1mn5gDZc9n63Ak/+jD67oWXn3Gs0M6vddAN+eDXBCS5EmNWzbSxsn9SzFWWg==}
|
||||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
typescript: '>=4.8.4 <5.9.0'
|
typescript: '>=4.8.4 <5.9.0'
|
||||||
|
|
||||||
'@typescript-eslint/project-service@8.39.0':
|
|
||||||
resolution: {integrity: sha512-CTzJqaSq30V/Z2Og9jogzZt8lJRR5TKlAdXmWgdu4hgcC9Kww5flQ+xFvMxIBWVNdxJO7OifgdOK4PokMIWPew==}
|
|
||||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
|
||||||
peerDependencies:
|
|
||||||
typescript: '>=4.8.4 <6.0.0'
|
|
||||||
|
|
||||||
'@typescript-eslint/scope-manager@8.38.0':
|
'@typescript-eslint/scope-manager@8.38.0':
|
||||||
resolution: {integrity: sha512-WJw3AVlFFcdT9Ri1xs/lg8LwDqgekWXWhH3iAF+1ZM+QPd7oxQ6jvtW/JPwzAScxitILUIFs0/AnQ/UWHzbATQ==}
|
resolution: {integrity: sha512-WJw3AVlFFcdT9Ri1xs/lg8LwDqgekWXWhH3iAF+1ZM+QPd7oxQ6jvtW/JPwzAScxitILUIFs0/AnQ/UWHzbATQ==}
|
||||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||||
|
|
||||||
'@typescript-eslint/scope-manager@8.39.0':
|
|
||||||
resolution: {integrity: sha512-8QOzff9UKxOh6npZQ/4FQu4mjdOCGSdO3p44ww0hk8Vu+IGbg0tB/H1LcTARRDzGCC8pDGbh2rissBuuoPgH8A==}
|
|
||||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
|
||||||
|
|
||||||
'@typescript-eslint/tsconfig-utils@8.38.0':
|
'@typescript-eslint/tsconfig-utils@8.38.0':
|
||||||
resolution: {integrity: sha512-Lum9RtSE3EroKk/bYns+sPOodqb2Fv50XOl/gMviMKNvanETUuUcC9ObRbzrJ4VSd2JalPqgSAavwrPiPvnAiQ==}
|
resolution: {integrity: sha512-Lum9RtSE3EroKk/bYns+sPOodqb2Fv50XOl/gMviMKNvanETUuUcC9ObRbzrJ4VSd2JalPqgSAavwrPiPvnAiQ==}
|
||||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
typescript: '>=4.8.4 <5.9.0'
|
typescript: '>=4.8.4 <5.9.0'
|
||||||
|
|
||||||
'@typescript-eslint/tsconfig-utils@8.39.0':
|
|
||||||
resolution: {integrity: sha512-Fd3/QjmFV2sKmvv3Mrj8r6N8CryYiCS8Wdb/6/rgOXAWGcFuc+VkQuG28uk/4kVNVZBQuuDHEDUpo/pQ32zsIQ==}
|
|
||||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
|
||||||
peerDependencies:
|
|
||||||
typescript: '>=4.8.4 <6.0.0'
|
|
||||||
|
|
||||||
'@typescript-eslint/type-utils@8.38.0':
|
'@typescript-eslint/type-utils@8.38.0':
|
||||||
resolution: {integrity: sha512-c7jAvGEZVf0ao2z+nnz8BUaHZD09Agbh+DY7qvBQqLiz8uJzRgVPj5YvOh8I8uEiH8oIUGIfHzMwUcGVco/SJg==}
|
resolution: {integrity: sha512-c7jAvGEZVf0ao2z+nnz8BUaHZD09Agbh+DY7qvBQqLiz8uJzRgVPj5YvOh8I8uEiH8oIUGIfHzMwUcGVco/SJg==}
|
||||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||||
@@ -997,33 +966,16 @@ packages:
|
|||||||
eslint: ^8.57.0 || ^9.0.0
|
eslint: ^8.57.0 || ^9.0.0
|
||||||
typescript: '>=4.8.4 <5.9.0'
|
typescript: '>=4.8.4 <5.9.0'
|
||||||
|
|
||||||
'@typescript-eslint/type-utils@8.39.0':
|
|
||||||
resolution: {integrity: sha512-6B3z0c1DXVT2vYA9+z9axjtc09rqKUPRmijD5m9iv8iQpHBRYRMBcgxSiKTZKm6FwWw1/cI4v6em35OsKCiN5Q==}
|
|
||||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
|
||||||
peerDependencies:
|
|
||||||
eslint: ^8.57.0 || ^9.0.0
|
|
||||||
typescript: '>=4.8.4 <6.0.0'
|
|
||||||
|
|
||||||
'@typescript-eslint/types@8.38.0':
|
'@typescript-eslint/types@8.38.0':
|
||||||
resolution: {integrity: sha512-wzkUfX3plUqij4YwWaJyqhiPE5UCRVlFpKn1oCRn2O1bJ592XxWJj8ROQ3JD5MYXLORW84063z3tZTb/cs4Tyw==}
|
resolution: {integrity: sha512-wzkUfX3plUqij4YwWaJyqhiPE5UCRVlFpKn1oCRn2O1bJ592XxWJj8ROQ3JD5MYXLORW84063z3tZTb/cs4Tyw==}
|
||||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||||
|
|
||||||
'@typescript-eslint/types@8.39.0':
|
|
||||||
resolution: {integrity: sha512-ArDdaOllnCj3yn/lzKn9s0pBQYmmyme/v1HbGIGB0GB/knFI3fWMHloC+oYTJW46tVbYnGKTMDK4ah1sC2v0Kg==}
|
|
||||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
|
||||||
|
|
||||||
'@typescript-eslint/typescript-estree@8.38.0':
|
'@typescript-eslint/typescript-estree@8.38.0':
|
||||||
resolution: {integrity: sha512-fooELKcAKzxux6fA6pxOflpNS0jc+nOQEEOipXFNjSlBS6fqrJOVY/whSn70SScHrcJ2LDsxWrneFoWYSVfqhQ==}
|
resolution: {integrity: sha512-fooELKcAKzxux6fA6pxOflpNS0jc+nOQEEOipXFNjSlBS6fqrJOVY/whSn70SScHrcJ2LDsxWrneFoWYSVfqhQ==}
|
||||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
typescript: '>=4.8.4 <5.9.0'
|
typescript: '>=4.8.4 <5.9.0'
|
||||||
|
|
||||||
'@typescript-eslint/typescript-estree@8.39.0':
|
|
||||||
resolution: {integrity: sha512-ndWdiflRMvfIgQRpckQQLiB5qAKQ7w++V4LlCHwp62eym1HLB/kw7D9f2e8ytONls/jt89TEasgvb+VwnRprsw==}
|
|
||||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
|
||||||
peerDependencies:
|
|
||||||
typescript: '>=4.8.4 <6.0.0'
|
|
||||||
|
|
||||||
'@typescript-eslint/utils@8.38.0':
|
'@typescript-eslint/utils@8.38.0':
|
||||||
resolution: {integrity: sha512-hHcMA86Hgt+ijJlrD8fX0j1j8w4C92zue/8LOPAFioIno+W0+L7KqE8QZKCcPGc/92Vs9x36w/4MPTJhqXdyvg==}
|
resolution: {integrity: sha512-hHcMA86Hgt+ijJlrD8fX0j1j8w4C92zue/8LOPAFioIno+W0+L7KqE8QZKCcPGc/92Vs9x36w/4MPTJhqXdyvg==}
|
||||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||||
@@ -1031,21 +983,10 @@ packages:
|
|||||||
eslint: ^8.57.0 || ^9.0.0
|
eslint: ^8.57.0 || ^9.0.0
|
||||||
typescript: '>=4.8.4 <5.9.0'
|
typescript: '>=4.8.4 <5.9.0'
|
||||||
|
|
||||||
'@typescript-eslint/utils@8.39.0':
|
|
||||||
resolution: {integrity: sha512-4GVSvNA0Vx1Ktwvf4sFE+exxJ3QGUorQG1/A5mRfRNZtkBT2xrA/BCO2H0eALx/PnvCS6/vmYwRdDA41EoffkQ==}
|
|
||||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
|
||||||
peerDependencies:
|
|
||||||
eslint: ^8.57.0 || ^9.0.0
|
|
||||||
typescript: '>=4.8.4 <6.0.0'
|
|
||||||
|
|
||||||
'@typescript-eslint/visitor-keys@8.38.0':
|
'@typescript-eslint/visitor-keys@8.38.0':
|
||||||
resolution: {integrity: sha512-pWrTcoFNWuwHlA9CvlfSsGWs14JxfN1TH25zM5L7o0pRLhsoZkDnTsXfQRJBEWJoV5DL0jf+Z+sxiud+K0mq1g==}
|
resolution: {integrity: sha512-pWrTcoFNWuwHlA9CvlfSsGWs14JxfN1TH25zM5L7o0pRLhsoZkDnTsXfQRJBEWJoV5DL0jf+Z+sxiud+K0mq1g==}
|
||||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||||
|
|
||||||
'@typescript-eslint/visitor-keys@8.39.0':
|
|
||||||
resolution: {integrity: sha512-ldgiJ+VAhQCfIjeOgu8Kj5nSxds0ktPOSO9p4+0VDH2R2pLvQraaM5Oen2d7NxzMCm+Sn/vJT+mv2H5u6b/3fA==}
|
|
||||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
|
||||||
|
|
||||||
'@unrs/resolver-binding-darwin-arm64@1.7.2':
|
'@unrs/resolver-binding-darwin-arm64@1.7.2':
|
||||||
resolution: {integrity: sha512-vxtBno4xvowwNmO/ASL0Y45TpHqmNkAaDtz4Jqb+clmcVSSl8XCG/PNFFkGsXXXS6AMjP+ja/TtNCFFa1QwLRg==}
|
resolution: {integrity: sha512-vxtBno4xvowwNmO/ASL0Y45TpHqmNkAaDtz4Jqb+clmcVSSl8XCG/PNFFkGsXXXS6AMjP+ja/TtNCFFa1QwLRg==}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
@@ -1521,8 +1462,8 @@ packages:
|
|||||||
resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==}
|
resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==}
|
||||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||||
|
|
||||||
eslint@9.33.0:
|
eslint@9.32.0:
|
||||||
resolution: {integrity: sha512-TS9bTNIryDzStCpJN93aC5VRSW3uTx9sClUn4B87pwiCaJh220otoI0X8mJKr+VcPtniMdN8GKjlwgWGUv5ZKA==}
|
resolution: {integrity: sha512-LSehfdpgMeWcTZkWZVIJl+tkZ2nuSkyyB9C27MZqFWXuph7DvaowgcTvKqxvpLW1JZIk8PN7hFY3Rj9LQ7m7lg==}
|
||||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -1947,8 +1888,8 @@ packages:
|
|||||||
resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
|
resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
lucide-react@0.539.0:
|
lucide-react@0.536.0:
|
||||||
resolution: {integrity: sha512-VVISr+VF2krO91FeuCrm1rSOLACQUYVy7NQkzrOty52Y8TlTPcXcMdQFj9bYzBgXbWCiywlwSZ3Z8u6a+6bMlg==}
|
resolution: {integrity: sha512-2PgvNa9v+qz4Jt/ni8vPLt4jwoFybXHuubQT8fv4iCW5TjDxkbZjNZZHa485ad73NSEn/jdsEtU57eE1g+ma8A==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||||
|
|
||||||
@@ -2205,8 +2146,8 @@ packages:
|
|||||||
peerDependencies:
|
peerDependencies:
|
||||||
react: ^19.1.0
|
react: ^19.1.0
|
||||||
|
|
||||||
react-hook-form@7.62.0:
|
react-hook-form@7.61.1:
|
||||||
resolution: {integrity: sha512-7KWFejc98xqG/F4bAxpL41NB3o1nnvQO1RWZT3TqRZYL8RryQETGfEdVnJN2fy1crCiBLLjkRBVK05j24FxJGA==}
|
resolution: {integrity: sha512-2vbXUFDYgqEgM2RcXcAT2PwDW/80QARi+PKmHy5q2KhuKvOlG8iIYgf7eIlIANR5trW9fJbP4r5aub3a4egsew==}
|
||||||
engines: {node: '>=18.0.0'}
|
engines: {node: '>=18.0.0'}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
react: ^16.8.0 || ^17 || ^18 || ^19
|
react: ^16.8.0 || ^17 || ^18 || ^19
|
||||||
@@ -2493,12 +2434,12 @@ packages:
|
|||||||
resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==}
|
resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
typescript-eslint@8.39.0:
|
typescript-eslint@8.38.0:
|
||||||
resolution: {integrity: sha512-lH8FvtdtzcHJCkMOKnN73LIn6SLTpoojgJqDAxPm1jCR14eWSGPX8ul/gggBdPMk/d5+u9V854vTYQ8T5jF/1Q==}
|
resolution: {integrity: sha512-FsZlrYK6bPDGoLeZRuvx2v6qrM03I0U0SnfCLPs/XCCPCFD80xU9Pg09H/K+XFa68uJuZo7l/Xhs+eDRg2l3hg==}
|
||||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
eslint: ^8.57.0 || ^9.0.0
|
eslint: ^8.57.0 || ^9.0.0
|
||||||
typescript: '>=4.8.4 <6.0.0'
|
typescript: '>=4.8.4 <5.9.0'
|
||||||
|
|
||||||
typescript@5.9.2:
|
typescript@5.9.2:
|
||||||
resolution: {integrity: sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==}
|
resolution: {integrity: sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==}
|
||||||
@@ -2574,8 +2515,8 @@ packages:
|
|||||||
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
|
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
zod@4.0.16:
|
zod@4.0.14:
|
||||||
resolution: {integrity: sha512-Djo/cM339grjI7/HmN+ixYO2FzEMcWr/On50UlQ/RjrWK1I/hPpWhpC76heCptnRFpH0LMwrEbUY50HDc0V8wg==}
|
resolution: {integrity: sha512-nGFJTnJN6cM2v9kXL+SOBq3AtjQby3Mv5ySGFof5UGRHrRioSJ5iG680cYNjE/yWk671nROcpPj4hAS8nyLhSw==}
|
||||||
|
|
||||||
snapshots:
|
snapshots:
|
||||||
|
|
||||||
@@ -2606,9 +2547,9 @@ snapshots:
|
|||||||
tslib: 2.8.1
|
tslib: 2.8.1
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@eslint-community/eslint-utils@4.7.0(eslint@9.33.0(jiti@2.4.2))':
|
'@eslint-community/eslint-utils@4.7.0(eslint@9.32.0(jiti@2.4.2))':
|
||||||
dependencies:
|
dependencies:
|
||||||
eslint: 9.33.0(jiti@2.4.2)
|
eslint: 9.32.0(jiti@2.4.2)
|
||||||
eslint-visitor-keys: 3.4.3
|
eslint-visitor-keys: 3.4.3
|
||||||
|
|
||||||
'@eslint-community/regexpp@4.12.1': {}
|
'@eslint-community/regexpp@4.12.1': {}
|
||||||
@@ -2621,9 +2562,9 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
'@eslint/config-helpers@0.3.1': {}
|
'@eslint/config-helpers@0.3.0': {}
|
||||||
|
|
||||||
'@eslint/core@0.15.2':
|
'@eslint/core@0.15.1':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/json-schema': 7.0.15
|
'@types/json-schema': 7.0.15
|
||||||
|
|
||||||
@@ -2641,13 +2582,13 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
'@eslint/js@9.33.0': {}
|
'@eslint/js@9.32.0': {}
|
||||||
|
|
||||||
'@eslint/object-schema@2.1.6': {}
|
'@eslint/object-schema@2.1.6': {}
|
||||||
|
|
||||||
'@eslint/plugin-kit@0.3.5':
|
'@eslint/plugin-kit@0.3.4':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@eslint/core': 0.15.2
|
'@eslint/core': 0.15.1
|
||||||
levn: 0.4.1
|
levn: 0.4.1
|
||||||
|
|
||||||
'@floating-ui/core@1.6.9':
|
'@floating-ui/core@1.6.9':
|
||||||
@@ -2667,10 +2608,10 @@ snapshots:
|
|||||||
|
|
||||||
'@floating-ui/utils@0.2.9': {}
|
'@floating-ui/utils@0.2.9': {}
|
||||||
|
|
||||||
'@hookform/resolvers@5.2.1(react-hook-form@7.62.0(react@19.1.0))':
|
'@hookform/resolvers@5.2.1(react-hook-form@7.61.1(react@19.1.0))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@standard-schema/utils': 0.3.0
|
'@standard-schema/utils': 0.3.0
|
||||||
react-hook-form: 7.62.0(react@19.1.0)
|
react-hook-form: 7.61.1(react@19.1.0)
|
||||||
|
|
||||||
'@humanfs/core@0.19.1': {}
|
'@humanfs/core@0.19.1': {}
|
||||||
|
|
||||||
@@ -3142,17 +3083,17 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
tslib: 2.8.1
|
tslib: 2.8.1
|
||||||
|
|
||||||
'@t3-oss/env-core@0.13.8(typescript@5.9.2)(zod@4.0.16)':
|
'@t3-oss/env-core@0.13.8(typescript@5.9.2)(zod@4.0.14)':
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
typescript: 5.9.2
|
typescript: 5.9.2
|
||||||
zod: 4.0.16
|
zod: 4.0.14
|
||||||
|
|
||||||
'@t3-oss/env-nextjs@0.13.8(typescript@5.9.2)(zod@4.0.16)':
|
'@t3-oss/env-nextjs@0.13.8(typescript@5.9.2)(zod@4.0.14)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@t3-oss/env-core': 0.13.8(typescript@5.9.2)(zod@4.0.16)
|
'@t3-oss/env-core': 0.13.8(typescript@5.9.2)(zod@4.0.14)
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
typescript: 5.9.2
|
typescript: 5.9.2
|
||||||
zod: 4.0.16
|
zod: 4.0.14
|
||||||
|
|
||||||
'@tailwindcss/node@4.1.11':
|
'@tailwindcss/node@4.1.11':
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -3261,7 +3202,7 @@ snapshots:
|
|||||||
|
|
||||||
'@types/json5@0.0.29': {}
|
'@types/json5@0.0.29': {}
|
||||||
|
|
||||||
'@types/node@22.17.1':
|
'@types/node@22.17.0':
|
||||||
dependencies:
|
dependencies:
|
||||||
undici-types: 6.21.0
|
undici-types: 6.21.0
|
||||||
|
|
||||||
@@ -3273,15 +3214,15 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
csstype: 3.1.3
|
csstype: 3.1.3
|
||||||
|
|
||||||
'@typescript-eslint/eslint-plugin@8.38.0(@typescript-eslint/parser@8.38.0(eslint@9.33.0(jiti@2.4.2))(typescript@5.9.2))(eslint@9.33.0(jiti@2.4.2))(typescript@5.9.2)':
|
'@typescript-eslint/eslint-plugin@8.38.0(@typescript-eslint/parser@8.38.0(eslint@9.32.0(jiti@2.4.2))(typescript@5.9.2))(eslint@9.32.0(jiti@2.4.2))(typescript@5.9.2)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@eslint-community/regexpp': 4.12.1
|
'@eslint-community/regexpp': 4.12.1
|
||||||
'@typescript-eslint/parser': 8.38.0(eslint@9.33.0(jiti@2.4.2))(typescript@5.9.2)
|
'@typescript-eslint/parser': 8.38.0(eslint@9.32.0(jiti@2.4.2))(typescript@5.9.2)
|
||||||
'@typescript-eslint/scope-manager': 8.38.0
|
'@typescript-eslint/scope-manager': 8.38.0
|
||||||
'@typescript-eslint/type-utils': 8.38.0(eslint@9.33.0(jiti@2.4.2))(typescript@5.9.2)
|
'@typescript-eslint/type-utils': 8.38.0(eslint@9.32.0(jiti@2.4.2))(typescript@5.9.2)
|
||||||
'@typescript-eslint/utils': 8.38.0(eslint@9.33.0(jiti@2.4.2))(typescript@5.9.2)
|
'@typescript-eslint/utils': 8.38.0(eslint@9.32.0(jiti@2.4.2))(typescript@5.9.2)
|
||||||
'@typescript-eslint/visitor-keys': 8.38.0
|
'@typescript-eslint/visitor-keys': 8.38.0
|
||||||
eslint: 9.33.0(jiti@2.4.2)
|
eslint: 9.32.0(jiti@2.4.2)
|
||||||
graphemer: 1.4.0
|
graphemer: 1.4.0
|
||||||
ignore: 7.0.4
|
ignore: 7.0.4
|
||||||
natural-compare: 1.4.0
|
natural-compare: 1.4.0
|
||||||
@@ -3290,43 +3231,14 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
'@typescript-eslint/eslint-plugin@8.39.0(@typescript-eslint/parser@8.39.0(eslint@9.33.0(jiti@2.4.2))(typescript@5.9.2))(eslint@9.33.0(jiti@2.4.2))(typescript@5.9.2)':
|
'@typescript-eslint/parser@8.38.0(eslint@9.32.0(jiti@2.4.2))(typescript@5.9.2)':
|
||||||
dependencies:
|
|
||||||
'@eslint-community/regexpp': 4.12.1
|
|
||||||
'@typescript-eslint/parser': 8.39.0(eslint@9.33.0(jiti@2.4.2))(typescript@5.9.2)
|
|
||||||
'@typescript-eslint/scope-manager': 8.39.0
|
|
||||||
'@typescript-eslint/type-utils': 8.39.0(eslint@9.33.0(jiti@2.4.2))(typescript@5.9.2)
|
|
||||||
'@typescript-eslint/utils': 8.39.0(eslint@9.33.0(jiti@2.4.2))(typescript@5.9.2)
|
|
||||||
'@typescript-eslint/visitor-keys': 8.39.0
|
|
||||||
eslint: 9.33.0(jiti@2.4.2)
|
|
||||||
graphemer: 1.4.0
|
|
||||||
ignore: 7.0.4
|
|
||||||
natural-compare: 1.4.0
|
|
||||||
ts-api-utils: 2.1.0(typescript@5.9.2)
|
|
||||||
typescript: 5.9.2
|
|
||||||
transitivePeerDependencies:
|
|
||||||
- supports-color
|
|
||||||
|
|
||||||
'@typescript-eslint/parser@8.38.0(eslint@9.33.0(jiti@2.4.2))(typescript@5.9.2)':
|
|
||||||
dependencies:
|
dependencies:
|
||||||
'@typescript-eslint/scope-manager': 8.38.0
|
'@typescript-eslint/scope-manager': 8.38.0
|
||||||
'@typescript-eslint/types': 8.38.0
|
'@typescript-eslint/types': 8.38.0
|
||||||
'@typescript-eslint/typescript-estree': 8.38.0(typescript@5.9.2)
|
'@typescript-eslint/typescript-estree': 8.38.0(typescript@5.9.2)
|
||||||
'@typescript-eslint/visitor-keys': 8.38.0
|
'@typescript-eslint/visitor-keys': 8.38.0
|
||||||
debug: 4.4.0
|
debug: 4.4.0
|
||||||
eslint: 9.33.0(jiti@2.4.2)
|
eslint: 9.32.0(jiti@2.4.2)
|
||||||
typescript: 5.9.2
|
|
||||||
transitivePeerDependencies:
|
|
||||||
- supports-color
|
|
||||||
|
|
||||||
'@typescript-eslint/parser@8.39.0(eslint@9.33.0(jiti@2.4.2))(typescript@5.9.2)':
|
|
||||||
dependencies:
|
|
||||||
'@typescript-eslint/scope-manager': 8.39.0
|
|
||||||
'@typescript-eslint/types': 8.39.0
|
|
||||||
'@typescript-eslint/typescript-estree': 8.39.0(typescript@5.9.2)
|
|
||||||
'@typescript-eslint/visitor-keys': 8.39.0
|
|
||||||
debug: 4.4.0
|
|
||||||
eslint: 9.33.0(jiti@2.4.2)
|
|
||||||
typescript: 5.9.2
|
typescript: 5.9.2
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
@@ -3340,52 +3252,22 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
'@typescript-eslint/project-service@8.39.0(typescript@5.9.2)':
|
|
||||||
dependencies:
|
|
||||||
'@typescript-eslint/tsconfig-utils': 8.39.0(typescript@5.9.2)
|
|
||||||
'@typescript-eslint/types': 8.39.0
|
|
||||||
debug: 4.4.0
|
|
||||||
typescript: 5.9.2
|
|
||||||
transitivePeerDependencies:
|
|
||||||
- supports-color
|
|
||||||
|
|
||||||
'@typescript-eslint/scope-manager@8.38.0':
|
'@typescript-eslint/scope-manager@8.38.0':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@typescript-eslint/types': 8.38.0
|
'@typescript-eslint/types': 8.38.0
|
||||||
'@typescript-eslint/visitor-keys': 8.38.0
|
'@typescript-eslint/visitor-keys': 8.38.0
|
||||||
|
|
||||||
'@typescript-eslint/scope-manager@8.39.0':
|
|
||||||
dependencies:
|
|
||||||
'@typescript-eslint/types': 8.39.0
|
|
||||||
'@typescript-eslint/visitor-keys': 8.39.0
|
|
||||||
|
|
||||||
'@typescript-eslint/tsconfig-utils@8.38.0(typescript@5.9.2)':
|
'@typescript-eslint/tsconfig-utils@8.38.0(typescript@5.9.2)':
|
||||||
dependencies:
|
dependencies:
|
||||||
typescript: 5.9.2
|
typescript: 5.9.2
|
||||||
|
|
||||||
'@typescript-eslint/tsconfig-utils@8.39.0(typescript@5.9.2)':
|
'@typescript-eslint/type-utils@8.38.0(eslint@9.32.0(jiti@2.4.2))(typescript@5.9.2)':
|
||||||
dependencies:
|
|
||||||
typescript: 5.9.2
|
|
||||||
|
|
||||||
'@typescript-eslint/type-utils@8.38.0(eslint@9.33.0(jiti@2.4.2))(typescript@5.9.2)':
|
|
||||||
dependencies:
|
dependencies:
|
||||||
'@typescript-eslint/types': 8.38.0
|
'@typescript-eslint/types': 8.38.0
|
||||||
'@typescript-eslint/typescript-estree': 8.38.0(typescript@5.9.2)
|
'@typescript-eslint/typescript-estree': 8.38.0(typescript@5.9.2)
|
||||||
'@typescript-eslint/utils': 8.38.0(eslint@9.33.0(jiti@2.4.2))(typescript@5.9.2)
|
'@typescript-eslint/utils': 8.38.0(eslint@9.32.0(jiti@2.4.2))(typescript@5.9.2)
|
||||||
debug: 4.4.0
|
debug: 4.4.0
|
||||||
eslint: 9.33.0(jiti@2.4.2)
|
eslint: 9.32.0(jiti@2.4.2)
|
||||||
ts-api-utils: 2.1.0(typescript@5.9.2)
|
|
||||||
typescript: 5.9.2
|
|
||||||
transitivePeerDependencies:
|
|
||||||
- supports-color
|
|
||||||
|
|
||||||
'@typescript-eslint/type-utils@8.39.0(eslint@9.33.0(jiti@2.4.2))(typescript@5.9.2)':
|
|
||||||
dependencies:
|
|
||||||
'@typescript-eslint/types': 8.39.0
|
|
||||||
'@typescript-eslint/typescript-estree': 8.39.0(typescript@5.9.2)
|
|
||||||
'@typescript-eslint/utils': 8.39.0(eslint@9.33.0(jiti@2.4.2))(typescript@5.9.2)
|
|
||||||
debug: 4.4.0
|
|
||||||
eslint: 9.33.0(jiti@2.4.2)
|
|
||||||
ts-api-utils: 2.1.0(typescript@5.9.2)
|
ts-api-utils: 2.1.0(typescript@5.9.2)
|
||||||
typescript: 5.9.2
|
typescript: 5.9.2
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
@@ -3393,8 +3275,6 @@ snapshots:
|
|||||||
|
|
||||||
'@typescript-eslint/types@8.38.0': {}
|
'@typescript-eslint/types@8.38.0': {}
|
||||||
|
|
||||||
'@typescript-eslint/types@8.39.0': {}
|
|
||||||
|
|
||||||
'@typescript-eslint/typescript-estree@8.38.0(typescript@5.9.2)':
|
'@typescript-eslint/typescript-estree@8.38.0(typescript@5.9.2)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@typescript-eslint/project-service': 8.38.0(typescript@5.9.2)
|
'@typescript-eslint/project-service': 8.38.0(typescript@5.9.2)
|
||||||
@@ -3411,40 +3291,13 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
'@typescript-eslint/typescript-estree@8.39.0(typescript@5.9.2)':
|
'@typescript-eslint/utils@8.38.0(eslint@9.32.0(jiti@2.4.2))(typescript@5.9.2)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@typescript-eslint/project-service': 8.39.0(typescript@5.9.2)
|
'@eslint-community/eslint-utils': 4.7.0(eslint@9.32.0(jiti@2.4.2))
|
||||||
'@typescript-eslint/tsconfig-utils': 8.39.0(typescript@5.9.2)
|
|
||||||
'@typescript-eslint/types': 8.39.0
|
|
||||||
'@typescript-eslint/visitor-keys': 8.39.0
|
|
||||||
debug: 4.4.0
|
|
||||||
fast-glob: 3.3.3
|
|
||||||
is-glob: 4.0.3
|
|
||||||
minimatch: 9.0.5
|
|
||||||
semver: 7.7.2
|
|
||||||
ts-api-utils: 2.1.0(typescript@5.9.2)
|
|
||||||
typescript: 5.9.2
|
|
||||||
transitivePeerDependencies:
|
|
||||||
- supports-color
|
|
||||||
|
|
||||||
'@typescript-eslint/utils@8.38.0(eslint@9.33.0(jiti@2.4.2))(typescript@5.9.2)':
|
|
||||||
dependencies:
|
|
||||||
'@eslint-community/eslint-utils': 4.7.0(eslint@9.33.0(jiti@2.4.2))
|
|
||||||
'@typescript-eslint/scope-manager': 8.38.0
|
'@typescript-eslint/scope-manager': 8.38.0
|
||||||
'@typescript-eslint/types': 8.38.0
|
'@typescript-eslint/types': 8.38.0
|
||||||
'@typescript-eslint/typescript-estree': 8.38.0(typescript@5.9.2)
|
'@typescript-eslint/typescript-estree': 8.38.0(typescript@5.9.2)
|
||||||
eslint: 9.33.0(jiti@2.4.2)
|
eslint: 9.32.0(jiti@2.4.2)
|
||||||
typescript: 5.9.2
|
|
||||||
transitivePeerDependencies:
|
|
||||||
- supports-color
|
|
||||||
|
|
||||||
'@typescript-eslint/utils@8.39.0(eslint@9.33.0(jiti@2.4.2))(typescript@5.9.2)':
|
|
||||||
dependencies:
|
|
||||||
'@eslint-community/eslint-utils': 4.7.0(eslint@9.33.0(jiti@2.4.2))
|
|
||||||
'@typescript-eslint/scope-manager': 8.39.0
|
|
||||||
'@typescript-eslint/types': 8.39.0
|
|
||||||
'@typescript-eslint/typescript-estree': 8.39.0(typescript@5.9.2)
|
|
||||||
eslint: 9.33.0(jiti@2.4.2)
|
|
||||||
typescript: 5.9.2
|
typescript: 5.9.2
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
@@ -3454,11 +3307,6 @@ snapshots:
|
|||||||
'@typescript-eslint/types': 8.38.0
|
'@typescript-eslint/types': 8.38.0
|
||||||
eslint-visitor-keys: 4.2.1
|
eslint-visitor-keys: 4.2.1
|
||||||
|
|
||||||
'@typescript-eslint/visitor-keys@8.39.0':
|
|
||||||
dependencies:
|
|
||||||
'@typescript-eslint/types': 8.39.0
|
|
||||||
eslint-visitor-keys: 4.2.1
|
|
||||||
|
|
||||||
'@unrs/resolver-binding-darwin-arm64@1.7.2':
|
'@unrs/resolver-binding-darwin-arm64@1.7.2':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
@@ -3901,19 +3749,19 @@ snapshots:
|
|||||||
|
|
||||||
escape-string-regexp@4.0.0: {}
|
escape-string-regexp@4.0.0: {}
|
||||||
|
|
||||||
eslint-config-next@15.4.4(eslint@9.33.0(jiti@2.4.2))(typescript@5.9.2):
|
eslint-config-next@15.4.4(eslint@9.32.0(jiti@2.4.2))(typescript@5.9.2):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@next/eslint-plugin-next': 15.4.4
|
'@next/eslint-plugin-next': 15.4.4
|
||||||
'@rushstack/eslint-patch': 1.11.0
|
'@rushstack/eslint-patch': 1.11.0
|
||||||
'@typescript-eslint/eslint-plugin': 8.38.0(@typescript-eslint/parser@8.38.0(eslint@9.33.0(jiti@2.4.2))(typescript@5.9.2))(eslint@9.33.0(jiti@2.4.2))(typescript@5.9.2)
|
'@typescript-eslint/eslint-plugin': 8.38.0(@typescript-eslint/parser@8.38.0(eslint@9.32.0(jiti@2.4.2))(typescript@5.9.2))(eslint@9.32.0(jiti@2.4.2))(typescript@5.9.2)
|
||||||
'@typescript-eslint/parser': 8.38.0(eslint@9.33.0(jiti@2.4.2))(typescript@5.9.2)
|
'@typescript-eslint/parser': 8.38.0(eslint@9.32.0(jiti@2.4.2))(typescript@5.9.2)
|
||||||
eslint: 9.33.0(jiti@2.4.2)
|
eslint: 9.32.0(jiti@2.4.2)
|
||||||
eslint-import-resolver-node: 0.3.9
|
eslint-import-resolver-node: 0.3.9
|
||||||
eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.31.0)(eslint@9.33.0(jiti@2.4.2))
|
eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.31.0)(eslint@9.32.0(jiti@2.4.2))
|
||||||
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.39.0(eslint@9.33.0(jiti@2.4.2))(typescript@5.9.2))(eslint@9.33.0(jiti@2.4.2))
|
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.38.0(eslint@9.32.0(jiti@2.4.2))(typescript@5.9.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.32.0(jiti@2.4.2))
|
||||||
eslint-plugin-jsx-a11y: 6.10.2(eslint@9.33.0(jiti@2.4.2))
|
eslint-plugin-jsx-a11y: 6.10.2(eslint@9.32.0(jiti@2.4.2))
|
||||||
eslint-plugin-react: 7.37.5(eslint@9.33.0(jiti@2.4.2))
|
eslint-plugin-react: 7.37.5(eslint@9.32.0(jiti@2.4.2))
|
||||||
eslint-plugin-react-hooks: 5.2.0(eslint@9.33.0(jiti@2.4.2))
|
eslint-plugin-react-hooks: 5.2.0(eslint@9.32.0(jiti@2.4.2))
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
typescript: 5.9.2
|
typescript: 5.9.2
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
@@ -3929,32 +3777,33 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0)(eslint@9.33.0(jiti@2.4.2)):
|
eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0)(eslint@9.32.0(jiti@2.4.2)):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@nolyfill/is-core-module': 1.0.39
|
'@nolyfill/is-core-module': 1.0.39
|
||||||
debug: 4.4.0
|
debug: 4.4.0
|
||||||
eslint: 9.33.0(jiti@2.4.2)
|
eslint: 9.32.0(jiti@2.4.2)
|
||||||
get-tsconfig: 4.10.0
|
get-tsconfig: 4.10.0
|
||||||
is-bun-module: 2.0.0
|
is-bun-module: 2.0.0
|
||||||
stable-hash: 0.0.5
|
stable-hash: 0.0.5
|
||||||
tinyglobby: 0.2.13
|
tinyglobby: 0.2.13
|
||||||
unrs-resolver: 1.7.2
|
unrs-resolver: 1.7.2
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.39.0(eslint@9.33.0(jiti@2.4.2))(typescript@5.9.2))(eslint@9.33.0(jiti@2.4.2))
|
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.38.0(eslint@9.32.0(jiti@2.4.2))(typescript@5.9.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.32.0(jiti@2.4.2))
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
eslint-module-utils@2.12.0(@typescript-eslint/parser@8.39.0(eslint@9.33.0(jiti@2.4.2))(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint@9.33.0(jiti@2.4.2)):
|
eslint-module-utils@2.12.0(@typescript-eslint/parser@8.38.0(eslint@9.32.0(jiti@2.4.2))(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.32.0(jiti@2.4.2)):
|
||||||
dependencies:
|
dependencies:
|
||||||
debug: 3.2.7
|
debug: 3.2.7
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@typescript-eslint/parser': 8.39.0(eslint@9.33.0(jiti@2.4.2))(typescript@5.9.2)
|
'@typescript-eslint/parser': 8.38.0(eslint@9.32.0(jiti@2.4.2))(typescript@5.9.2)
|
||||||
eslint: 9.33.0(jiti@2.4.2)
|
eslint: 9.32.0(jiti@2.4.2)
|
||||||
eslint-import-resolver-node: 0.3.9
|
eslint-import-resolver-node: 0.3.9
|
||||||
|
eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.31.0)(eslint@9.32.0(jiti@2.4.2))
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.39.0(eslint@9.33.0(jiti@2.4.2))(typescript@5.9.2))(eslint@9.33.0(jiti@2.4.2)):
|
eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.38.0(eslint@9.32.0(jiti@2.4.2))(typescript@5.9.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.32.0(jiti@2.4.2)):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@rtsao/scc': 1.1.0
|
'@rtsao/scc': 1.1.0
|
||||||
array-includes: 3.1.8
|
array-includes: 3.1.8
|
||||||
@@ -3963,9 +3812,9 @@ snapshots:
|
|||||||
array.prototype.flatmap: 1.3.3
|
array.prototype.flatmap: 1.3.3
|
||||||
debug: 3.2.7
|
debug: 3.2.7
|
||||||
doctrine: 2.1.0
|
doctrine: 2.1.0
|
||||||
eslint: 9.33.0(jiti@2.4.2)
|
eslint: 9.32.0(jiti@2.4.2)
|
||||||
eslint-import-resolver-node: 0.3.9
|
eslint-import-resolver-node: 0.3.9
|
||||||
eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.39.0(eslint@9.33.0(jiti@2.4.2))(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint@9.33.0(jiti@2.4.2))
|
eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.38.0(eslint@9.32.0(jiti@2.4.2))(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.32.0(jiti@2.4.2))
|
||||||
hasown: 2.0.2
|
hasown: 2.0.2
|
||||||
is-core-module: 2.16.1
|
is-core-module: 2.16.1
|
||||||
is-glob: 4.0.3
|
is-glob: 4.0.3
|
||||||
@@ -3977,13 +3826,13 @@ snapshots:
|
|||||||
string.prototype.trimend: 1.0.9
|
string.prototype.trimend: 1.0.9
|
||||||
tsconfig-paths: 3.15.0
|
tsconfig-paths: 3.15.0
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@typescript-eslint/parser': 8.39.0(eslint@9.33.0(jiti@2.4.2))(typescript@5.9.2)
|
'@typescript-eslint/parser': 8.38.0(eslint@9.32.0(jiti@2.4.2))(typescript@5.9.2)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- eslint-import-resolver-typescript
|
- eslint-import-resolver-typescript
|
||||||
- eslint-import-resolver-webpack
|
- eslint-import-resolver-webpack
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
eslint-plugin-jsx-a11y@6.10.2(eslint@9.33.0(jiti@2.4.2)):
|
eslint-plugin-jsx-a11y@6.10.2(eslint@9.32.0(jiti@2.4.2)):
|
||||||
dependencies:
|
dependencies:
|
||||||
aria-query: 5.3.2
|
aria-query: 5.3.2
|
||||||
array-includes: 3.1.8
|
array-includes: 3.1.8
|
||||||
@@ -3993,7 +3842,7 @@ snapshots:
|
|||||||
axobject-query: 4.1.0
|
axobject-query: 4.1.0
|
||||||
damerau-levenshtein: 1.0.8
|
damerau-levenshtein: 1.0.8
|
||||||
emoji-regex: 9.2.2
|
emoji-regex: 9.2.2
|
||||||
eslint: 9.33.0(jiti@2.4.2)
|
eslint: 9.32.0(jiti@2.4.2)
|
||||||
hasown: 2.0.2
|
hasown: 2.0.2
|
||||||
jsx-ast-utils: 3.3.5
|
jsx-ast-utils: 3.3.5
|
||||||
language-tags: 1.0.9
|
language-tags: 1.0.9
|
||||||
@@ -4002,11 +3851,11 @@ snapshots:
|
|||||||
safe-regex-test: 1.1.0
|
safe-regex-test: 1.1.0
|
||||||
string.prototype.includes: 2.0.1
|
string.prototype.includes: 2.0.1
|
||||||
|
|
||||||
eslint-plugin-react-hooks@5.2.0(eslint@9.33.0(jiti@2.4.2)):
|
eslint-plugin-react-hooks@5.2.0(eslint@9.32.0(jiti@2.4.2)):
|
||||||
dependencies:
|
dependencies:
|
||||||
eslint: 9.33.0(jiti@2.4.2)
|
eslint: 9.32.0(jiti@2.4.2)
|
||||||
|
|
||||||
eslint-plugin-react@7.37.5(eslint@9.33.0(jiti@2.4.2)):
|
eslint-plugin-react@7.37.5(eslint@9.32.0(jiti@2.4.2)):
|
||||||
dependencies:
|
dependencies:
|
||||||
array-includes: 3.1.8
|
array-includes: 3.1.8
|
||||||
array.prototype.findlast: 1.2.5
|
array.prototype.findlast: 1.2.5
|
||||||
@@ -4014,7 +3863,7 @@ snapshots:
|
|||||||
array.prototype.tosorted: 1.1.4
|
array.prototype.tosorted: 1.1.4
|
||||||
doctrine: 2.1.0
|
doctrine: 2.1.0
|
||||||
es-iterator-helpers: 1.2.1
|
es-iterator-helpers: 1.2.1
|
||||||
eslint: 9.33.0(jiti@2.4.2)
|
eslint: 9.32.0(jiti@2.4.2)
|
||||||
estraverse: 5.3.0
|
estraverse: 5.3.0
|
||||||
hasown: 2.0.2
|
hasown: 2.0.2
|
||||||
jsx-ast-utils: 3.3.5
|
jsx-ast-utils: 3.3.5
|
||||||
@@ -4037,16 +3886,16 @@ snapshots:
|
|||||||
|
|
||||||
eslint-visitor-keys@4.2.1: {}
|
eslint-visitor-keys@4.2.1: {}
|
||||||
|
|
||||||
eslint@9.33.0(jiti@2.4.2):
|
eslint@9.32.0(jiti@2.4.2):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@eslint-community/eslint-utils': 4.7.0(eslint@9.33.0(jiti@2.4.2))
|
'@eslint-community/eslint-utils': 4.7.0(eslint@9.32.0(jiti@2.4.2))
|
||||||
'@eslint-community/regexpp': 4.12.1
|
'@eslint-community/regexpp': 4.12.1
|
||||||
'@eslint/config-array': 0.21.0
|
'@eslint/config-array': 0.21.0
|
||||||
'@eslint/config-helpers': 0.3.1
|
'@eslint/config-helpers': 0.3.0
|
||||||
'@eslint/core': 0.15.2
|
'@eslint/core': 0.15.1
|
||||||
'@eslint/eslintrc': 3.3.1
|
'@eslint/eslintrc': 3.3.1
|
||||||
'@eslint/js': 9.33.0
|
'@eslint/js': 9.32.0
|
||||||
'@eslint/plugin-kit': 0.3.5
|
'@eslint/plugin-kit': 0.3.4
|
||||||
'@humanfs/node': 0.16.6
|
'@humanfs/node': 0.16.6
|
||||||
'@humanwhocodes/module-importer': 1.0.1
|
'@humanwhocodes/module-importer': 1.0.1
|
||||||
'@humanwhocodes/retry': 0.4.2
|
'@humanwhocodes/retry': 0.4.2
|
||||||
@@ -4482,7 +4331,7 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
js-tokens: 4.0.0
|
js-tokens: 4.0.0
|
||||||
|
|
||||||
lucide-react@0.539.0(react@19.1.0):
|
lucide-react@0.536.0(react@19.1.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
react: 19.1.0
|
react: 19.1.0
|
||||||
|
|
||||||
@@ -4672,7 +4521,7 @@ snapshots:
|
|||||||
react: 19.1.0
|
react: 19.1.0
|
||||||
scheduler: 0.26.0
|
scheduler: 0.26.0
|
||||||
|
|
||||||
react-hook-form@7.62.0(react@19.1.0):
|
react-hook-form@7.61.1(react@19.1.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
react: 19.1.0
|
react: 19.1.0
|
||||||
|
|
||||||
@@ -5050,13 +4899,13 @@ snapshots:
|
|||||||
possible-typed-array-names: 1.1.0
|
possible-typed-array-names: 1.1.0
|
||||||
reflect.getprototypeof: 1.0.10
|
reflect.getprototypeof: 1.0.10
|
||||||
|
|
||||||
typescript-eslint@8.39.0(eslint@9.33.0(jiti@2.4.2))(typescript@5.9.2):
|
typescript-eslint@8.38.0(eslint@9.32.0(jiti@2.4.2))(typescript@5.9.2):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@typescript-eslint/eslint-plugin': 8.39.0(@typescript-eslint/parser@8.39.0(eslint@9.33.0(jiti@2.4.2))(typescript@5.9.2))(eslint@9.33.0(jiti@2.4.2))(typescript@5.9.2)
|
'@typescript-eslint/eslint-plugin': 8.38.0(@typescript-eslint/parser@8.38.0(eslint@9.32.0(jiti@2.4.2))(typescript@5.9.2))(eslint@9.32.0(jiti@2.4.2))(typescript@5.9.2)
|
||||||
'@typescript-eslint/parser': 8.39.0(eslint@9.33.0(jiti@2.4.2))(typescript@5.9.2)
|
'@typescript-eslint/parser': 8.38.0(eslint@9.32.0(jiti@2.4.2))(typescript@5.9.2)
|
||||||
'@typescript-eslint/typescript-estree': 8.39.0(typescript@5.9.2)
|
'@typescript-eslint/typescript-estree': 8.38.0(typescript@5.9.2)
|
||||||
'@typescript-eslint/utils': 8.39.0(eslint@9.33.0(jiti@2.4.2))(typescript@5.9.2)
|
'@typescript-eslint/utils': 8.38.0(eslint@9.32.0(jiti@2.4.2))(typescript@5.9.2)
|
||||||
eslint: 9.33.0(jiti@2.4.2)
|
eslint: 9.32.0(jiti@2.4.2)
|
||||||
typescript: 5.9.2
|
typescript: 5.9.2
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
@@ -5181,4 +5030,4 @@ snapshots:
|
|||||||
|
|
||||||
yocto-queue@0.1.0: {}
|
yocto-queue@0.1.0: {}
|
||||||
|
|
||||||
zod@4.0.16: {}
|
zod@4.0.14: {}
|
||||||
|
@@ -1,223 +0,0 @@
|
|||||||
"use client";
|
|
||||||
|
|
||||||
import { useState } from "react";
|
|
||||||
import {
|
|
||||||
Card,
|
|
||||||
CardContent,
|
|
||||||
CardDescription,
|
|
||||||
CardHeader,
|
|
||||||
CardTitle,
|
|
||||||
} from "@/components/ui/card";
|
|
||||||
import { Input } from "@/components/ui/input";
|
|
||||||
import { Label } from "@/components/ui/label";
|
|
||||||
import { Slider } from "@/components/ui/slider";
|
|
||||||
|
|
||||||
export default function FourPercentRuleCalculator() {
|
|
||||||
const [annualExpenses, setAnnualExpenses] = useState(50000);
|
|
||||||
const [portfolioValue, setPortfolioValue] = useState(0);
|
|
||||||
const [withdrawalRate, setWithdrawalRate] = useState(4);
|
|
||||||
|
|
||||||
// Calculate FIRE number based on withdrawal rate
|
|
||||||
const fireNumber = Math.round(annualExpenses / (withdrawalRate / 100));
|
|
||||||
|
|
||||||
// Calculate safe withdrawal amount from portfolio
|
|
||||||
const safeWithdrawal = Math.round(portfolioValue * (withdrawalRate / 100));
|
|
||||||
|
|
||||||
// Calculate years to FIRE if saving
|
|
||||||
const monthlySavings = 2000; // Default for demo
|
|
||||||
const growthRate = 0.07; // 7% annual growth
|
|
||||||
const yearsToFire =
|
|
||||||
portfolioValue < fireNumber
|
|
||||||
? Math.log(
|
|
||||||
(fireNumber + (monthlySavings * 12) / growthRate) /
|
|
||||||
(portfolioValue + (monthlySavings * 12) / growthRate),
|
|
||||||
) / Math.log(1 + growthRate)
|
|
||||||
: 0;
|
|
||||||
|
|
||||||
// Format currency
|
|
||||||
const formatCurrency = (value: number) => {
|
|
||||||
return new Intl.NumberFormat("en-US", {
|
|
||||||
style: "currency",
|
|
||||||
currency: "USD",
|
|
||||||
minimumFractionDigits: 0,
|
|
||||||
maximumFractionDigits: 0,
|
|
||||||
}).format(value);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="space-y-6">
|
|
||||||
{/* Input Section */}
|
|
||||||
<Card>
|
|
||||||
<CardHeader>
|
|
||||||
<CardTitle>Calculate Your FIRE Number</CardTitle>
|
|
||||||
<CardDescription>
|
|
||||||
Enter your annual expenses and current portfolio value to see your
|
|
||||||
path to financial independence
|
|
||||||
</CardDescription>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent className="space-y-6">
|
|
||||||
<div className="grid gap-6 md:grid-cols-2">
|
|
||||||
<div className="space-y-2">
|
|
||||||
<Label htmlFor="annual-expenses">Annual Expenses</Label>
|
|
||||||
<Input
|
|
||||||
id="annual-expenses"
|
|
||||||
type="number"
|
|
||||||
value={annualExpenses}
|
|
||||||
onChange={(e) => setAnnualExpenses(Number(e.target.value))}
|
|
||||||
min={0}
|
|
||||||
step={1000}
|
|
||||||
/>
|
|
||||||
<p className="text-muted-foreground text-sm">
|
|
||||||
Your yearly spending in retirement
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="space-y-2">
|
|
||||||
<Label htmlFor="portfolio-value">Current Portfolio Value</Label>
|
|
||||||
<Input
|
|
||||||
id="portfolio-value"
|
|
||||||
type="number"
|
|
||||||
value={portfolioValue}
|
|
||||||
onChange={(e) => setPortfolioValue(Number(e.target.value))}
|
|
||||||
min={0}
|
|
||||||
step={10000}
|
|
||||||
/>
|
|
||||||
<p className="text-muted-foreground text-sm">
|
|
||||||
Your current invested assets
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="space-y-2">
|
|
||||||
<div className="flex justify-between">
|
|
||||||
<Label htmlFor="withdrawal-rate">Withdrawal Rate</Label>
|
|
||||||
<span className="text-sm font-medium">{withdrawalRate}%</span>
|
|
||||||
</div>
|
|
||||||
<Slider
|
|
||||||
id="withdrawal-rate"
|
|
||||||
min={3}
|
|
||||||
max={5}
|
|
||||||
step={0.1}
|
|
||||||
value={[withdrawalRate]}
|
|
||||||
onValueChange={(value) => setWithdrawalRate(value[0] ?? 4)}
|
|
||||||
className="w-full"
|
|
||||||
/>
|
|
||||||
<p className="text-muted-foreground text-sm">
|
|
||||||
The classic 4% rule suggests 4%, but adjust based on your risk
|
|
||||||
tolerance
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
{/* Results Section */}
|
|
||||||
<div className="grid gap-6 md:grid-cols-2">
|
|
||||||
<Card>
|
|
||||||
<CardHeader>
|
|
||||||
<CardTitle className="text-2xl">Your FIRE Number</CardTitle>
|
|
||||||
<CardDescription>
|
|
||||||
Portfolio needed for {withdrawalRate}% withdrawal rate
|
|
||||||
</CardDescription>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent>
|
|
||||||
<p className="text-primary text-4xl font-bold">
|
|
||||||
{formatCurrency(fireNumber)}
|
|
||||||
</p>
|
|
||||||
<p className="text-muted-foreground mt-2 text-sm">
|
|
||||||
This is {Math.round(fireNumber / annualExpenses)}× your annual
|
|
||||||
expenses
|
|
||||||
</p>
|
|
||||||
{portfolioValue > 0 && portfolioValue < fireNumber && (
|
|
||||||
<div className="bg-foreground/5 mt-4 rounded-lg p-3">
|
|
||||||
<p className="text-sm font-medium">Progress to FIRE</p>
|
|
||||||
<div className="mt-2 h-2 rounded-full bg-gray-200">
|
|
||||||
<div
|
|
||||||
className="bg-primary h-2 rounded-full transition-all duration-300"
|
|
||||||
style={{
|
|
||||||
width: `${Math.min((portfolioValue / fireNumber) * 100, 100)}%`,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<p className="text-muted-foreground mt-1 text-xs">
|
|
||||||
{Math.round((portfolioValue / fireNumber) * 100)}% complete
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
<Card>
|
|
||||||
<CardHeader>
|
|
||||||
<CardTitle className="text-2xl">Safe Annual Withdrawal</CardTitle>
|
|
||||||
<CardDescription>From your current portfolio</CardDescription>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent>
|
|
||||||
<p className="text-primary text-4xl font-bold">
|
|
||||||
{formatCurrency(safeWithdrawal)}
|
|
||||||
</p>
|
|
||||||
<p className="text-muted-foreground mt-2 text-sm">
|
|
||||||
Monthly: {formatCurrency(safeWithdrawal / 12)}
|
|
||||||
</p>
|
|
||||||
{safeWithdrawal > 0 && safeWithdrawal < annualExpenses && (
|
|
||||||
<div className="mt-4 rounded-lg bg-orange-100 p-3 dark:bg-orange-900/20">
|
|
||||||
<p className="text-sm text-orange-900 dark:text-orange-100">
|
|
||||||
⚠️ Your safe withdrawal ({formatCurrency(safeWithdrawal)}) is
|
|
||||||
less than your annual expenses (
|
|
||||||
{formatCurrency(annualExpenses)})
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{safeWithdrawal >= annualExpenses && (
|
|
||||||
<div className="mt-4 rounded-lg bg-green-100 p-3 dark:bg-green-900/20">
|
|
||||||
<p className="text-sm text-green-900 dark:text-green-100">
|
|
||||||
✓ Congratulations! You've reached FIRE
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Additional Insights */}
|
|
||||||
<Card>
|
|
||||||
<CardHeader>
|
|
||||||
<CardTitle>Quick Insights</CardTitle>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent>
|
|
||||||
<div className="grid gap-4 md:grid-cols-3">
|
|
||||||
<div className="bg-foreground/5 rounded-lg p-4 text-center">
|
|
||||||
<p className="text-muted-foreground text-sm">Gap to FIRE</p>
|
|
||||||
<p className="text-2xl font-bold">
|
|
||||||
{formatCurrency(Math.max(fireNumber - portfolioValue, 0))}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div className="bg-foreground/5 rounded-lg p-4 text-center">
|
|
||||||
<p className="text-muted-foreground text-sm">Monthly Target</p>
|
|
||||||
<p className="text-2xl font-bold">
|
|
||||||
{formatCurrency(annualExpenses / 12)}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div className="bg-foreground/5 rounded-lg p-4 text-center">
|
|
||||||
<p className="text-muted-foreground text-sm">25× Rule Result</p>
|
|
||||||
<p className="text-2xl font-bold">
|
|
||||||
{formatCurrency(annualExpenses * 25)}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
{/* Educational Note */}
|
|
||||||
<Card className="border-blue-200 bg-blue-50 dark:border-blue-900 dark:bg-blue-950/20">
|
|
||||||
<CardContent className="pt-6">
|
|
||||||
<p className="text-sm">
|
|
||||||
<strong>💡 Pro Tip:</strong> The 4% rule is based on a 30-year
|
|
||||||
retirement. For early retirees planning 40-50+ years, consider using
|
|
||||||
3.5% or even 3% for added safety. Remember to account for taxes,
|
|
||||||
healthcare costs, and inflation in your planning.
|
|
||||||
</p>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
@@ -1,429 +0,0 @@
|
|||||||
import type { Metadata } from "next";
|
|
||||||
import Link from "next/link";
|
|
||||||
import Image from "next/image";
|
|
||||||
import BackgroundPattern from "@/app/components/BackgroundPattern";
|
|
||||||
import Footer from "@/app/components/footer";
|
|
||||||
import FourPercentRuleCalculator from "./FourPercentRuleCalculator";
|
|
||||||
import {
|
|
||||||
Accordion,
|
|
||||||
AccordionContent,
|
|
||||||
AccordionItem,
|
|
||||||
AccordionTrigger,
|
|
||||||
} from "@/components/ui/accordion";
|
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
|
||||||
title: "4% Rule Calculator - Safe Withdrawal Rate for FIRE | InvestingFIRE",
|
|
||||||
description:
|
|
||||||
"Calculate your safe withdrawal rate using the 4% rule. Determine how much you need to retire early and how long your retirement savings will last. Free FIRE calculator with real-time results.",
|
|
||||||
keywords:
|
|
||||||
"4 percent rule calculator, safe withdrawal rate calculator, 4% rule retirement, FIRE calculator, retirement withdrawal calculator, Trinity Study calculator",
|
|
||||||
openGraph: {
|
|
||||||
title: "4% Rule Calculator - Safe Withdrawal Rate Calculator",
|
|
||||||
description:
|
|
||||||
"Free 4% rule calculator to determine your safe withdrawal rate and retirement portfolio size. Based on the Trinity Study for FIRE planning.",
|
|
||||||
type: "website",
|
|
||||||
url: "https://investingfire.com/calculators/4-percent-rule",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export default function FourPercentRulePage() {
|
|
||||||
const faqData = {
|
|
||||||
"@context": "https://schema.org",
|
|
||||||
"@type": "FAQPage",
|
|
||||||
mainEntity: [
|
|
||||||
{
|
|
||||||
"@type": "Question",
|
|
||||||
name: "What is the 4% rule?",
|
|
||||||
acceptedAnswer: {
|
|
||||||
"@type": "Answer",
|
|
||||||
text: "The 4% rule is a retirement planning guideline that suggests you can safely withdraw 4% of your retirement portfolio in the first year, then adjust that amount for inflation each subsequent year, with a high probability of not running out of money over 30 years.",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"@type": "Question",
|
|
||||||
name: "How accurate is the 4% rule?",
|
|
||||||
acceptedAnswer: {
|
|
||||||
"@type": "Answer",
|
|
||||||
text: "The 4% rule, based on the Trinity Study, showed a 95% success rate for a 30-year retirement with a 50/50 stock/bond portfolio. However, it's based on historical U.S. market data and may need adjustment for longer retirements, different asset allocations, or varying market conditions.",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"@type": "Question",
|
|
||||||
name: "Is 4% too conservative or too aggressive?",
|
|
||||||
acceptedAnswer: {
|
|
||||||
"@type": "Answer",
|
|
||||||
text: "It depends on your situation. For early retirees with 40-50+ year horizons, 4% might be too aggressive. Some prefer 3-3.5%. Conversely, flexible spenders who can reduce withdrawals in down markets might safely use 4.5-5%. Personal factors like other income sources and spending flexibility matter.",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"@type": "Question",
|
|
||||||
name: "How do I calculate my FIRE number using the 4% rule?",
|
|
||||||
acceptedAnswer: {
|
|
||||||
"@type": "Answer",
|
|
||||||
text: "Simply multiply your annual expenses by 25. For example, if you need $40,000 per year, your FIRE number is $1,000,000 ($40,000 × 25). This gives you a portfolio where 4% equals your annual spending needs.",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
const breadcrumbData = {
|
|
||||||
"@context": "https://schema.org",
|
|
||||||
"@type": "BreadcrumbList",
|
|
||||||
itemListElement: [
|
|
||||||
{
|
|
||||||
"@type": "ListItem",
|
|
||||||
position: 1,
|
|
||||||
name: "Home",
|
|
||||||
item: "https://investingfire.com",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"@type": "ListItem",
|
|
||||||
position: 2,
|
|
||||||
name: "Calculators",
|
|
||||||
item: "https://investingfire.com/calculators",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"@type": "ListItem",
|
|
||||||
position: 3,
|
|
||||||
name: "4% Rule Calculator",
|
|
||||||
item: "https://investingfire.com/calculators/4-percent-rule",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<main className="text-primary-foreground to-destructive from-secondary flex min-h-screen flex-col items-center bg-gradient-to-b p-2">
|
|
||||||
<BackgroundPattern />
|
|
||||||
|
|
||||||
{/* Header */}
|
|
||||||
<div className="z-10 mx-auto flex flex-col items-center justify-center gap-4 text-center">
|
|
||||||
<div className="mt-8 flex flex-row flex-wrap items-center justify-center gap-4 align-middle">
|
|
||||||
<Link
|
|
||||||
href="/"
|
|
||||||
className="flex items-center gap-4 transition-opacity hover:opacity-90"
|
|
||||||
>
|
|
||||||
<Image
|
|
||||||
priority
|
|
||||||
unoptimized
|
|
||||||
src="/investingfire_logo_no-bg.svg"
|
|
||||||
alt="InvestingFIRE Logo"
|
|
||||||
width={60}
|
|
||||||
height={60}
|
|
||||||
/>
|
|
||||||
<span className="text-2xl font-bold">InvestingFIRE</span>
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h1 className="from-primary via-primary-foreground to-primary mt-8 bg-gradient-to-r bg-clip-text text-4xl font-extrabold tracking-tight text-transparent drop-shadow-md sm:text-[4rem]">
|
|
||||||
4% Rule Calculator
|
|
||||||
</h1>
|
|
||||||
<p className="text-primary-foreground/90 max-w-2xl text-xl font-semibold md:text-2xl">
|
|
||||||
Calculate Your Safe Withdrawal Rate & FIRE Number Using the Trinity
|
|
||||||
Study Method
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Breadcrumb Schema */}
|
|
||||||
<script
|
|
||||||
type="application/ld+json"
|
|
||||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(breadcrumbData) }}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{/* Calculator */}
|
|
||||||
<div className="z-10 mt-8 w-full max-w-4xl">
|
|
||||||
<FourPercentRuleCalculator />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* SEO Content */}
|
|
||||||
<div className="z-10 mx-auto max-w-4xl px-4 py-12 text-left">
|
|
||||||
<section className="mb-12">
|
|
||||||
<h2 className="mb-4 text-3xl font-bold">
|
|
||||||
Understanding the 4% Rule for Safe Retirement Withdrawals
|
|
||||||
</h2>
|
|
||||||
<p className="mb-4 text-lg leading-relaxed">
|
|
||||||
The <strong>4% rule</strong> is one of the most widely recognized
|
|
||||||
guidelines in retirement planning, particularly within the FIRE
|
|
||||||
(Financial Independence, Retire Early) community. This rule of thumb
|
|
||||||
suggests you can withdraw 4% of your retirement portfolio in the
|
|
||||||
first year of retirement, then adjust that dollar amount for
|
|
||||||
inflation each subsequent year, with a high probability of not
|
|
||||||
depleting your savings over a 30-year retirement period.
|
|
||||||
</p>
|
|
||||||
<p className="mb-4 text-lg leading-relaxed">
|
|
||||||
Originating from the groundbreaking <strong>Trinity Study</strong>{" "}
|
|
||||||
(1998), which analyzed historical market data from 1926-1995, the 4%
|
|
||||||
rule demonstrated a 95% success rate for mixed portfolios of stocks
|
|
||||||
and bonds over 30-year periods. This simple yet powerful concept
|
|
||||||
revolutionized retirement planning by providing a clear target:
|
|
||||||
accumulate 25 times your annual expenses to achieve financial
|
|
||||||
independence.
|
|
||||||
</p>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section className="mb-12">
|
|
||||||
<h2 className="mb-4 text-3xl font-bold">
|
|
||||||
How the 4% Rule Calculator Works
|
|
||||||
</h2>
|
|
||||||
<p className="mb-4 text-lg leading-relaxed">
|
|
||||||
Our 4% rule calculator helps you determine two critical numbers for
|
|
||||||
your retirement planning:
|
|
||||||
</p>
|
|
||||||
<ol className="mb-6 ml-6 list-decimal space-y-3 text-lg">
|
|
||||||
<li>
|
|
||||||
<strong>Your FIRE Number</strong> - The total portfolio size
|
|
||||||
needed to support your desired lifestyle using the 4% withdrawal
|
|
||||||
rate
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<strong>Safe Annual Withdrawal</strong> - How much you can
|
|
||||||
withdraw from a given portfolio size while maintaining the 4% rule
|
|
||||||
</li>
|
|
||||||
</ol>
|
|
||||||
|
|
||||||
<div className="bg-foreground/10 mb-6 rounded-lg p-6">
|
|
||||||
<h3 className="mb-3 text-xl font-semibold">
|
|
||||||
Quick 4% Rule Formula:
|
|
||||||
</h3>
|
|
||||||
<p className="mb-2 text-lg">
|
|
||||||
<strong>FIRE Number = Annual Expenses × 25</strong>
|
|
||||||
</p>
|
|
||||||
<p className="text-lg">
|
|
||||||
<strong>Safe Annual Withdrawal = Portfolio Value × 0.04</strong>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<p className="mb-4 text-lg leading-relaxed">
|
|
||||||
For example, if you need $50,000 per year to cover your expenses,
|
|
||||||
your FIRE number would be $1,250,000 ($50,000 × 25). Conversely, if
|
|
||||||
you have a $2,000,000 portfolio, you could safely withdraw $80,000
|
|
||||||
in the first year (2,000,000 × 0.04).
|
|
||||||
</p>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section className="mb-12">
|
|
||||||
<h2 className="mb-4 text-3xl font-bold">
|
|
||||||
Adjusting the 4% Rule for Your Situation
|
|
||||||
</h2>
|
|
||||||
<p className="mb-4 text-lg leading-relaxed">
|
|
||||||
While the 4% rule provides an excellent starting point, many
|
|
||||||
financial experts suggest adjustments based on individual
|
|
||||||
circumstances:
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div className="mb-6 grid gap-6 md:grid-cols-2">
|
|
||||||
<div className="bg-foreground/10 rounded-lg p-6">
|
|
||||||
<h3 className="mb-3 text-xl font-semibold">
|
|
||||||
More Conservative Approaches
|
|
||||||
</h3>
|
|
||||||
<ul className="ml-6 list-disc space-y-2 text-lg">
|
|
||||||
<li>
|
|
||||||
<strong>3.5% Rule</strong> - For early retirees with 40-50+
|
|
||||||
year horizons
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<strong>3% Rule</strong> - Ultra-conservative for maximum
|
|
||||||
safety
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<strong>Dynamic Withdrawals</strong> - Adjust based on market
|
|
||||||
performance
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="bg-foreground/10 rounded-lg p-6">
|
|
||||||
<h3 className="mb-3 text-xl font-semibold">
|
|
||||||
Factors That May Allow Higher Withdrawals
|
|
||||||
</h3>
|
|
||||||
<ul className="ml-6 list-disc space-y-2 text-lg">
|
|
||||||
<li>
|
|
||||||
<strong>Flexible Spending</strong> - Ability to reduce
|
|
||||||
expenses in down markets
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<strong>Part-time Income</strong> - Earning some money in
|
|
||||||
retirement
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<strong>Social Security/Pensions</strong> - Additional income
|
|
||||||
sources later
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section className="mb-12">
|
|
||||||
<h2 className="mb-4 text-3xl font-bold">
|
|
||||||
4% Rule vs. Other FIRE Strategies
|
|
||||||
</h2>
|
|
||||||
<p className="mb-4 text-lg leading-relaxed">
|
|
||||||
The 4% rule is just one approach to achieving financial
|
|
||||||
independence. Here's how it compares to other popular FIRE
|
|
||||||
strategies:
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div className="space-y-4">
|
|
||||||
<div className="bg-foreground/10 rounded-lg p-4">
|
|
||||||
<h3 className="mb-2 text-lg font-semibold">
|
|
||||||
Traditional FIRE (4% Rule)
|
|
||||||
</h3>
|
|
||||||
<p>
|
|
||||||
Target: 25× annual expenses | Best for: Balanced lifestyle with
|
|
||||||
moderate spending
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="bg-foreground/10 rounded-lg p-4">
|
|
||||||
<h3 className="mb-2 text-lg font-semibold">
|
|
||||||
Lean FIRE (3-3.5% Rule)
|
|
||||||
</h3>
|
|
||||||
<p>
|
|
||||||
Target: 28-33× annual expenses | Best for: Minimalist lifestyle,
|
|
||||||
maximum safety
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="bg-foreground/10 rounded-lg p-4">
|
|
||||||
<h3 className="mb-2 text-lg font-semibold">
|
|
||||||
Fat FIRE (4-5% Rule)
|
|
||||||
</h3>
|
|
||||||
<p>
|
|
||||||
Target: 20-25× annual expenses | Best for: Luxurious retirement
|
|
||||||
with higher spending
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="bg-foreground/10 rounded-lg p-4">
|
|
||||||
<h3 className="mb-2 text-lg font-semibold">Coast FIRE</h3>
|
|
||||||
<p>
|
|
||||||
Let investments grow while covering expenses with part-time work
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<p className="mt-4 text-lg">
|
|
||||||
Want to explore these strategies in detail? Try our{" "}
|
|
||||||
<Link
|
|
||||||
href="/"
|
|
||||||
className="text-primary font-semibold hover:underline"
|
|
||||||
>
|
|
||||||
comprehensive FIRE calculator
|
|
||||||
</Link>{" "}
|
|
||||||
for a full retirement simulation.
|
|
||||||
</p>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
{/* FAQ Section */}
|
|
||||||
<section className="mb-12">
|
|
||||||
<script
|
|
||||||
type="application/ld+json"
|
|
||||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(faqData) }}
|
|
||||||
/>
|
|
||||||
<h2 className="mb-4 text-3xl font-bold">
|
|
||||||
4% Rule Frequently Asked Questions
|
|
||||||
</h2>
|
|
||||||
|
|
||||||
<Accordion type="single" collapsible className="w-full">
|
|
||||||
<AccordionItem value="item-1">
|
|
||||||
<AccordionTrigger className="text-xl font-semibold">
|
|
||||||
What is the 4% rule?
|
|
||||||
</AccordionTrigger>
|
|
||||||
<AccordionContent className="text-lg leading-relaxed">
|
|
||||||
The 4% rule is a retirement planning guideline that suggests you
|
|
||||||
can safely withdraw 4% of your retirement portfolio in the first
|
|
||||||
year, then adjust that amount for inflation each subsequent
|
|
||||||
year, with a high probability of not running out of money over
|
|
||||||
30 years.
|
|
||||||
</AccordionContent>
|
|
||||||
</AccordionItem>
|
|
||||||
|
|
||||||
<AccordionItem value="item-2">
|
|
||||||
<AccordionTrigger className="text-xl font-semibold">
|
|
||||||
How accurate is the 4% rule?
|
|
||||||
</AccordionTrigger>
|
|
||||||
<AccordionContent className="text-lg leading-relaxed">
|
|
||||||
The 4% rule, based on the Trinity Study, showed a 95% success
|
|
||||||
rate for a 30-year retirement with a 50/50 stock/bond portfolio.
|
|
||||||
However, it's based on historical U.S. market data and may need
|
|
||||||
adjustment for longer retirements, different asset allocations,
|
|
||||||
or varying market conditions.
|
|
||||||
</AccordionContent>
|
|
||||||
</AccordionItem>
|
|
||||||
|
|
||||||
<AccordionItem value="item-3">
|
|
||||||
<AccordionTrigger className="text-xl font-semibold">
|
|
||||||
Is 4% too conservative or too aggressive?
|
|
||||||
</AccordionTrigger>
|
|
||||||
<AccordionContent className="text-lg leading-relaxed">
|
|
||||||
It depends on your situation. For early retirees with 40-50+
|
|
||||||
year horizons, 4% might be too aggressive. Some prefer 3-3.5%.
|
|
||||||
Conversely, flexible spenders who can reduce withdrawals in down
|
|
||||||
markets might safely use 4.5-5%. Personal factors like other
|
|
||||||
income sources and spending flexibility matter.
|
|
||||||
</AccordionContent>
|
|
||||||
</AccordionItem>
|
|
||||||
|
|
||||||
<AccordionItem value="item-4">
|
|
||||||
<AccordionTrigger className="text-xl font-semibold">
|
|
||||||
How do I calculate my FIRE number using the 4% rule?
|
|
||||||
</AccordionTrigger>
|
|
||||||
<AccordionContent className="text-lg leading-relaxed">
|
|
||||||
Simply multiply your annual expenses by 25. For example, if you
|
|
||||||
need $40,000 per year, your FIRE number is $1,000,000 ($40,000 ×
|
|
||||||
25). This gives you a portfolio where 4% equals your annual
|
|
||||||
spending needs.
|
|
||||||
</AccordionContent>
|
|
||||||
</AccordionItem>
|
|
||||||
|
|
||||||
<AccordionItem value="item-5">
|
|
||||||
<AccordionTrigger className="text-xl font-semibold">
|
|
||||||
Does the 4% rule account for taxes?
|
|
||||||
</AccordionTrigger>
|
|
||||||
<AccordionContent className="text-lg leading-relaxed">
|
|
||||||
No, the 4% rule calculates gross withdrawals. You'll need to
|
|
||||||
account for taxes separately based on your account types
|
|
||||||
(traditional vs. Roth IRA, taxable accounts) and tax bracket.
|
|
||||||
Many FIRE planners target a portfolio 25-30% larger than the
|
|
||||||
basic calculation to cover taxes.
|
|
||||||
</AccordionContent>
|
|
||||||
</AccordionItem>
|
|
||||||
|
|
||||||
<AccordionItem value="item-6">
|
|
||||||
<AccordionTrigger className="text-xl font-semibold">
|
|
||||||
What asset allocation works best with the 4% rule?
|
|
||||||
</AccordionTrigger>
|
|
||||||
<AccordionContent className="text-lg leading-relaxed">
|
|
||||||
The original Trinity Study found success with portfolios ranging
|
|
||||||
from 50/50 to 75/25 stocks/bonds. Higher stock allocations
|
|
||||||
generally provided better long-term results but with more
|
|
||||||
volatility. Most FIRE practitioners use 60-80% stocks for the
|
|
||||||
growth needed to sustain long retirements.
|
|
||||||
</AccordionContent>
|
|
||||||
</AccordionItem>
|
|
||||||
</Accordion>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
{/* Call to Action */}
|
|
||||||
<section className="bg-foreground/10 mb-12 rounded-lg p-8 text-center">
|
|
||||||
<h2 className="mb-4 text-2xl font-bold">
|
|
||||||
Ready for a More Detailed FIRE Analysis?
|
|
||||||
</h2>
|
|
||||||
<p className="mb-6 text-lg">
|
|
||||||
While the 4% rule provides a great starting point, our comprehensive
|
|
||||||
FIRE calculator offers year-by-year projections, inflation
|
|
||||||
adjustments, and personalized scenarios for your unique situation.
|
|
||||||
</p>
|
|
||||||
<Link
|
|
||||||
href="/"
|
|
||||||
className="bg-primary text-primary-foreground inline-block rounded-lg px-6 py-3 font-semibold transition-opacity hover:opacity-90"
|
|
||||||
>
|
|
||||||
Try Our Full FIRE Calculator →
|
|
||||||
</Link>
|
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Footer />
|
|
||||||
</main>
|
|
||||||
);
|
|
||||||
}
|
|
@@ -1,345 +0,0 @@
|
|||||||
"use client";
|
|
||||||
|
|
||||||
import { useState, useEffect } from "react";
|
|
||||||
import {
|
|
||||||
Card,
|
|
||||||
CardContent,
|
|
||||||
CardDescription,
|
|
||||||
CardHeader,
|
|
||||||
CardTitle,
|
|
||||||
} from "@/components/ui/card";
|
|
||||||
import { Input } from "@/components/ui/input";
|
|
||||||
import { Label } from "@/components/ui/label";
|
|
||||||
import { Slider } from "@/components/ui/slider";
|
|
||||||
|
|
||||||
export default function CoastFireCalculator() {
|
|
||||||
const [currentAge, setCurrentAge] = useState(30);
|
|
||||||
const [retirementAge, setRetirementAge] = useState(65);
|
|
||||||
const [currentPortfolio, setCurrentPortfolio] = useState(50000);
|
|
||||||
const [annualExpenses, setAnnualExpenses] = useState(50000);
|
|
||||||
const [expectedReturn, setExpectedReturn] = useState(7);
|
|
||||||
|
|
||||||
// Calculate years until retirement
|
|
||||||
const yearsUntilRetirement = retirementAge - currentAge;
|
|
||||||
|
|
||||||
// Calculate target FIRE number (25x annual expenses)
|
|
||||||
const targetFireNumber = annualExpenses * 25;
|
|
||||||
|
|
||||||
// Calculate Coast FIRE number (present value of future FIRE number)
|
|
||||||
const coastFireNumber =
|
|
||||||
yearsUntilRetirement > 0
|
|
||||||
? targetFireNumber /
|
|
||||||
Math.pow(1 + expectedReturn / 100, yearsUntilRetirement)
|
|
||||||
: targetFireNumber;
|
|
||||||
|
|
||||||
// Check if already at Coast FIRE
|
|
||||||
const isCoastFire = currentPortfolio >= coastFireNumber;
|
|
||||||
|
|
||||||
// Calculate what portfolio will grow to by retirement
|
|
||||||
const projectedPortfolioAtRetirement =
|
|
||||||
currentPortfolio * Math.pow(1 + expectedReturn / 100, yearsUntilRetirement);
|
|
||||||
|
|
||||||
// Calculate gap to Coast FIRE
|
|
||||||
const gapToCoastFire = Math.max(coastFireNumber - currentPortfolio, 0);
|
|
||||||
|
|
||||||
// Calculate monthly savings needed to reach Coast FIRE in different timeframes
|
|
||||||
const calculateMonthlySavings = (years: number) => {
|
|
||||||
if (years <= 0 || isCoastFire) return 0;
|
|
||||||
const monthlyReturn = expectedReturn / 100 / 12;
|
|
||||||
const months = years * 12;
|
|
||||||
return (
|
|
||||||
(gapToCoastFire * monthlyReturn) /
|
|
||||||
(Math.pow(1 + monthlyReturn, months) - 1)
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Format currency
|
|
||||||
const formatCurrency = (value: number) => {
|
|
||||||
return new Intl.NumberFormat("en-US", {
|
|
||||||
style: "currency",
|
|
||||||
currency: "USD",
|
|
||||||
minimumFractionDigits: 0,
|
|
||||||
maximumFractionDigits: 0,
|
|
||||||
}).format(value);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="space-y-6">
|
|
||||||
{/* Input Section */}
|
|
||||||
<Card>
|
|
||||||
<CardHeader>
|
|
||||||
<CardTitle>Your Coast FIRE Inputs</CardTitle>
|
|
||||||
<CardDescription>
|
|
||||||
Enter your details to calculate when you can stop saving for
|
|
||||||
retirement
|
|
||||||
</CardDescription>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent className="space-y-6">
|
|
||||||
<div className="grid gap-6 md:grid-cols-2">
|
|
||||||
<div className="space-y-2">
|
|
||||||
<div className="flex justify-between">
|
|
||||||
<Label htmlFor="current-age">Current Age</Label>
|
|
||||||
<span className="text-sm font-medium">{currentAge}</span>
|
|
||||||
</div>
|
|
||||||
<Slider
|
|
||||||
id="current-age"
|
|
||||||
min={20}
|
|
||||||
max={60}
|
|
||||||
step={1}
|
|
||||||
value={[currentAge]}
|
|
||||||
onValueChange={(value) => setCurrentAge(value[0] ?? 30)}
|
|
||||||
className="w-full"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="space-y-2">
|
|
||||||
<div className="flex justify-between">
|
|
||||||
<Label htmlFor="retirement-age">Target Retirement Age</Label>
|
|
||||||
<span className="text-sm font-medium">{retirementAge}</span>
|
|
||||||
</div>
|
|
||||||
<Slider
|
|
||||||
id="retirement-age"
|
|
||||||
min={40}
|
|
||||||
max={80}
|
|
||||||
step={1}
|
|
||||||
value={[retirementAge]}
|
|
||||||
onValueChange={(value) => setRetirementAge(value[0] ?? 65)}
|
|
||||||
className="w-full"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="grid gap-6 md:grid-cols-2">
|
|
||||||
<div className="space-y-2">
|
|
||||||
<Label htmlFor="current-portfolio">Current Portfolio Value</Label>
|
|
||||||
<Input
|
|
||||||
id="current-portfolio"
|
|
||||||
type="number"
|
|
||||||
value={currentPortfolio}
|
|
||||||
onChange={(e) => setCurrentPortfolio(Number(e.target.value))}
|
|
||||||
min={0}
|
|
||||||
step={1000}
|
|
||||||
/>
|
|
||||||
<p className="text-muted-foreground text-sm">
|
|
||||||
Your current retirement savings
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="space-y-2">
|
|
||||||
<Label htmlFor="annual-expenses">
|
|
||||||
Annual Expenses in Retirement
|
|
||||||
</Label>
|
|
||||||
<Input
|
|
||||||
id="annual-expenses"
|
|
||||||
type="number"
|
|
||||||
value={annualExpenses}
|
|
||||||
onChange={(e) => setAnnualExpenses(Number(e.target.value))}
|
|
||||||
min={0}
|
|
||||||
step={1000}
|
|
||||||
/>
|
|
||||||
<p className="text-muted-foreground text-sm">
|
|
||||||
Yearly spending (in today's dollars)
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="space-y-2">
|
|
||||||
<div className="flex justify-between">
|
|
||||||
<Label htmlFor="expected-return">Expected Annual Return</Label>
|
|
||||||
<span className="text-sm font-medium">{expectedReturn}%</span>
|
|
||||||
</div>
|
|
||||||
<Slider
|
|
||||||
id="expected-return"
|
|
||||||
min={4}
|
|
||||||
max={10}
|
|
||||||
step={0.5}
|
|
||||||
value={[expectedReturn]}
|
|
||||||
onValueChange={(value) => setExpectedReturn(value[0] ?? 7)}
|
|
||||||
className="w-full"
|
|
||||||
/>
|
|
||||||
<p className="text-muted-foreground text-sm">
|
|
||||||
Average annual investment return before inflation
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
{/* Results Section */}
|
|
||||||
<div className="grid gap-6 md:grid-cols-2">
|
|
||||||
<Card>
|
|
||||||
<CardHeader>
|
|
||||||
<CardTitle className="text-2xl">Your Coast FIRE Number</CardTitle>
|
|
||||||
<CardDescription>
|
|
||||||
Amount needed today to coast to retirement
|
|
||||||
</CardDescription>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent>
|
|
||||||
<p className="text-primary text-4xl font-bold">
|
|
||||||
{formatCurrency(coastFireNumber)}
|
|
||||||
</p>
|
|
||||||
<p className="text-muted-foreground mt-2 text-sm">
|
|
||||||
Grows to {formatCurrency(targetFireNumber)} in{" "}
|
|
||||||
{yearsUntilRetirement} years
|
|
||||||
</p>
|
|
||||||
{isCoastFire ? (
|
|
||||||
<div className="mt-4 rounded-lg bg-green-100 p-3 dark:bg-green-900/20">
|
|
||||||
<p className="text-sm text-green-900 dark:text-green-100">
|
|
||||||
🎉 Congratulations! You've reached Coast FIRE!
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className="bg-foreground/5 mt-4 rounded-lg p-3">
|
|
||||||
<p className="text-sm font-medium">Progress to Coast FIRE</p>
|
|
||||||
<div className="mt-2 h-2 rounded-full bg-gray-200">
|
|
||||||
<div
|
|
||||||
className="bg-primary h-2 rounded-full transition-all duration-300"
|
|
||||||
style={{
|
|
||||||
width: `${Math.min((currentPortfolio / coastFireNumber) * 100, 100)}%`,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<p className="text-muted-foreground mt-1 text-xs">
|
|
||||||
{Math.round((currentPortfolio / coastFireNumber) * 100)}%
|
|
||||||
complete
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
<Card>
|
|
||||||
<CardHeader>
|
|
||||||
<CardTitle className="text-2xl">Portfolio at Retirement</CardTitle>
|
|
||||||
<CardDescription>
|
|
||||||
What your current savings will grow to
|
|
||||||
</CardDescription>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent>
|
|
||||||
<p className="text-primary text-4xl font-bold">
|
|
||||||
{formatCurrency(projectedPortfolioAtRetirement)}
|
|
||||||
</p>
|
|
||||||
<p className="text-muted-foreground mt-2 text-sm">
|
|
||||||
{projectedPortfolioAtRetirement >= targetFireNumber
|
|
||||||
? `${Math.round((projectedPortfolioAtRetirement / targetFireNumber) * 100)}% of target`
|
|
||||||
: `${formatCurrency(targetFireNumber - projectedPortfolioAtRetirement)} short`}
|
|
||||||
</p>
|
|
||||||
{projectedPortfolioAtRetirement >= targetFireNumber && (
|
|
||||||
<div className="mt-4 rounded-lg bg-blue-100 p-3 dark:bg-blue-900/20">
|
|
||||||
<p className="text-sm text-blue-900 dark:text-blue-100">
|
|
||||||
💡 You'll exceed your FIRE target by{" "}
|
|
||||||
{formatCurrency(
|
|
||||||
projectedPortfolioAtRetirement - targetFireNumber,
|
|
||||||
)}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Savings Scenarios */}
|
|
||||||
{!isCoastFire && (
|
|
||||||
<Card>
|
|
||||||
<CardHeader>
|
|
||||||
<CardTitle>Monthly Savings to Reach Coast FIRE</CardTitle>
|
|
||||||
<CardDescription>
|
|
||||||
How much to save monthly to hit Coast FIRE in different timeframes
|
|
||||||
</CardDescription>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent>
|
|
||||||
<div className="grid gap-4 md:grid-cols-3">
|
|
||||||
<div className="bg-foreground/5 rounded-lg p-4 text-center">
|
|
||||||
<p className="text-muted-foreground text-sm">In 5 Years</p>
|
|
||||||
<p className="text-2xl font-bold">
|
|
||||||
{formatCurrency(calculateMonthlySavings(5))}
|
|
||||||
</p>
|
|
||||||
<p className="text-muted-foreground text-xs">per month</p>
|
|
||||||
</div>
|
|
||||||
<div className="bg-foreground/5 rounded-lg p-4 text-center">
|
|
||||||
<p className="text-muted-foreground text-sm">In 10 Years</p>
|
|
||||||
<p className="text-2xl font-bold">
|
|
||||||
{formatCurrency(calculateMonthlySavings(10))}
|
|
||||||
</p>
|
|
||||||
<p className="text-muted-foreground text-xs">per month</p>
|
|
||||||
</div>
|
|
||||||
<div className="bg-foreground/5 rounded-lg p-4 text-center">
|
|
||||||
<p className="text-muted-foreground text-sm">In 15 Years</p>
|
|
||||||
<p className="text-2xl font-bold">
|
|
||||||
{formatCurrency(calculateMonthlySavings(15))}
|
|
||||||
</p>
|
|
||||||
<p className="text-muted-foreground text-xs">per month</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Key Metrics */}
|
|
||||||
<Card>
|
|
||||||
<CardHeader>
|
|
||||||
<CardTitle>Coast FIRE Summary</CardTitle>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent>
|
|
||||||
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
|
|
||||||
<div className="bg-foreground/5 rounded-lg p-4 text-center">
|
|
||||||
<p className="text-muted-foreground text-sm">
|
|
||||||
Years to Retirement
|
|
||||||
</p>
|
|
||||||
<p className="text-2xl font-bold">{yearsUntilRetirement}</p>
|
|
||||||
</div>
|
|
||||||
<div className="bg-foreground/5 rounded-lg p-4 text-center">
|
|
||||||
<p className="text-muted-foreground text-sm">
|
|
||||||
Target FIRE Number
|
|
||||||
</p>
|
|
||||||
<p className="text-2xl font-bold">
|
|
||||||
{formatCurrency(targetFireNumber)}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div className="bg-foreground/5 rounded-lg p-4 text-center">
|
|
||||||
<p className="text-muted-foreground text-sm">Gap to Coast FIRE</p>
|
|
||||||
<p className="text-2xl font-bold">
|
|
||||||
{formatCurrency(gapToCoastFire)}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div className="bg-foreground/5 rounded-lg p-4 text-center">
|
|
||||||
<p className="text-muted-foreground text-sm">Growth Multiple</p>
|
|
||||||
<p className="text-2xl font-bold">
|
|
||||||
{Math.pow(
|
|
||||||
1 + expectedReturn / 100,
|
|
||||||
yearsUntilRetirement,
|
|
||||||
).toFixed(1)}
|
|
||||||
×
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
{/* Educational Notes */}
|
|
||||||
<div className="grid gap-4 md:grid-cols-2">
|
|
||||||
<Card className="border-blue-200 bg-blue-50 dark:border-blue-900 dark:bg-blue-950/20">
|
|
||||||
<CardContent className="pt-6">
|
|
||||||
<p className="text-sm">
|
|
||||||
<strong>🎯 Coast FIRE Strategy:</strong> Once you hit your Coast
|
|
||||||
FIRE number, you can stop saving for retirement entirely. Work
|
|
||||||
only to cover current expenses while your investments grow to your
|
|
||||||
full FIRE target.
|
|
||||||
</p>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
<Card className="border-purple-200 bg-purple-50 dark:border-purple-900 dark:bg-purple-950/20">
|
|
||||||
<CardContent className="pt-6">
|
|
||||||
<p className="text-sm">
|
|
||||||
<strong>⚡ Power of Time:</strong> Starting early is crucial. A
|
|
||||||
25-year-old needs only{" "}
|
|
||||||
{formatCurrency(targetFireNumber / Math.pow(1.07, 40))}
|
|
||||||
to coast to a {formatCurrency(targetFireNumber)} retirement at 65
|
|
||||||
(assuming 7% returns).
|
|
||||||
</p>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
@@ -1,481 +0,0 @@
|
|||||||
import type { Metadata } from "next";
|
|
||||||
import Link from "next/link";
|
|
||||||
import Image from "next/image";
|
|
||||||
import BackgroundPattern from "@/app/components/BackgroundPattern";
|
|
||||||
import Footer from "@/app/components/footer";
|
|
||||||
import CoastFireCalculator from "./CoastFireCalculator";
|
|
||||||
import {
|
|
||||||
Accordion,
|
|
||||||
AccordionContent,
|
|
||||||
AccordionItem,
|
|
||||||
AccordionTrigger,
|
|
||||||
} from "@/components/ui/accordion";
|
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
|
||||||
title: "Coast FIRE Calculator - When Can You Stop Saving? | InvestingFIRE",
|
|
||||||
description:
|
|
||||||
"Calculate your Coast FIRE number and find out when you can stop saving for retirement. Free Coast FI calculator shows how compound interest can work for you.",
|
|
||||||
keywords:
|
|
||||||
"coast fire calculator, coast fi calculator, coast fire number, barista fire calculator, coast financial independence, stop saving calculator",
|
|
||||||
openGraph: {
|
|
||||||
title: "Coast FIRE Calculator - Stop Saving & Let Compound Interest Work",
|
|
||||||
description:
|
|
||||||
"Discover when you can stop saving for retirement and coast to financial independence. Free calculator with real-time projections.",
|
|
||||||
type: "website",
|
|
||||||
url: "https://investingfire.com/calculators/coast-fire",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export default function CoastFirePage() {
|
|
||||||
const faqData = {
|
|
||||||
"@context": "https://schema.org",
|
|
||||||
"@type": "FAQPage",
|
|
||||||
mainEntity: [
|
|
||||||
{
|
|
||||||
"@type": "Question",
|
|
||||||
name: "What is Coast FIRE?",
|
|
||||||
acceptedAnswer: {
|
|
||||||
"@type": "Answer",
|
|
||||||
text: "Coast FIRE (Financial Independence, Retire Early) is when you've saved enough that you can stop contributing to retirement accounts and still reach your FIRE number by your target retirement age through compound growth alone. You still need to cover current expenses but no longer need to save for retirement.",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"@type": "Question",
|
|
||||||
name: "How is Coast FIRE different from regular FIRE?",
|
|
||||||
acceptedAnswer: {
|
|
||||||
"@type": "Answer",
|
|
||||||
text: "Regular FIRE means you have enough saved to retire immediately and live off withdrawals. Coast FIRE means you have enough that will grow to your FIRE number by retirement age without additional contributions. You still work to cover current expenses but can spend everything you earn.",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"@type": "Question",
|
|
||||||
name: "What's the Coast FIRE formula?",
|
|
||||||
acceptedAnswer: {
|
|
||||||
"@type": "Answer",
|
|
||||||
text: "Coast FIRE Number = FIRE Number ÷ (1 + growth rate)^years until retirement. For example, if you need $1 million at 65 and you're 35 with 30 years to go, assuming 7% growth: $1,000,000 ÷ (1.07)^30 = $131,367.",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"@type": "Question",
|
|
||||||
name: "Is Coast FIRE realistic?",
|
|
||||||
acceptedAnswer: {
|
|
||||||
"@type": "Answer",
|
|
||||||
text: "Yes, Coast FIRE is very achievable, especially for those who start saving aggressively in their 20s or 30s. The key is front-loading retirement savings early in your career when compound interest has the most time to work. Many achieve Coast FIRE in 10-15 years of focused saving.",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
const breadcrumbData = {
|
|
||||||
"@context": "https://schema.org",
|
|
||||||
"@type": "BreadcrumbList",
|
|
||||||
itemListElement: [
|
|
||||||
{
|
|
||||||
"@type": "ListItem",
|
|
||||||
position: 1,
|
|
||||||
name: "Home",
|
|
||||||
item: "https://investingfire.com",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"@type": "ListItem",
|
|
||||||
position: 2,
|
|
||||||
name: "Calculators",
|
|
||||||
item: "https://investingfire.com/calculators",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"@type": "ListItem",
|
|
||||||
position: 3,
|
|
||||||
name: "Coast FIRE Calculator",
|
|
||||||
item: "https://investingfire.com/calculators/coast-fire",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<main className="text-primary-foreground to-destructive from-secondary flex min-h-screen flex-col items-center bg-gradient-to-b p-2">
|
|
||||||
<BackgroundPattern />
|
|
||||||
|
|
||||||
{/* Header */}
|
|
||||||
<div className="z-10 mx-auto flex flex-col items-center justify-center gap-4 text-center">
|
|
||||||
<div className="mt-8 flex flex-row flex-wrap items-center justify-center gap-4 align-middle">
|
|
||||||
<Link
|
|
||||||
href="/"
|
|
||||||
className="flex items-center gap-4 transition-opacity hover:opacity-90"
|
|
||||||
>
|
|
||||||
<Image
|
|
||||||
priority
|
|
||||||
unoptimized
|
|
||||||
src="/investingfire_logo_no-bg.svg"
|
|
||||||
alt="InvestingFIRE Logo"
|
|
||||||
width={60}
|
|
||||||
height={60}
|
|
||||||
/>
|
|
||||||
<span className="text-2xl font-bold">InvestingFIRE</span>
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h1 className="from-primary via-primary-foreground to-primary mt-8 bg-gradient-to-r bg-clip-text text-4xl font-extrabold tracking-tight text-transparent drop-shadow-md sm:text-[4rem]">
|
|
||||||
Coast FIRE Calculator
|
|
||||||
</h1>
|
|
||||||
<p className="text-primary-foreground/90 max-w-2xl text-xl font-semibold md:text-2xl">
|
|
||||||
Find Out When You Can Stop Saving and Let Compound Interest Do the
|
|
||||||
Work
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Breadcrumb Schema */}
|
|
||||||
<script
|
|
||||||
type="application/ld+json"
|
|
||||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(breadcrumbData) }}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{/* Calculator */}
|
|
||||||
<div className="z-10 mt-8 w-full max-w-4xl">
|
|
||||||
<CoastFireCalculator />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* SEO Content */}
|
|
||||||
<div className="z-10 mx-auto max-w-4xl px-4 py-12 text-left">
|
|
||||||
<section className="mb-12">
|
|
||||||
<h2 className="mb-4 text-3xl font-bold">
|
|
||||||
What Is Coast FIRE? Your Path to Stress-Free Saving
|
|
||||||
</h2>
|
|
||||||
<p className="mb-4 text-lg leading-relaxed">
|
|
||||||
<strong>Coast FIRE</strong> represents a powerful milestone in your
|
|
||||||
financial independence journey. It's the point where you've
|
|
||||||
accumulated enough investments that you can completely stop saving
|
|
||||||
for retirement and still reach your FIRE number by your target
|
|
||||||
retirement age through compound growth alone.
|
|
||||||
</p>
|
|
||||||
<p className="mb-4 text-lg leading-relaxed">
|
|
||||||
Unlike traditional FIRE where you need 25× your annual expenses
|
|
||||||
saved before retiring, Coast FIRE allows you to "coast" to
|
|
||||||
retirement. You still work to cover your current living expenses,
|
|
||||||
but you can spend 100% of your income knowing your future retirement
|
|
||||||
is already secured through the magic of compound interest.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div className="bg-foreground/10 my-6 rounded-lg p-6">
|
|
||||||
<h3 className="mb-3 text-xl font-semibold">Coast FIRE Benefits:</h3>
|
|
||||||
<ul className="ml-6 list-disc space-y-2 text-lg">
|
|
||||||
<li>
|
|
||||||
<strong>Reduced financial stress</strong> - No more pressure to
|
|
||||||
save aggressively
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<strong>Career flexibility</strong> - Take lower-paying but more
|
|
||||||
fulfilling work
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<strong>Lifestyle upgrade</strong> - Spend your entire paycheck
|
|
||||||
guilt-free
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<strong>Early achievement</strong> - Often reachable in your 30s
|
|
||||||
or 40s
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section className="mb-12">
|
|
||||||
<h2 className="mb-4 text-3xl font-bold">
|
|
||||||
How the Coast FIRE Calculator Works
|
|
||||||
</h2>
|
|
||||||
<p className="mb-4 text-lg leading-relaxed">
|
|
||||||
Our Coast FIRE calculator uses the time value of money principle to
|
|
||||||
determine exactly how much you need invested today to reach your
|
|
||||||
retirement goal without any additional contributions. Here's the
|
|
||||||
math behind it:
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div className="bg-foreground/10 mb-6 rounded-lg p-6">
|
|
||||||
<h3 className="mb-3 text-xl font-semibold">
|
|
||||||
The Coast FIRE Formula:
|
|
||||||
</h3>
|
|
||||||
<p className="mb-4 font-mono text-lg">
|
|
||||||
Coast FIRE Number = Target FIRE Number ÷ (1 + growth rate)^years
|
|
||||||
</p>
|
|
||||||
<p className="text-lg">Where:</p>
|
|
||||||
<ul className="ml-6 list-disc space-y-1 text-lg">
|
|
||||||
<li>Target FIRE Number = 25× your annual retirement expenses</li>
|
|
||||||
<li>
|
|
||||||
Growth rate = Expected annual investment return (e.g., 7%)
|
|
||||||
</li>
|
|
||||||
<li>Years = Time until your target retirement age</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<p className="mb-4 text-lg leading-relaxed">
|
|
||||||
For example, if you need $1,000,000 to retire in 30 years and expect
|
|
||||||
7% annual returns, you need just $131,367 invested today to coast to
|
|
||||||
retirement. That's the power of compound interest working over
|
|
||||||
decades!
|
|
||||||
</p>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section className="mb-12">
|
|
||||||
<h2 className="mb-4 text-3xl font-bold">
|
|
||||||
Coast FIRE vs Other FIRE Variations
|
|
||||||
</h2>
|
|
||||||
<p className="mb-4 text-lg leading-relaxed">
|
|
||||||
Understanding how Coast FIRE fits into the broader FIRE movement
|
|
||||||
helps you choose the right strategy for your situation:
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div className="grid gap-4 md:grid-cols-2">
|
|
||||||
<div className="bg-foreground/10 rounded-lg p-6">
|
|
||||||
<h3 className="mb-3 text-xl font-semibold">Coast FIRE</h3>
|
|
||||||
<p className="mb-2">Stop saving, work for expenses only</p>
|
|
||||||
<ul className="ml-4 list-disc space-y-1 text-sm">
|
|
||||||
<li>Achievable in 10-15 years</li>
|
|
||||||
<li>Reduces financial stress immediately</li>
|
|
||||||
<li>Perfect for career pivots</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="bg-foreground/10 rounded-lg p-6">
|
|
||||||
<h3 className="mb-3 text-xl font-semibold">Barista FIRE</h3>
|
|
||||||
<p className="mb-2">Similar to Coast but work part-time</p>
|
|
||||||
<ul className="ml-4 list-disc space-y-1 text-sm">
|
|
||||||
<li>Often includes health benefits</li>
|
|
||||||
<li>More lifestyle flexibility</li>
|
|
||||||
<li>Bridge to full retirement</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="bg-foreground/10 rounded-lg p-6">
|
|
||||||
<h3 className="mb-3 text-xl font-semibold">Traditional FIRE</h3>
|
|
||||||
<p className="mb-2">Full retirement with 4% rule</p>
|
|
||||||
<ul className="ml-4 list-disc space-y-1 text-sm">
|
|
||||||
<li>Need 25× annual expenses</li>
|
|
||||||
<li>Complete work optional</li>
|
|
||||||
<li>Takes longer to achieve</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="bg-foreground/10 rounded-lg p-6">
|
|
||||||
<h3 className="mb-3 text-xl font-semibold">Lean/Fat FIRE</h3>
|
|
||||||
<p className="mb-2">Variations based on spending</p>
|
|
||||||
<ul className="ml-4 list-disc space-y-1 text-sm">
|
|
||||||
<li>Lean: Minimal expenses</li>
|
|
||||||
<li>Fat: Luxury lifestyle</li>
|
|
||||||
<li>Different savings targets</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section className="mb-12">
|
|
||||||
<h2 className="mb-4 text-3xl font-bold">
|
|
||||||
Strategies to Reach Coast FIRE Faster
|
|
||||||
</h2>
|
|
||||||
<p className="mb-4 text-lg leading-relaxed">
|
|
||||||
Achieving Coast FIRE is all about front-loading your retirement
|
|
||||||
savings while you're young. Here are proven strategies to accelerate
|
|
||||||
your journey:
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div className="space-y-4">
|
|
||||||
<div className="bg-foreground/10 rounded-lg p-4">
|
|
||||||
<h3 className="mb-2 text-lg font-semibold">
|
|
||||||
1. Maximize Tax-Advantaged Accounts Early
|
|
||||||
</h3>
|
|
||||||
<p>
|
|
||||||
Prioritize 401(k), IRA, and HSA contributions in your 20s and
|
|
||||||
30s. The tax savings compound alongside your investments.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="bg-foreground/10 rounded-lg p-4">
|
|
||||||
<h3 className="mb-2 text-lg font-semibold">
|
|
||||||
2. Live on One Income (If Partnered)
|
|
||||||
</h3>
|
|
||||||
<p>
|
|
||||||
Save 100% of one partner's income while living on the other.
|
|
||||||
This can cut your Coast FIRE timeline in half.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="bg-foreground/10 rounded-lg p-4">
|
|
||||||
<h3 className="mb-2 text-lg font-semibold">
|
|
||||||
3. House Hack or Geographic Arbitrage
|
|
||||||
</h3>
|
|
||||||
<p>
|
|
||||||
Minimize housing costs through rental income or moving to lower
|
|
||||||
cost areas while maintaining income.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="bg-foreground/10 rounded-lg p-4">
|
|
||||||
<h3 className="mb-2 text-lg font-semibold">
|
|
||||||
4. Invest Windfalls Immediately
|
|
||||||
</h3>
|
|
||||||
<p>
|
|
||||||
Bonuses, tax refunds, and gifts go straight to investments.
|
|
||||||
These lump sums have maximum time to compound.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="bg-foreground/10 rounded-lg p-4">
|
|
||||||
<h3 className="mb-2 text-lg font-semibold">
|
|
||||||
5. Increase Savings Rate Annually
|
|
||||||
</h3>
|
|
||||||
<p>
|
|
||||||
Bump up your savings by 1-2% each year. You won't feel the pinch
|
|
||||||
but will dramatically accelerate your timeline.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
{/* FAQ Section */}
|
|
||||||
<section className="mb-12">
|
|
||||||
<script
|
|
||||||
type="application/ld+json"
|
|
||||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(faqData) }}
|
|
||||||
/>
|
|
||||||
<h2 className="mb-4 text-3xl font-bold">
|
|
||||||
Coast FIRE Frequently Asked Questions
|
|
||||||
</h2>
|
|
||||||
|
|
||||||
<Accordion type="single" collapsible className="w-full">
|
|
||||||
<AccordionItem value="item-1">
|
|
||||||
<AccordionTrigger className="text-xl font-semibold">
|
|
||||||
What is Coast FIRE?
|
|
||||||
</AccordionTrigger>
|
|
||||||
<AccordionContent className="text-lg leading-relaxed">
|
|
||||||
Coast FIRE (Financial Independence, Retire Early) is when you've
|
|
||||||
saved enough that you can stop contributing to retirement
|
|
||||||
accounts and still reach your FIRE number by your target
|
|
||||||
retirement age through compound growth alone. You still need to
|
|
||||||
cover current expenses but no longer need to save for
|
|
||||||
retirement.
|
|
||||||
</AccordionContent>
|
|
||||||
</AccordionItem>
|
|
||||||
|
|
||||||
<AccordionItem value="item-2">
|
|
||||||
<AccordionTrigger className="text-xl font-semibold">
|
|
||||||
How is Coast FIRE different from regular FIRE?
|
|
||||||
</AccordionTrigger>
|
|
||||||
<AccordionContent className="text-lg leading-relaxed">
|
|
||||||
Regular FIRE means you have enough saved to retire immediately
|
|
||||||
and live off withdrawals. Coast FIRE means you have enough that
|
|
||||||
will grow to your FIRE number by retirement age without
|
|
||||||
additional contributions. You still work to cover current
|
|
||||||
expenses but can spend everything you earn.
|
|
||||||
</AccordionContent>
|
|
||||||
</AccordionItem>
|
|
||||||
|
|
||||||
<AccordionItem value="item-3">
|
|
||||||
<AccordionTrigger className="text-xl font-semibold">
|
|
||||||
What's the Coast FIRE formula?
|
|
||||||
</AccordionTrigger>
|
|
||||||
<AccordionContent className="text-lg leading-relaxed">
|
|
||||||
Coast FIRE Number = FIRE Number ÷ (1 + growth rate)^years until
|
|
||||||
retirement. For example, if you need $1 million at 65 and you're
|
|
||||||
35 with 30 years to go, assuming 7% growth: $1,000,000 ÷
|
|
||||||
(1.07)^30 = $131,367.
|
|
||||||
</AccordionContent>
|
|
||||||
</AccordionItem>
|
|
||||||
|
|
||||||
<AccordionItem value="item-4">
|
|
||||||
<AccordionTrigger className="text-xl font-semibold">
|
|
||||||
Is Coast FIRE realistic?
|
|
||||||
</AccordionTrigger>
|
|
||||||
<AccordionContent className="text-lg leading-relaxed">
|
|
||||||
Yes, Coast FIRE is very achievable, especially for those who
|
|
||||||
start saving aggressively in their 20s or 30s. The key is
|
|
||||||
front-loading retirement savings early in your career when
|
|
||||||
compound interest has the most time to work. Many achieve Coast
|
|
||||||
FIRE in 10-15 years of focused saving.
|
|
||||||
</AccordionContent>
|
|
||||||
</AccordionItem>
|
|
||||||
|
|
||||||
<AccordionItem value="item-5">
|
|
||||||
<AccordionTrigger className="text-xl font-semibold">
|
|
||||||
What if I already have some retirement savings?
|
|
||||||
</AccordionTrigger>
|
|
||||||
<AccordionContent className="text-lg leading-relaxed">
|
|
||||||
Great! You're already on your way. Enter your current portfolio
|
|
||||||
value in the calculator to see how close you are to Coast FIRE.
|
|
||||||
You might be surprised to find you're closer than you think,
|
|
||||||
especially if you have many years until retirement.
|
|
||||||
</AccordionContent>
|
|
||||||
</AccordionItem>
|
|
||||||
|
|
||||||
<AccordionItem value="item-6">
|
|
||||||
<AccordionTrigger className="text-xl font-semibold">
|
|
||||||
Should I actually stop saving once I hit Coast FIRE?
|
|
||||||
</AccordionTrigger>
|
|
||||||
<AccordionContent className="text-lg leading-relaxed">
|
|
||||||
That's a personal choice! Many people continue saving to reach
|
|
||||||
full FIRE faster, build a buffer for market downturns, or
|
|
||||||
upgrade their retirement lifestyle. Others use Coast FIRE as
|
|
||||||
permission to pursue lower-paying passion projects or reduce
|
|
||||||
work hours. The beauty is having options.
|
|
||||||
</AccordionContent>
|
|
||||||
</AccordionItem>
|
|
||||||
</Accordion>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
{/* Success Stories */}
|
|
||||||
<section className="mb-12">
|
|
||||||
<h2 className="mb-4 text-3xl font-bold">
|
|
||||||
Real Coast FIRE Success Stories
|
|
||||||
</h2>
|
|
||||||
<div className="grid gap-6 md:grid-cols-2">
|
|
||||||
<div className="bg-foreground/10 rounded-lg p-6">
|
|
||||||
<h3 className="mb-2 text-xl font-semibold">The Teacher's Tale</h3>
|
|
||||||
<p className="text-sm">
|
|
||||||
"I hit Coast FIRE at 32 after 10 years of saving 60% as an
|
|
||||||
engineer. Now I teach high school physics—half the pay but 10x
|
|
||||||
the satisfaction. My retirement is secured, so every paycheck
|
|
||||||
goes to enjoying life now."
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="bg-foreground/10 rounded-lg p-6">
|
|
||||||
<h3 className="mb-2 text-xl font-semibold">
|
|
||||||
The Entrepreneur's Freedom
|
|
||||||
</h3>
|
|
||||||
<p className="text-sm">
|
|
||||||
"Reaching Coast FIRE at 38 gave me the courage to start my
|
|
||||||
business. Without needing to save for retirement, I could
|
|
||||||
reinvest everything back into growth. Best decision ever."
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
{/* Call to Action */}
|
|
||||||
<section className="bg-foreground/10 mb-12 rounded-lg p-8 text-center">
|
|
||||||
<h2 className="mb-4 text-2xl font-bold">
|
|
||||||
Want a Complete FIRE Plan?
|
|
||||||
</h2>
|
|
||||||
<p className="mb-6 text-lg">
|
|
||||||
Coast FIRE is just one strategy. Our comprehensive FIRE calculator
|
|
||||||
models your entire journey with multiple scenarios, withdrawal
|
|
||||||
strategies, and detailed projections.
|
|
||||||
</p>
|
|
||||||
<div className="flex flex-col justify-center gap-4 sm:flex-row">
|
|
||||||
<Link
|
|
||||||
href="/"
|
|
||||||
className="bg-primary text-primary-foreground inline-block rounded-lg px-6 py-3 font-semibold transition-opacity hover:opacity-90"
|
|
||||||
>
|
|
||||||
Try Full FIRE Calculator →
|
|
||||||
</Link>
|
|
||||||
<Link
|
|
||||||
href="/calculators/4-percent-rule"
|
|
||||||
className="bg-secondary text-secondary-foreground inline-block rounded-lg px-6 py-3 font-semibold transition-opacity hover:opacity-90"
|
|
||||||
>
|
|
||||||
Explore 4% Rule →
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Footer />
|
|
||||||
</main>
|
|
||||||
);
|
|
||||||
}
|
|
@@ -1,404 +0,0 @@
|
|||||||
"use client";
|
|
||||||
|
|
||||||
import { useState } from "react";
|
|
||||||
import {
|
|
||||||
Card,
|
|
||||||
CardContent,
|
|
||||||
CardDescription,
|
|
||||||
CardHeader,
|
|
||||||
CardTitle,
|
|
||||||
} from "@/components/ui/card";
|
|
||||||
import { Input } from "@/components/ui/input";
|
|
||||||
import { Label } from "@/components/ui/label";
|
|
||||||
import { Slider } from "@/components/ui/slider";
|
|
||||||
import {
|
|
||||||
Select,
|
|
||||||
SelectContent,
|
|
||||||
SelectItem,
|
|
||||||
SelectTrigger,
|
|
||||||
SelectValue,
|
|
||||||
} from "@/components/ui/select";
|
|
||||||
|
|
||||||
export default function FireByAgeCalculator() {
|
|
||||||
const [currentAge, setCurrentAge] = useState(25);
|
|
||||||
const [targetRetirementAge, setTargetRetirementAge] = useState(40);
|
|
||||||
const [currentSavings, setCurrentSavings] = useState(10000);
|
|
||||||
const [annualExpenses, setAnnualExpenses] = useState(50000);
|
|
||||||
const [expectedReturn, setExpectedReturn] = useState(7);
|
|
||||||
const [currentIncome, setCurrentIncome] = useState(75000);
|
|
||||||
|
|
||||||
// Calculate years to retirement
|
|
||||||
const yearsToRetirement = targetRetirementAge - currentAge;
|
|
||||||
|
|
||||||
// Determine withdrawal rate based on retirement age
|
|
||||||
const getWithdrawalRate = (age: number) => {
|
|
||||||
if (age <= 35) return 3;
|
|
||||||
if (age <= 40) return 3.5;
|
|
||||||
if (age <= 45) return 3.75;
|
|
||||||
if (age <= 50) return 4;
|
|
||||||
return 4.25;
|
|
||||||
};
|
|
||||||
|
|
||||||
const withdrawalRate = getWithdrawalRate(targetRetirementAge);
|
|
||||||
const fireMultiplier = 100 / withdrawalRate;
|
|
||||||
|
|
||||||
// Calculate FIRE number
|
|
||||||
const fireNumber = annualExpenses * fireMultiplier;
|
|
||||||
|
|
||||||
// Calculate future value of current savings
|
|
||||||
const futureValueOfCurrentSavings =
|
|
||||||
currentSavings * Math.pow(1 + expectedReturn / 100, yearsToRetirement);
|
|
||||||
|
|
||||||
// Calculate gap
|
|
||||||
const gap = fireNumber - futureValueOfCurrentSavings;
|
|
||||||
|
|
||||||
// Calculate required monthly savings
|
|
||||||
const calculateMonthlySavings = () => {
|
|
||||||
if (yearsToRetirement <= 0 || gap <= 0) return 0;
|
|
||||||
const monthlyReturn = expectedReturn / 100 / 12;
|
|
||||||
const months = yearsToRetirement * 12;
|
|
||||||
return (gap * monthlyReturn) / (Math.pow(1 + monthlyReturn, months) - 1);
|
|
||||||
};
|
|
||||||
|
|
||||||
const requiredMonthlySavings = calculateMonthlySavings();
|
|
||||||
const requiredAnnualSavings = requiredMonthlySavings * 12;
|
|
||||||
const savingsRate = (requiredAnnualSavings / currentIncome) * 100;
|
|
||||||
|
|
||||||
// Format currency
|
|
||||||
const formatCurrency = (value: number) => {
|
|
||||||
return new Intl.NumberFormat("en-US", {
|
|
||||||
style: "currency",
|
|
||||||
currency: "USD",
|
|
||||||
minimumFractionDigits: 0,
|
|
||||||
maximumFractionDigits: 0,
|
|
||||||
}).format(value);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Age-specific recommendations
|
|
||||||
const getRecommendations = () => {
|
|
||||||
if (targetRetirementAge <= 35) {
|
|
||||||
return {
|
|
||||||
difficulty: "Extremely Challenging",
|
|
||||||
color: "text-red-600",
|
|
||||||
tips: [
|
|
||||||
"Requires 60-80% savings rate",
|
|
||||||
"Focus on maximizing income",
|
|
||||||
"Consider geographic arbitrage",
|
|
||||||
"Live extremely frugally",
|
|
||||||
],
|
|
||||||
};
|
|
||||||
} else if (targetRetirementAge <= 40) {
|
|
||||||
return {
|
|
||||||
difficulty: "Very Challenging",
|
|
||||||
color: "text-orange-600",
|
|
||||||
tips: [
|
|
||||||
"Requires 50-60% savings rate",
|
|
||||||
"Maximize career growth",
|
|
||||||
"House hack or minimize housing",
|
|
||||||
"Avoid lifestyle inflation",
|
|
||||||
],
|
|
||||||
};
|
|
||||||
} else if (targetRetirementAge <= 45) {
|
|
||||||
return {
|
|
||||||
difficulty: "Challenging",
|
|
||||||
color: "text-yellow-600",
|
|
||||||
tips: [
|
|
||||||
"Requires 40-50% savings rate",
|
|
||||||
"Build multiple income streams",
|
|
||||||
"Consider Coast FIRE strategy",
|
|
||||||
"Plan for healthcare costs",
|
|
||||||
],
|
|
||||||
};
|
|
||||||
} else if (targetRetirementAge <= 50) {
|
|
||||||
return {
|
|
||||||
difficulty: "Moderate",
|
|
||||||
color: "text-blue-600",
|
|
||||||
tips: [
|
|
||||||
"Requires 30-40% savings rate",
|
|
||||||
"Use tax-advantaged accounts",
|
|
||||||
"Consider part-time work",
|
|
||||||
"Plan Roth conversions",
|
|
||||||
],
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
difficulty: "Achievable",
|
|
||||||
color: "text-green-600",
|
|
||||||
tips: [
|
|
||||||
"Requires 25-35% savings rate",
|
|
||||||
"Maximize 401(k) contributions",
|
|
||||||
"Use Rule of 55 if applicable",
|
|
||||||
"Bridge to Social Security",
|
|
||||||
],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const recommendations = getRecommendations();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="space-y-6">
|
|
||||||
{/* Input Section */}
|
|
||||||
<Card>
|
|
||||||
<CardHeader>
|
|
||||||
<CardTitle>Your FIRE by Age Inputs</CardTitle>
|
|
||||||
<CardDescription>
|
|
||||||
Enter your details to see what it takes to retire at your target age
|
|
||||||
</CardDescription>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent className="space-y-6">
|
|
||||||
<div className="grid gap-6 md:grid-cols-2">
|
|
||||||
<div className="space-y-2">
|
|
||||||
<div className="flex justify-between">
|
|
||||||
<Label htmlFor="current-age">Current Age</Label>
|
|
||||||
<span className="text-sm font-medium">{currentAge}</span>
|
|
||||||
</div>
|
|
||||||
<Slider
|
|
||||||
id="current-age"
|
|
||||||
min={20}
|
|
||||||
max={55}
|
|
||||||
step={1}
|
|
||||||
value={[currentAge]}
|
|
||||||
onValueChange={(value) => setCurrentAge(value[0] ?? 25)}
|
|
||||||
className="w-full"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="space-y-2">
|
|
||||||
<Label htmlFor="target-age">Target Retirement Age</Label>
|
|
||||||
<Select
|
|
||||||
value={targetRetirementAge.toString()}
|
|
||||||
onValueChange={(value) => setTargetRetirementAge(Number(value))}
|
|
||||||
>
|
|
||||||
<SelectTrigger id="target-age">
|
|
||||||
<SelectValue />
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent>
|
|
||||||
<SelectItem value="30">Retire at 30</SelectItem>
|
|
||||||
<SelectItem value="35">Retire at 35</SelectItem>
|
|
||||||
<SelectItem value="40">Retire at 40</SelectItem>
|
|
||||||
<SelectItem value="45">Retire at 45</SelectItem>
|
|
||||||
<SelectItem value="50">Retire at 50</SelectItem>
|
|
||||||
<SelectItem value="55">Retire at 55</SelectItem>
|
|
||||||
<SelectItem value="60">Retire at 60</SelectItem>
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="grid gap-6 md:grid-cols-2">
|
|
||||||
<div className="space-y-2">
|
|
||||||
<Label htmlFor="current-savings">
|
|
||||||
Current Retirement Savings
|
|
||||||
</Label>
|
|
||||||
<Input
|
|
||||||
id="current-savings"
|
|
||||||
type="number"
|
|
||||||
value={currentSavings}
|
|
||||||
onChange={(e) => setCurrentSavings(Number(e.target.value))}
|
|
||||||
min={0}
|
|
||||||
step={1000}
|
|
||||||
/>
|
|
||||||
<p className="text-muted-foreground text-sm">
|
|
||||||
Total invested for retirement
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="space-y-2">
|
|
||||||
<Label htmlFor="current-income">Current Annual Income</Label>
|
|
||||||
<Input
|
|
||||||
id="current-income"
|
|
||||||
type="number"
|
|
||||||
value={currentIncome}
|
|
||||||
onChange={(e) => setCurrentIncome(Number(e.target.value))}
|
|
||||||
min={0}
|
|
||||||
step={1000}
|
|
||||||
/>
|
|
||||||
<p className="text-muted-foreground text-sm">
|
|
||||||
Pre-tax annual income
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="grid gap-6 md:grid-cols-2">
|
|
||||||
<div className="space-y-2">
|
|
||||||
<Label htmlFor="annual-expenses">
|
|
||||||
Annual Expenses in Retirement
|
|
||||||
</Label>
|
|
||||||
<Input
|
|
||||||
id="annual-expenses"
|
|
||||||
type="number"
|
|
||||||
value={annualExpenses}
|
|
||||||
onChange={(e) => setAnnualExpenses(Number(e.target.value))}
|
|
||||||
min={0}
|
|
||||||
step={1000}
|
|
||||||
/>
|
|
||||||
<p className="text-muted-foreground text-sm">
|
|
||||||
Yearly spending needs
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="space-y-2">
|
|
||||||
<div className="flex justify-between">
|
|
||||||
<Label htmlFor="expected-return">Expected Annual Return</Label>
|
|
||||||
<span className="text-sm font-medium">{expectedReturn}%</span>
|
|
||||||
</div>
|
|
||||||
<Slider
|
|
||||||
id="expected-return"
|
|
||||||
min={4}
|
|
||||||
max={10}
|
|
||||||
step={0.5}
|
|
||||||
value={[expectedReturn]}
|
|
||||||
onValueChange={(value) => setExpectedReturn(value[0] ?? 7)}
|
|
||||||
className="w-full"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
{/* Results Section */}
|
|
||||||
<div className="grid gap-6 md:grid-cols-2">
|
|
||||||
<Card>
|
|
||||||
<CardHeader>
|
|
||||||
<CardTitle className="text-2xl">Your FIRE Number</CardTitle>
|
|
||||||
<CardDescription>
|
|
||||||
Target for retiring at {targetRetirementAge}
|
|
||||||
</CardDescription>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent>
|
|
||||||
<p className="text-primary text-4xl font-bold">
|
|
||||||
{formatCurrency(fireNumber)}
|
|
||||||
</p>
|
|
||||||
<p className="text-muted-foreground mt-2 text-sm">
|
|
||||||
{fireMultiplier.toFixed(1)}× annual expenses ({withdrawalRate}%
|
|
||||||
withdrawal rate)
|
|
||||||
</p>
|
|
||||||
<div className="bg-foreground/5 mt-4 rounded-lg p-3">
|
|
||||||
<p className="text-sm font-medium">Years to Retirement</p>
|
|
||||||
<p className="text-2xl font-bold">{yearsToRetirement}</p>
|
|
||||||
</div>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
<Card>
|
|
||||||
<CardHeader>
|
|
||||||
<CardTitle className="text-2xl">Required Monthly Savings</CardTitle>
|
|
||||||
<CardDescription>To reach your FIRE goal</CardDescription>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent>
|
|
||||||
<p className="text-primary text-4xl font-bold">
|
|
||||||
{formatCurrency(requiredMonthlySavings)}
|
|
||||||
</p>
|
|
||||||
<p className="text-muted-foreground mt-2 text-sm">
|
|
||||||
{formatCurrency(requiredAnnualSavings)}/year
|
|
||||||
</p>
|
|
||||||
<div className="bg-foreground/5 mt-4 rounded-lg p-3">
|
|
||||||
<p className="text-sm font-medium">Savings Rate Required</p>
|
|
||||||
<p className="text-2xl font-bold">
|
|
||||||
{Math.min(savingsRate, 100).toFixed(0)}%
|
|
||||||
</p>
|
|
||||||
{savingsRate > 100 && (
|
|
||||||
<p className="mt-1 text-xs text-red-600">
|
|
||||||
⚠️ Exceeds current income
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Difficulty Assessment */}
|
|
||||||
<Card>
|
|
||||||
<CardHeader>
|
|
||||||
<CardTitle>
|
|
||||||
Difficulty Assessment: Retiring at {targetRetirementAge}
|
|
||||||
</CardTitle>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent>
|
|
||||||
<div className="mb-4">
|
|
||||||
<p className="text-lg font-medium">Difficulty Level:</p>
|
|
||||||
<p className={`text-3xl font-bold ${recommendations.color}`}>
|
|
||||||
{recommendations.difficulty}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<p className="mb-3 text-lg font-medium">Key Success Factors:</p>
|
|
||||||
<ul className="ml-6 list-disc space-y-2">
|
|
||||||
{recommendations.tips.map((tip, idx) => (
|
|
||||||
<li key={idx} className="text-lg">
|
|
||||||
{tip}
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
{/* Breakdown */}
|
|
||||||
<Card>
|
|
||||||
<CardHeader>
|
|
||||||
<CardTitle>Your Path to FIRE at {targetRetirementAge}</CardTitle>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent>
|
|
||||||
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
|
|
||||||
<div className="bg-foreground/5 rounded-lg p-4 text-center">
|
|
||||||
<p className="text-muted-foreground text-sm">
|
|
||||||
Current Savings Growth
|
|
||||||
</p>
|
|
||||||
<p className="text-xl font-bold">
|
|
||||||
{formatCurrency(futureValueOfCurrentSavings)}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div className="bg-foreground/5 rounded-lg p-4 text-center">
|
|
||||||
<p className="text-muted-foreground text-sm">Additional Needed</p>
|
|
||||||
<p className="text-xl font-bold">
|
|
||||||
{formatCurrency(Math.max(gap, 0))}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div className="bg-foreground/5 rounded-lg p-4 text-center">
|
|
||||||
<p className="text-muted-foreground text-sm">
|
|
||||||
Total Contributions
|
|
||||||
</p>
|
|
||||||
<p className="text-xl font-bold">
|
|
||||||
{formatCurrency(requiredAnnualSavings * yearsToRetirement)}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div className="bg-foreground/5 rounded-lg p-4 text-center">
|
|
||||||
<p className="text-muted-foreground text-sm">Investment Growth</p>
|
|
||||||
<p className="text-xl font-bold">
|
|
||||||
{formatCurrency(
|
|
||||||
fireNumber -
|
|
||||||
currentSavings -
|
|
||||||
requiredAnnualSavings * yearsToRetirement,
|
|
||||||
)}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
{/* Tips Card */}
|
|
||||||
{savingsRate > 50 && (
|
|
||||||
<Card className="border-orange-200 bg-orange-50 dark:border-orange-900 dark:bg-orange-950/20">
|
|
||||||
<CardContent className="pt-6">
|
|
||||||
<p className="text-sm">
|
|
||||||
<strong>⚡ High Savings Rate Required:</strong> Achieving a{" "}
|
|
||||||
{savingsRate.toFixed(0)}% savings rate is challenging. Consider
|
|
||||||
increasing income through side hustles, reducing major expenses
|
|
||||||
like housing/transportation, or adjusting your target retirement
|
|
||||||
age to {targetRetirementAge + 5} for a more manageable{" "}
|
|
||||||
{(
|
|
||||||
((requiredAnnualSavings * yearsToRetirement) /
|
|
||||||
((targetRetirementAge + 5 - currentAge) * currentIncome)) *
|
|
||||||
100
|
|
||||||
).toFixed(0)}
|
|
||||||
% savings rate.
|
|
||||||
</p>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
@@ -1,625 +0,0 @@
|
|||||||
import type { Metadata } from "next";
|
|
||||||
import Link from "next/link";
|
|
||||||
import Image from "next/image";
|
|
||||||
import BackgroundPattern from "@/app/components/BackgroundPattern";
|
|
||||||
import Footer from "@/app/components/footer";
|
|
||||||
import FireByAgeCalculator from "./FireByAgeCalculator";
|
|
||||||
import {
|
|
||||||
Accordion,
|
|
||||||
AccordionContent,
|
|
||||||
AccordionItem,
|
|
||||||
AccordionTrigger,
|
|
||||||
} from "@/components/ui/accordion";
|
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
|
||||||
title: "FIRE by Age Guide - Retire at 30, 35, 40, 45, 50, 55 | InvestingFIRE",
|
|
||||||
description:
|
|
||||||
"Complete guide to achieving FIRE at any age. Learn how much you need to retire at 30, 35, 40, 45, 50, or 55. Free calculator with age-specific strategies and savings targets.",
|
|
||||||
keywords:
|
|
||||||
"retire at 40, retire at 45, retire at 50, retire at 35, retire at 30, early retirement by age, FIRE age calculator, how much to retire at 40",
|
|
||||||
openGraph: {
|
|
||||||
title: "FIRE by Age Guide - When Can You Retire?",
|
|
||||||
description:
|
|
||||||
"Discover exactly how much you need to retire at 30, 35, 40, 45, 50, or 55. Complete guide with calculator and age-specific strategies.",
|
|
||||||
type: "website",
|
|
||||||
url: "https://investingfire.com/guides/fire-by-age",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export default function FireByAgePage() {
|
|
||||||
const faqData = {
|
|
||||||
"@context": "https://schema.org",
|
|
||||||
"@type": "FAQPage",
|
|
||||||
mainEntity: [
|
|
||||||
{
|
|
||||||
"@type": "Question",
|
|
||||||
name: "How much do I need to retire at 40?",
|
|
||||||
acceptedAnswer: {
|
|
||||||
"@type": "Answer",
|
|
||||||
text: "To retire at 40, you typically need 25-30x your annual expenses saved. For $50,000/year in expenses, that's $1.25-1.5 million. The higher multiplier accounts for a longer retirement period. Starting at 25, you'd need to save about $3,000-4,000/month assuming 7% returns.",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"@type": "Question",
|
|
||||||
name: "Can I retire at 50 with $1 million?",
|
|
||||||
acceptedAnswer: {
|
|
||||||
"@type": "Answer",
|
|
||||||
text: "Yes, you can retire at 50 with $1 million if your annual expenses are $40,000 or less (using the 4% rule). For a more conservative 3.5% withdrawal rate, you'd need expenses under $35,000/year. Consider that you'll have 15 years before Medicare eligibility, so factor in health insurance costs.",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"@type": "Question",
|
|
||||||
name: "What's the best age to retire early?",
|
|
||||||
acceptedAnswer: {
|
|
||||||
"@type": "Answer",
|
|
||||||
text: "The 'best' age depends on your personal circumstances, but many FIRE achievers target 40-50. This balances having enough working years to accumulate wealth with plenty of healthy retirement years. Earlier retirement requires more aggressive saving and potentially lower withdrawal rates.",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"@type": "Question",
|
|
||||||
name: "How does retirement age affect withdrawal rates?",
|
|
||||||
acceptedAnswer: {
|
|
||||||
"@type": "Answer",
|
|
||||||
text: "Younger retirees should use lower withdrawal rates. While the 4% rule works for 30-year retirements, consider: Age 30-35: 3-3.25% withdrawal rate. Age 40-45: 3.5% withdrawal rate. Age 50-55: 3.75-4% withdrawal rate. Age 60+: 4-4.5% withdrawal rate.",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
const breadcrumbData = {
|
|
||||||
"@context": "https://schema.org",
|
|
||||||
"@type": "BreadcrumbList",
|
|
||||||
itemListElement: [
|
|
||||||
{
|
|
||||||
"@type": "ListItem",
|
|
||||||
position: 1,
|
|
||||||
name: "Home",
|
|
||||||
item: "https://investingfire.com",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"@type": "ListItem",
|
|
||||||
position: 2,
|
|
||||||
name: "Guides",
|
|
||||||
item: "https://investingfire.com/guides",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"@type": "ListItem",
|
|
||||||
position: 3,
|
|
||||||
name: "FIRE by Age",
|
|
||||||
item: "https://investingfire.com/guides/fire-by-age",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
const ageTargets = [
|
|
||||||
{
|
|
||||||
age: 30,
|
|
||||||
multiplier: 33,
|
|
||||||
withdrawalRate: 3,
|
|
||||||
savingsYears: "5-10",
|
|
||||||
challenges: [
|
|
||||||
"Extremely aggressive saving required",
|
|
||||||
"Limited career earnings time",
|
|
||||||
"60+ year retirement horizon",
|
|
||||||
],
|
|
||||||
strategies: [
|
|
||||||
"Save 70-80% of income",
|
|
||||||
"High-income career essential",
|
|
||||||
"Consider geographic arbitrage",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
age: 35,
|
|
||||||
multiplier: 30,
|
|
||||||
withdrawalRate: 3.25,
|
|
||||||
savingsYears: "10-15",
|
|
||||||
challenges: [
|
|
||||||
"Very high savings rate needed",
|
|
||||||
"Family formation years",
|
|
||||||
"55+ year retirement",
|
|
||||||
],
|
|
||||||
strategies: [
|
|
||||||
"Save 60-70% of income",
|
|
||||||
"Maximize career growth",
|
|
||||||
"House hack or minimize housing",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
age: 40,
|
|
||||||
multiplier: 28,
|
|
||||||
withdrawalRate: 3.5,
|
|
||||||
savingsYears: "15-20",
|
|
||||||
challenges: [
|
|
||||||
"Peak earning years cut short",
|
|
||||||
"Children's education costs",
|
|
||||||
"Healthcare before Medicare",
|
|
||||||
],
|
|
||||||
strategies: [
|
|
||||||
"Save 50-60% of income",
|
|
||||||
"Build multiple income streams",
|
|
||||||
"Plan for health insurance",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
age: 45,
|
|
||||||
multiplier: 27,
|
|
||||||
withdrawalRate: 3.75,
|
|
||||||
savingsYears: "20-25",
|
|
||||||
challenges: [
|
|
||||||
"Mid-career transition",
|
|
||||||
"Aging parents care",
|
|
||||||
"20 years to Medicare",
|
|
||||||
],
|
|
||||||
strategies: [
|
|
||||||
"Save 40-50% of income",
|
|
||||||
"Consider Coast FIRE first",
|
|
||||||
"Build health insurance fund",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
age: 50,
|
|
||||||
multiplier: 25,
|
|
||||||
withdrawalRate: 4,
|
|
||||||
savingsYears: "25-30",
|
|
||||||
challenges: [
|
|
||||||
"Early retirement penalties",
|
|
||||||
"15 years to Medicare",
|
|
||||||
"Sequence of returns risk",
|
|
||||||
],
|
|
||||||
strategies: [
|
|
||||||
"Save 30-40% of income",
|
|
||||||
"Ladder conversions",
|
|
||||||
"Part-time work option",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
age: 55,
|
|
||||||
multiplier: 25,
|
|
||||||
withdrawalRate: 4.25,
|
|
||||||
savingsYears: "30-35",
|
|
||||||
challenges: [
|
|
||||||
"10 years to Medicare",
|
|
||||||
"Social Security timing",
|
|
||||||
"Market volatility impact",
|
|
||||||
],
|
|
||||||
strategies: [
|
|
||||||
"Save 25-35% of income",
|
|
||||||
"Rule of 55 withdrawals",
|
|
||||||
"Bridge account planning",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
|
||||||
<main className="text-primary-foreground to-destructive from-secondary flex min-h-screen flex-col items-center bg-gradient-to-b p-2">
|
|
||||||
<BackgroundPattern />
|
|
||||||
|
|
||||||
{/* Header */}
|
|
||||||
<div className="z-10 mx-auto flex flex-col items-center justify-center gap-4 text-center">
|
|
||||||
<div className="mt-8 flex flex-row flex-wrap items-center justify-center gap-4 align-middle">
|
|
||||||
<Link
|
|
||||||
href="/"
|
|
||||||
className="flex items-center gap-4 transition-opacity hover:opacity-90"
|
|
||||||
>
|
|
||||||
<Image
|
|
||||||
priority
|
|
||||||
unoptimized
|
|
||||||
src="/investingfire_logo_no-bg.svg"
|
|
||||||
alt="InvestingFIRE Logo"
|
|
||||||
width={60}
|
|
||||||
height={60}
|
|
||||||
/>
|
|
||||||
<span className="text-2xl font-bold">InvestingFIRE</span>
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h1 className="from-primary via-primary-foreground to-primary mt-8 bg-gradient-to-r bg-clip-text text-4xl font-extrabold tracking-tight text-transparent drop-shadow-md sm:text-[4rem]">
|
|
||||||
FIRE by Age Guide
|
|
||||||
</h1>
|
|
||||||
<p className="text-primary-foreground/90 max-w-2xl text-xl font-semibold md:text-2xl">
|
|
||||||
Complete Guide to Retiring at 30, 35, 40, 45, 50, or 55
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Breadcrumb Schema */}
|
|
||||||
<script
|
|
||||||
type="application/ld+json"
|
|
||||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(breadcrumbData) }}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{/* Calculator */}
|
|
||||||
<div className="z-10 mt-8 w-full max-w-4xl">
|
|
||||||
<FireByAgeCalculator />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* SEO Content */}
|
|
||||||
<div className="z-10 mx-auto max-w-4xl px-4 py-12 text-left">
|
|
||||||
<section className="mb-12">
|
|
||||||
<h2 className="mb-4 text-3xl font-bold">
|
|
||||||
Your Complete Guide to FIRE at Any Age
|
|
||||||
</h2>
|
|
||||||
<p className="mb-4 text-lg leading-relaxed">
|
|
||||||
Achieving{" "}
|
|
||||||
<strong>Financial Independence, Retire Early (FIRE)</strong> is
|
|
||||||
possible at virtually any age, but the strategies, savings rates,
|
|
||||||
and challenges vary dramatically depending on when you want to
|
|
||||||
retire. This comprehensive guide breaks down exactly what it takes
|
|
||||||
to retire at 30, 35, 40, 45, 50, or 55.
|
|
||||||
</p>
|
|
||||||
<p className="mb-4 text-lg leading-relaxed">
|
|
||||||
The younger your target retirement age, the more aggressive your
|
|
||||||
approach needs to be. While retiring at 55 might require saving
|
|
||||||
25-35% of your income, retiring at 35 could demand 60-70% savings
|
|
||||||
rates and significant lifestyle adjustments. Let's explore what's
|
|
||||||
realistic for each age milestone.
|
|
||||||
</p>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
{/* Age-Specific Sections */}
|
|
||||||
{ageTargets.map((target) => (
|
|
||||||
<section
|
|
||||||
key={target.age}
|
|
||||||
className="mb-12 scroll-mt-20"
|
|
||||||
id={`retire-at-${target.age}`}
|
|
||||||
>
|
|
||||||
<h2 className="mb-4 text-3xl font-bold">
|
|
||||||
How to Retire at {target.age}: Complete Strategy
|
|
||||||
</h2>
|
|
||||||
|
|
||||||
<div className="bg-foreground/10 mb-6 rounded-lg p-6">
|
|
||||||
<h3 className="mb-4 text-xl font-semibold">
|
|
||||||
Quick Facts: Retiring at {target.age}
|
|
||||||
</h3>
|
|
||||||
<div className="grid gap-4 md:grid-cols-2">
|
|
||||||
<div>
|
|
||||||
<p className="font-medium">Target Multiple:</p>
|
|
||||||
<p className="text-primary text-2xl font-bold">
|
|
||||||
{target.multiplier}× annual expenses
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<p className="font-medium">Safe Withdrawal Rate:</p>
|
|
||||||
<p className="text-primary text-2xl font-bold">
|
|
||||||
{target.withdrawalRate}%
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<p className="font-medium">Typical Saving Period:</p>
|
|
||||||
<p className="text-primary text-2xl font-bold">
|
|
||||||
{target.savingsYears} years
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<p className="font-medium">For $50k/year expenses:</p>
|
|
||||||
<p className="text-primary text-2xl font-bold">
|
|
||||||
${(target.multiplier * 50).toLocaleString()}k needed
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="mb-6 grid gap-6 md:grid-cols-2">
|
|
||||||
<div>
|
|
||||||
<h3 className="mb-3 text-xl font-semibold">Key Challenges</h3>
|
|
||||||
<ul className="ml-6 list-disc space-y-2 text-lg">
|
|
||||||
{target.challenges.map((challenge, idx) => (
|
|
||||||
<li key={idx}>{challenge}</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<h3 className="mb-3 text-xl font-semibold">
|
|
||||||
Winning Strategies
|
|
||||||
</h3>
|
|
||||||
<ul className="ml-6 list-disc space-y-2 text-lg">
|
|
||||||
{target.strategies.map((strategy, idx) => (
|
|
||||||
<li key={idx}>{strategy}</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<p className="text-lg leading-relaxed">
|
|
||||||
{target.age <= 35 &&
|
|
||||||
"This ultra-early retirement requires exceptional discipline and often a very high income. Most successful retirees at this age work in tech, finance, or have entrepreneurial success. Geographic arbitrage is almost essential."}
|
|
||||||
{target.age > 35 &&
|
|
||||||
target.age <= 45 &&
|
|
||||||
"This is the sweet spot for many FIRE achievers - enough time to build wealth while still having decades of healthy retirement. Focus on maximizing your peak earning years and maintaining a high savings rate."}
|
|
||||||
{target.age > 45 &&
|
|
||||||
"This more traditional early retirement timeline allows for a balanced approach. You'll have more time to benefit from compound growth and can use strategies like the Rule of 55 for penalty-free 401(k) access."}
|
|
||||||
</p>
|
|
||||||
</section>
|
|
||||||
))}
|
|
||||||
|
|
||||||
<section className="mb-12">
|
|
||||||
<h2 className="mb-4 text-3xl font-bold">
|
|
||||||
Age-Specific FIRE Strategies Comparison
|
|
||||||
</h2>
|
|
||||||
|
|
||||||
<div className="overflow-x-auto">
|
|
||||||
<table className="bg-foreground/5 w-full border-collapse rounded-lg">
|
|
||||||
<thead>
|
|
||||||
<tr className="border-foreground/20 border-b">
|
|
||||||
<th className="p-4 text-left">Retirement Age</th>
|
|
||||||
<th className="p-4 text-center">Savings Rate</th>
|
|
||||||
<th className="p-4 text-center">Years to Save</th>
|
|
||||||
<th className="p-4 text-center">Withdrawal Rate</th>
|
|
||||||
<th className="p-4 text-center">Risk Level</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr className="border-foreground/10 border-b">
|
|
||||||
<td className="p-4 font-medium">Retire at 30</td>
|
|
||||||
<td className="p-4 text-center">70-80%</td>
|
|
||||||
<td className="p-4 text-center">5-10</td>
|
|
||||||
<td className="p-4 text-center">3%</td>
|
|
||||||
<td className="p-4 text-center text-red-600">Very High</td>
|
|
||||||
</tr>
|
|
||||||
<tr className="border-foreground/10 border-b">
|
|
||||||
<td className="p-4 font-medium">Retire at 35</td>
|
|
||||||
<td className="p-4 text-center">60-70%</td>
|
|
||||||
<td className="p-4 text-center">10-15</td>
|
|
||||||
<td className="p-4 text-center">3.25%</td>
|
|
||||||
<td className="p-4 text-center text-orange-600">High</td>
|
|
||||||
</tr>
|
|
||||||
<tr className="border-foreground/10 border-b">
|
|
||||||
<td className="p-4 font-medium">Retire at 40</td>
|
|
||||||
<td className="p-4 text-center">50-60%</td>
|
|
||||||
<td className="p-4 text-center">15-20</td>
|
|
||||||
<td className="p-4 text-center">3.5%</td>
|
|
||||||
<td className="p-4 text-center text-yellow-600">
|
|
||||||
Moderate-High
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr className="border-foreground/10 border-b">
|
|
||||||
<td className="p-4 font-medium">Retire at 45</td>
|
|
||||||
<td className="p-4 text-center">40-50%</td>
|
|
||||||
<td className="p-4 text-center">20-25</td>
|
|
||||||
<td className="p-4 text-center">3.75%</td>
|
|
||||||
<td className="p-4 text-center text-blue-600">Moderate</td>
|
|
||||||
</tr>
|
|
||||||
<tr className="border-foreground/10 border-b">
|
|
||||||
<td className="p-4 font-medium">Retire at 50</td>
|
|
||||||
<td className="p-4 text-center">30-40%</td>
|
|
||||||
<td className="p-4 text-center">25-30</td>
|
|
||||||
<td className="p-4 text-center">4%</td>
|
|
||||||
<td className="p-4 text-center text-green-600">
|
|
||||||
Low-Moderate
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td className="p-4 font-medium">Retire at 55</td>
|
|
||||||
<td className="p-4 text-center">25-35%</td>
|
|
||||||
<td className="p-4 text-center">30-35</td>
|
|
||||||
<td className="p-4 text-center">4.25%</td>
|
|
||||||
<td className="p-4 text-center text-green-600">Low</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section className="mb-12">
|
|
||||||
<h2 className="mb-4 text-3xl font-bold">
|
|
||||||
Critical Considerations by Retirement Age
|
|
||||||
</h2>
|
|
||||||
|
|
||||||
<div className="space-y-6">
|
|
||||||
<div className="bg-foreground/10 rounded-lg p-6">
|
|
||||||
<h3 className="mb-3 text-xl font-semibold">
|
|
||||||
Healthcare Coverage Gap
|
|
||||||
</h3>
|
|
||||||
<ul className="ml-6 list-disc space-y-2 text-lg">
|
|
||||||
<li>
|
|
||||||
<strong>Retire at 30-40:</strong> 25-35 years until Medicare -
|
|
||||||
Budget $15-25k/year for health insurance
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<strong>Retire at 45-50:</strong> 15-20 years gap - Consider
|
|
||||||
ACA subsidies and HSA maximization
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<strong>Retire at 55:</strong> 10-year gap - Explore COBRA,
|
|
||||||
spouse's plan, or private insurance
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="bg-foreground/10 rounded-lg p-6">
|
|
||||||
<h3 className="mb-3 text-xl font-semibold">
|
|
||||||
Social Security Strategy
|
|
||||||
</h3>
|
|
||||||
<ul className="ml-6 list-disc space-y-2 text-lg">
|
|
||||||
<li>
|
|
||||||
<strong>Retire before 35:</strong> Minimal SS benefits - Plan
|
|
||||||
without relying on it
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<strong>Retire at 40-45:</strong> Reduced benefits - Factor in
|
|
||||||
25-50% of normal benefit
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<strong>Retire at 50-55:</strong> Near-full benefits - Can be
|
|
||||||
significant income supplement
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="bg-foreground/10 rounded-lg p-6">
|
|
||||||
<h3 className="mb-3 text-xl font-semibold">
|
|
||||||
Investment Allocation
|
|
||||||
</h3>
|
|
||||||
<ul className="ml-6 list-disc space-y-2 text-lg">
|
|
||||||
<li>
|
|
||||||
<strong>60+ year retirement:</strong> 80-90% stocks for
|
|
||||||
growth, rebalance gradually
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<strong>40-50 year retirement:</strong> 70-80% stocks,
|
|
||||||
consider bond ladder for first decade
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<strong>30-40 year retirement:</strong> 60-70% stocks,
|
|
||||||
traditional balanced approach
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
{/* FAQ Section */}
|
|
||||||
<section className="mb-12">
|
|
||||||
<script
|
|
||||||
type="application/ld+json"
|
|
||||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(faqData) }}
|
|
||||||
/>
|
|
||||||
<h2 className="mb-4 text-3xl font-bold">FIRE by Age FAQ</h2>
|
|
||||||
|
|
||||||
<Accordion type="single" collapsible className="w-full">
|
|
||||||
<AccordionItem value="item-1">
|
|
||||||
<AccordionTrigger className="text-xl font-semibold">
|
|
||||||
How much do I need to retire at 40?
|
|
||||||
</AccordionTrigger>
|
|
||||||
<AccordionContent className="text-lg leading-relaxed">
|
|
||||||
To retire at 40, you typically need 25-30x your annual expenses
|
|
||||||
saved. For $50,000/year in expenses, that's $1.25-1.5 million.
|
|
||||||
The higher multiplier accounts for a longer retirement period.
|
|
||||||
Starting at 25, you'd need to save about $3,000-4,000/month
|
|
||||||
assuming 7% returns.
|
|
||||||
</AccordionContent>
|
|
||||||
</AccordionItem>
|
|
||||||
|
|
||||||
<AccordionItem value="item-2">
|
|
||||||
<AccordionTrigger className="text-xl font-semibold">
|
|
||||||
Can I retire at 50 with $1 million?
|
|
||||||
</AccordionTrigger>
|
|
||||||
<AccordionContent className="text-lg leading-relaxed">
|
|
||||||
Yes, you can retire at 50 with $1 million if your annual
|
|
||||||
expenses are $40,000 or less (using the 4% rule). For a more
|
|
||||||
conservative 3.5% withdrawal rate, you'd need expenses under
|
|
||||||
$35,000/year. Consider that you'll have 15 years before Medicare
|
|
||||||
eligibility, so factor in health insurance costs.
|
|
||||||
</AccordionContent>
|
|
||||||
</AccordionItem>
|
|
||||||
|
|
||||||
<AccordionItem value="item-3">
|
|
||||||
<AccordionTrigger className="text-xl font-semibold">
|
|
||||||
What's the best age to retire early?
|
|
||||||
</AccordionTrigger>
|
|
||||||
<AccordionContent className="text-lg leading-relaxed">
|
|
||||||
The "best" age depends on your personal circumstances, but many
|
|
||||||
FIRE achievers target 40-50. This balances having enough working
|
|
||||||
years to accumulate wealth with plenty of healthy retirement
|
|
||||||
years. Earlier retirement requires more aggressive saving and
|
|
||||||
potentially lower withdrawal rates.
|
|
||||||
</AccordionContent>
|
|
||||||
</AccordionItem>
|
|
||||||
|
|
||||||
<AccordionItem value="item-4">
|
|
||||||
<AccordionTrigger className="text-xl font-semibold">
|
|
||||||
How does retirement age affect withdrawal rates?
|
|
||||||
</AccordionTrigger>
|
|
||||||
<AccordionContent className="text-lg leading-relaxed">
|
|
||||||
Younger retirees should use lower withdrawal rates. While the 4%
|
|
||||||
rule works for 30-year retirements, consider:
|
|
||||||
<ul className="mt-2 ml-6 list-disc">
|
|
||||||
<li>Age 30-35: 3-3.25% withdrawal rate</li>
|
|
||||||
<li>Age 40-45: 3.5% withdrawal rate</li>
|
|
||||||
<li>Age 50-55: 3.75-4% withdrawal rate</li>
|
|
||||||
<li>Age 60+: 4-4.5% withdrawal rate</li>
|
|
||||||
</ul>
|
|
||||||
</AccordionContent>
|
|
||||||
</AccordionItem>
|
|
||||||
|
|
||||||
<AccordionItem value="item-5">
|
|
||||||
<AccordionTrigger className="text-xl font-semibold">
|
|
||||||
Is retiring at 35 realistic?
|
|
||||||
</AccordionTrigger>
|
|
||||||
<AccordionContent className="text-lg leading-relaxed">
|
|
||||||
Retiring at 35 is challenging but achievable with the right
|
|
||||||
circumstances: high income ($100k+), low expenses, 60-70%
|
|
||||||
savings rate, and disciplined investing. Most who achieve this
|
|
||||||
work in high-paying fields, live frugally, and often have no
|
|
||||||
children or delay having them. Geographic arbitrage to low-cost
|
|
||||||
areas helps significantly.
|
|
||||||
</AccordionContent>
|
|
||||||
</AccordionItem>
|
|
||||||
|
|
||||||
<AccordionItem value="item-6">
|
|
||||||
<AccordionTrigger className="text-xl font-semibold">
|
|
||||||
What about retiring with kids?
|
|
||||||
</AccordionTrigger>
|
|
||||||
<AccordionContent className="text-lg leading-relaxed">
|
|
||||||
Retiring early with children adds complexity but is doable. Key
|
|
||||||
considerations: Budget $10-15k per child annually, plan for
|
|
||||||
college (529 plans), factor in larger housing needs, and
|
|
||||||
consider part-time work for stability. Many FIRE families find
|
|
||||||
that having more time with kids is worth the extra financial
|
|
||||||
planning required.
|
|
||||||
</AccordionContent>
|
|
||||||
</AccordionItem>
|
|
||||||
</Accordion>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
{/* Quick Reference */}
|
|
||||||
<section className="mb-12">
|
|
||||||
<h2 className="mb-4 text-3xl font-bold">
|
|
||||||
Quick Reference: FIRE Numbers by Age
|
|
||||||
</h2>
|
|
||||||
<div className="bg-foreground/10 rounded-lg p-6">
|
|
||||||
<p className="mb-4 text-lg font-medium">
|
|
||||||
Based on $50,000 annual expenses:
|
|
||||||
</p>
|
|
||||||
<div className="grid gap-3 md:grid-cols-2">
|
|
||||||
{ageTargets.map((target) => (
|
|
||||||
<div
|
|
||||||
key={target.age}
|
|
||||||
className="bg-background/50 flex justify-between rounded p-3"
|
|
||||||
>
|
|
||||||
<span className="font-medium">Retire at {target.age}:</span>
|
|
||||||
<span className="text-primary font-bold">
|
|
||||||
${(target.multiplier * 50).toLocaleString()},000
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
{/* Call to Action */}
|
|
||||||
<section className="bg-foreground/10 mb-12 rounded-lg p-8 text-center">
|
|
||||||
<h2 className="mb-4 text-2xl font-bold">
|
|
||||||
Ready to Plan Your Early Retirement?
|
|
||||||
</h2>
|
|
||||||
<p className="mb-6 text-lg">
|
|
||||||
Use our comprehensive calculators to create your personalized FIRE
|
|
||||||
plan, whether you're targeting retirement at 35 or 55.
|
|
||||||
</p>
|
|
||||||
<div className="flex flex-col justify-center gap-4 sm:flex-row">
|
|
||||||
<Link
|
|
||||||
href="/"
|
|
||||||
className="bg-primary text-primary-foreground inline-block rounded-lg px-6 py-3 font-semibold transition-opacity hover:opacity-90"
|
|
||||||
>
|
|
||||||
FIRE Calculator →
|
|
||||||
</Link>
|
|
||||||
<Link
|
|
||||||
href="/calculators/coast-fire"
|
|
||||||
className="bg-secondary text-secondary-foreground inline-block rounded-lg px-6 py-3 font-semibold transition-opacity hover:opacity-90"
|
|
||||||
>
|
|
||||||
Coast FIRE Calculator →
|
|
||||||
</Link>
|
|
||||||
<Link
|
|
||||||
href="/calculators/4-percent-rule"
|
|
||||||
className="bg-secondary text-secondary-foreground inline-block rounded-lg px-6 py-3 font-semibold transition-opacity hover:opacity-90"
|
|
||||||
>
|
|
||||||
4% Rule Calculator →
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Footer />
|
|
||||||
</main>
|
|
||||||
);
|
|
||||||
}
|
|
@@ -9,23 +9,5 @@ export default function sitemap(): MetadataRoute.Sitemap {
|
|||||||
changeFrequency: "yearly",
|
changeFrequency: "yearly",
|
||||||
priority: 1,
|
priority: 1,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
url: `${BASE_URL}/calculators/4-percent-rule`,
|
|
||||||
lastModified: new Date(),
|
|
||||||
changeFrequency: "monthly",
|
|
||||||
priority: 0.9,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
url: `${BASE_URL}/calculators/coast-fire`,
|
|
||||||
lastModified: new Date(),
|
|
||||||
changeFrequency: "monthly",
|
|
||||||
priority: 0.9,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
url: `${BASE_URL}/guides/fire-by-age`,
|
|
||||||
lastModified: new Date(),
|
|
||||||
changeFrequency: "monthly",
|
|
||||||
priority: 0.9,
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user