diff --git a/package-lock.json b/package-lock.json
index ec48166..3314280 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8,12 +8,37 @@
"name": "profit-planet-frontend",
"version": "0.1.0",
"dependencies": {
+ "@headlessui/react": "^2.2.9",
+ "@heroicons/react": "^2.2.0",
+ "@hookform/resolvers": "^5.2.2",
+ "@lottiefiles/react-lottie-player": "^3.6.0",
+ "@react-pdf/renderer": "^4.3.0",
+ "@tailwindcss/forms": "^0.5.10",
+ "@tailwindcss/typography": "^0.5.19",
+ "@tailwindplus/elements": "^1.0.15",
+ "@tailwindui/react": "^0.1.1",
+ "axios": "^1.12.2",
+ "clsx": "^2.1.1",
+ "country-flag-icons": "^1.5.21",
+ "country-select-js": "^2.1.0",
+ "intl-tel-input": "^25.10.11",
+ "motion": "^12.23.22",
"next": "15.5.4",
+ "pdfjs-dist": "^5.4.149",
"react": "19.1.0",
- "react-dom": "19.1.0"
+ "react-dom": "19.1.0",
+ "react-hook-form": "^7.63.0",
+ "react-hot-toast": "^2.6.0",
+ "react-pdf": "^10.1.0",
+ "react-phone-number-input": "^3.4.12",
+ "react-toastify": "^11.0.5",
+ "winston": "^3.17.0",
+ "yup": "^1.7.1",
+ "zustand": "^5.0.8"
},
"devDependencies": {
"@eslint/eslintrc": "^3",
+ "@eslint/js": "^9.36.0",
"@tailwindcss/postcss": "^4",
"@types/node": "^20",
"@types/react": "^19",
@@ -21,7 +46,10 @@
"autoprefixer": "^10.4.21",
"eslint": "^9",
"eslint-config-next": "15.5.4",
+ "eslint-plugin-react-hooks": "^5.2.0",
+ "globals": "^16.4.0",
"postcss": "^8.5.6",
+ "postcss-preset-env": "^10.4.0",
"tailwindcss": "^4.1.13",
"typescript": "^5"
}
@@ -39,6 +67,1568 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/@babel/code-frame": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz",
+ "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-validator-identifier": "^7.27.1",
+ "js-tokens": "^4.0.0",
+ "picocolors": "^1.1.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/compat-data": {
+ "version": "7.28.4",
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.4.tgz",
+ "integrity": "sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/core": {
+ "version": "7.28.4",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.4.tgz",
+ "integrity": "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.27.1",
+ "@babel/generator": "^7.28.3",
+ "@babel/helper-compilation-targets": "^7.27.2",
+ "@babel/helper-module-transforms": "^7.28.3",
+ "@babel/helpers": "^7.28.4",
+ "@babel/parser": "^7.28.4",
+ "@babel/template": "^7.27.2",
+ "@babel/traverse": "^7.28.4",
+ "@babel/types": "^7.28.4",
+ "@jridgewell/remapping": "^2.3.5",
+ "convert-source-map": "^2.0.0",
+ "debug": "^4.1.0",
+ "gensync": "^1.0.0-beta.2",
+ "json5": "^2.2.3",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/babel"
+ }
+ },
+ "node_modules/@babel/core/node_modules/json5": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
+ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
+ "license": "MIT",
+ "bin": {
+ "json5": "lib/cli.js"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/@babel/core/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/@babel/generator": {
+ "version": "7.28.3",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz",
+ "integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.28.3",
+ "@babel/types": "^7.28.2",
+ "@jridgewell/gen-mapping": "^0.3.12",
+ "@jridgewell/trace-mapping": "^0.3.28",
+ "jsesc": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-compilation-targets": {
+ "version": "7.27.2",
+ "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz",
+ "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/compat-data": "^7.27.2",
+ "@babel/helper-validator-option": "^7.27.1",
+ "browserslist": "^4.24.0",
+ "lru-cache": "^5.1.1",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-compilation-targets/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/@babel/helper-globals": {
+ "version": "7.28.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz",
+ "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-imports": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz",
+ "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/traverse": "^7.27.1",
+ "@babel/types": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-transforms": {
+ "version": "7.28.3",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz",
+ "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-module-imports": "^7.27.1",
+ "@babel/helper-validator-identifier": "^7.27.1",
+ "@babel/traverse": "^7.28.3"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-plugin-utils": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz",
+ "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-string-parser": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
+ "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-identifier": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz",
+ "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-option": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz",
+ "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helpers": {
+ "version": "7.28.4",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz",
+ "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/template": "^7.27.2",
+ "@babel/types": "^7.28.4"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/parser": {
+ "version": "7.28.4",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.4.tgz",
+ "integrity": "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.28.4"
+ },
+ "bin": {
+ "parser": "bin/babel-parser.js"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-proposal-export-namespace-from": {
+ "version": "7.18.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz",
+ "integrity": "sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==",
+ "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-export-namespace-from instead.",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.18.9",
+ "@babel/plugin-syntax-export-namespace-from": "^7.8.3"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-export-namespace-from": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz",
+ "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.8.3"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-modules-commonjs": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.27.1.tgz",
+ "integrity": "sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-module-transforms": "^7.27.1",
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/runtime": {
+ "version": "7.28.4",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz",
+ "integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/template": {
+ "version": "7.27.2",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz",
+ "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.27.1",
+ "@babel/parser": "^7.27.2",
+ "@babel/types": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/traverse": {
+ "version": "7.28.4",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.4.tgz",
+ "integrity": "sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.27.1",
+ "@babel/generator": "^7.28.3",
+ "@babel/helper-globals": "^7.28.0",
+ "@babel/parser": "^7.28.4",
+ "@babel/template": "^7.27.2",
+ "@babel/types": "^7.28.4",
+ "debug": "^4.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/types": {
+ "version": "7.28.4",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.4.tgz",
+ "integrity": "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-string-parser": "^7.27.1",
+ "@babel/helper-validator-identifier": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@colors/colors": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz",
+ "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.1.90"
+ }
+ },
+ "node_modules/@csstools/cascade-layer-name-parser": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@csstools/cascade-layer-name-parser/-/cascade-layer-name-parser-2.0.5.tgz",
+ "integrity": "sha512-p1ko5eHgV+MgXFVa4STPKpvPxr6ReS8oS2jzTukjR74i5zJNyWO1ZM1m8YKBXnzDKWfBN1ztLYlHxbVemDD88A==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@csstools/css-parser-algorithms": "^3.0.5",
+ "@csstools/css-tokenizer": "^3.0.4"
+ }
+ },
+ "node_modules/@csstools/color-helpers": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.1.0.tgz",
+ "integrity": "sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@csstools/css-calc": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.4.tgz",
+ "integrity": "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@csstools/css-parser-algorithms": "^3.0.5",
+ "@csstools/css-tokenizer": "^3.0.4"
+ }
+ },
+ "node_modules/@csstools/css-color-parser": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.1.0.tgz",
+ "integrity": "sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "@csstools/color-helpers": "^5.1.0",
+ "@csstools/css-calc": "^2.1.4"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@csstools/css-parser-algorithms": "^3.0.5",
+ "@csstools/css-tokenizer": "^3.0.4"
+ }
+ },
+ "node_modules/@csstools/css-parser-algorithms": {
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz",
+ "integrity": "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@csstools/css-tokenizer": "^3.0.4"
+ }
+ },
+ "node_modules/@csstools/css-tokenizer": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz",
+ "integrity": "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@csstools/media-query-list-parser": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-4.0.3.tgz",
+ "integrity": "sha512-HAYH7d3TLRHDOUQK4mZKf9k9Ph/m8Akstg66ywKR4SFAigjs3yBiUeZtFxywiTm5moZMAp/5W/ZuFnNXXYLuuQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@csstools/css-parser-algorithms": "^3.0.5",
+ "@csstools/css-tokenizer": "^3.0.4"
+ }
+ },
+ "node_modules/@csstools/postcss-alpha-function": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-alpha-function/-/postcss-alpha-function-1.0.1.tgz",
+ "integrity": "sha512-isfLLwksH3yHkFXfCI2Gcaqg7wGGHZZwunoJzEZk0yKYIokgre6hYVFibKL3SYAoR1kBXova8LB+JoO5vZzi9w==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "@csstools/css-color-parser": "^3.1.0",
+ "@csstools/css-parser-algorithms": "^3.0.5",
+ "@csstools/css-tokenizer": "^3.0.4",
+ "@csstools/postcss-progressive-custom-properties": "^4.2.1",
+ "@csstools/utilities": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/@csstools/postcss-cascade-layers": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-cascade-layers/-/postcss-cascade-layers-5.0.2.tgz",
+ "integrity": "sha512-nWBE08nhO8uWl6kSAeCx4im7QfVko3zLrtgWZY4/bP87zrSPpSyN/3W3TDqz1jJuH+kbKOHXg5rJnK+ZVYcFFg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "@csstools/selector-specificity": "^5.0.0",
+ "postcss-selector-parser": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/@csstools/postcss-color-function": {
+ "version": "4.0.12",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-color-function/-/postcss-color-function-4.0.12.tgz",
+ "integrity": "sha512-yx3cljQKRaSBc2hfh8rMZFZzChaFgwmO2JfFgFr1vMcF3C/uyy5I4RFIBOIWGq1D+XbKCG789CGkG6zzkLpagA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "@csstools/css-color-parser": "^3.1.0",
+ "@csstools/css-parser-algorithms": "^3.0.5",
+ "@csstools/css-tokenizer": "^3.0.4",
+ "@csstools/postcss-progressive-custom-properties": "^4.2.1",
+ "@csstools/utilities": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/@csstools/postcss-color-function-display-p3-linear": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-color-function-display-p3-linear/-/postcss-color-function-display-p3-linear-1.0.1.tgz",
+ "integrity": "sha512-E5qusdzhlmO1TztYzDIi8XPdPoYOjoTY6HBYBCYSj+Gn4gQRBlvjgPQXzfzuPQqt8EhkC/SzPKObg4Mbn8/xMg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "@csstools/css-color-parser": "^3.1.0",
+ "@csstools/css-parser-algorithms": "^3.0.5",
+ "@csstools/css-tokenizer": "^3.0.4",
+ "@csstools/postcss-progressive-custom-properties": "^4.2.1",
+ "@csstools/utilities": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/@csstools/postcss-color-mix-function": {
+ "version": "3.0.12",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-color-mix-function/-/postcss-color-mix-function-3.0.12.tgz",
+ "integrity": "sha512-4STERZfCP5Jcs13P1U5pTvI9SkgLgfMUMhdXW8IlJWkzOOOqhZIjcNhWtNJZes2nkBDsIKJ0CJtFtuaZ00moag==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "@csstools/css-color-parser": "^3.1.0",
+ "@csstools/css-parser-algorithms": "^3.0.5",
+ "@csstools/css-tokenizer": "^3.0.4",
+ "@csstools/postcss-progressive-custom-properties": "^4.2.1",
+ "@csstools/utilities": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/@csstools/postcss-color-mix-variadic-function-arguments": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-color-mix-variadic-function-arguments/-/postcss-color-mix-variadic-function-arguments-1.0.2.tgz",
+ "integrity": "sha512-rM67Gp9lRAkTo+X31DUqMEq+iK+EFqsidfecmhrteErxJZb6tUoJBVQca1Vn1GpDql1s1rD1pKcuYzMsg7Z1KQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "@csstools/css-color-parser": "^3.1.0",
+ "@csstools/css-parser-algorithms": "^3.0.5",
+ "@csstools/css-tokenizer": "^3.0.4",
+ "@csstools/postcss-progressive-custom-properties": "^4.2.1",
+ "@csstools/utilities": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/@csstools/postcss-content-alt-text": {
+ "version": "2.0.8",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-content-alt-text/-/postcss-content-alt-text-2.0.8.tgz",
+ "integrity": "sha512-9SfEW9QCxEpTlNMnpSqFaHyzsiRpZ5J5+KqCu1u5/eEJAWsMhzT40qf0FIbeeglEvrGRMdDzAxMIz3wqoGSb+Q==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "@csstools/css-parser-algorithms": "^3.0.5",
+ "@csstools/css-tokenizer": "^3.0.4",
+ "@csstools/postcss-progressive-custom-properties": "^4.2.1",
+ "@csstools/utilities": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/@csstools/postcss-contrast-color-function": {
+ "version": "2.0.12",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-contrast-color-function/-/postcss-contrast-color-function-2.0.12.tgz",
+ "integrity": "sha512-YbwWckjK3qwKjeYz/CijgcS7WDUCtKTd8ShLztm3/i5dhh4NaqzsbYnhm4bjrpFpnLZ31jVcbK8YL77z3GBPzA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "@csstools/css-color-parser": "^3.1.0",
+ "@csstools/css-parser-algorithms": "^3.0.5",
+ "@csstools/css-tokenizer": "^3.0.4",
+ "@csstools/postcss-progressive-custom-properties": "^4.2.1",
+ "@csstools/utilities": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/@csstools/postcss-exponential-functions": {
+ "version": "2.0.9",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-exponential-functions/-/postcss-exponential-functions-2.0.9.tgz",
+ "integrity": "sha512-abg2W/PI3HXwS/CZshSa79kNWNZHdJPMBXeZNyPQFbbj8sKO3jXxOt/wF7juJVjyDTc6JrvaUZYFcSBZBhaxjw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "@csstools/css-calc": "^2.1.4",
+ "@csstools/css-parser-algorithms": "^3.0.5",
+ "@csstools/css-tokenizer": "^3.0.4"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/@csstools/postcss-font-format-keywords": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-font-format-keywords/-/postcss-font-format-keywords-4.0.0.tgz",
+ "integrity": "sha512-usBzw9aCRDvchpok6C+4TXC57btc4bJtmKQWOHQxOVKen1ZfVqBUuCZ/wuqdX5GHsD0NRSr9XTP+5ID1ZZQBXw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "@csstools/utilities": "^2.0.0",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/@csstools/postcss-gamut-mapping": {
+ "version": "2.0.11",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-gamut-mapping/-/postcss-gamut-mapping-2.0.11.tgz",
+ "integrity": "sha512-fCpCUgZNE2piVJKC76zFsgVW1apF6dpYsqGyH8SIeCcM4pTEsRTWTLCaJIMKFEundsCKwY1rwfhtrio04RJ4Dw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "@csstools/css-color-parser": "^3.1.0",
+ "@csstools/css-parser-algorithms": "^3.0.5",
+ "@csstools/css-tokenizer": "^3.0.4"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/@csstools/postcss-gradients-interpolation-method": {
+ "version": "5.0.12",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-gradients-interpolation-method/-/postcss-gradients-interpolation-method-5.0.12.tgz",
+ "integrity": "sha512-jugzjwkUY0wtNrZlFeyXzimUL3hN4xMvoPnIXxoZqxDvjZRiSh+itgHcVUWzJ2VwD/VAMEgCLvtaJHX+4Vj3Ow==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "@csstools/css-color-parser": "^3.1.0",
+ "@csstools/css-parser-algorithms": "^3.0.5",
+ "@csstools/css-tokenizer": "^3.0.4",
+ "@csstools/postcss-progressive-custom-properties": "^4.2.1",
+ "@csstools/utilities": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/@csstools/postcss-hwb-function": {
+ "version": "4.0.12",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-hwb-function/-/postcss-hwb-function-4.0.12.tgz",
+ "integrity": "sha512-mL/+88Z53KrE4JdePYFJAQWFrcADEqsLprExCM04GDNgHIztwFzj0Mbhd/yxMBngq0NIlz58VVxjt5abNs1VhA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "@csstools/css-color-parser": "^3.1.0",
+ "@csstools/css-parser-algorithms": "^3.0.5",
+ "@csstools/css-tokenizer": "^3.0.4",
+ "@csstools/postcss-progressive-custom-properties": "^4.2.1",
+ "@csstools/utilities": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/@csstools/postcss-ic-unit": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-ic-unit/-/postcss-ic-unit-4.0.4.tgz",
+ "integrity": "sha512-yQ4VmossuOAql65sCPppVO1yfb7hDscf4GseF0VCA/DTDaBc0Wtf8MTqVPfjGYlT5+2buokG0Gp7y0atYZpwjg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "@csstools/postcss-progressive-custom-properties": "^4.2.1",
+ "@csstools/utilities": "^2.0.0",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/@csstools/postcss-initial": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-initial/-/postcss-initial-2.0.1.tgz",
+ "integrity": "sha512-L1wLVMSAZ4wovznquK0xmC7QSctzO4D0Is590bxpGqhqjboLXYA16dWZpfwImkdOgACdQ9PqXsuRroW6qPlEsg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/@csstools/postcss-is-pseudo-class": {
+ "version": "5.0.3",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-is-pseudo-class/-/postcss-is-pseudo-class-5.0.3.tgz",
+ "integrity": "sha512-jS/TY4SpG4gszAtIg7Qnf3AS2pjcUM5SzxpApOrlndMeGhIbaTzWBzzP/IApXoNWEW7OhcjkRT48jnAUIFXhAQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "@csstools/selector-specificity": "^5.0.0",
+ "postcss-selector-parser": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/@csstools/postcss-light-dark-function": {
+ "version": "2.0.11",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-light-dark-function/-/postcss-light-dark-function-2.0.11.tgz",
+ "integrity": "sha512-fNJcKXJdPM3Lyrbmgw2OBbaioU7yuKZtiXClf4sGdQttitijYlZMD5K7HrC/eF83VRWRrYq6OZ0Lx92leV2LFA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "@csstools/css-parser-algorithms": "^3.0.5",
+ "@csstools/css-tokenizer": "^3.0.4",
+ "@csstools/postcss-progressive-custom-properties": "^4.2.1",
+ "@csstools/utilities": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/@csstools/postcss-logical-float-and-clear": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-logical-float-and-clear/-/postcss-logical-float-and-clear-3.0.0.tgz",
+ "integrity": "sha512-SEmaHMszwakI2rqKRJgE+8rpotFfne1ZS6bZqBoQIicFyV+xT1UF42eORPxJkVJVrH9C0ctUgwMSn3BLOIZldQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/@csstools/postcss-logical-overflow": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-logical-overflow/-/postcss-logical-overflow-2.0.0.tgz",
+ "integrity": "sha512-spzR1MInxPuXKEX2csMamshR4LRaSZ3UXVaRGjeQxl70ySxOhMpP2252RAFsg8QyyBXBzuVOOdx1+bVO5bPIzA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/@csstools/postcss-logical-overscroll-behavior": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-logical-overscroll-behavior/-/postcss-logical-overscroll-behavior-2.0.0.tgz",
+ "integrity": "sha512-e/webMjoGOSYfqLunyzByZj5KKe5oyVg/YSbie99VEaSDE2kimFm0q1f6t/6Jo+VVCQ/jbe2Xy+uX+C4xzWs4w==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/@csstools/postcss-logical-resize": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-logical-resize/-/postcss-logical-resize-3.0.0.tgz",
+ "integrity": "sha512-DFbHQOFW/+I+MY4Ycd/QN6Dg4Hcbb50elIJCfnwkRTCX05G11SwViI5BbBlg9iHRl4ytB7pmY5ieAFk3ws7yyg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/@csstools/postcss-logical-viewport-units": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-logical-viewport-units/-/postcss-logical-viewport-units-3.0.4.tgz",
+ "integrity": "sha512-q+eHV1haXA4w9xBwZLKjVKAWn3W2CMqmpNpZUk5kRprvSiBEGMgrNH3/sJZ8UA3JgyHaOt3jwT9uFa4wLX4EqQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "@csstools/css-tokenizer": "^3.0.4",
+ "@csstools/utilities": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/@csstools/postcss-media-minmax": {
+ "version": "2.0.9",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-media-minmax/-/postcss-media-minmax-2.0.9.tgz",
+ "integrity": "sha512-af9Qw3uS3JhYLnCbqtZ9crTvvkR+0Se+bBqSr7ykAnl9yKhk6895z9rf+2F4dClIDJWxgn0iZZ1PSdkhrbs2ig==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "@csstools/css-calc": "^2.1.4",
+ "@csstools/css-parser-algorithms": "^3.0.5",
+ "@csstools/css-tokenizer": "^3.0.4",
+ "@csstools/media-query-list-parser": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/@csstools/postcss-media-queries-aspect-ratio-number-values": {
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-media-queries-aspect-ratio-number-values/-/postcss-media-queries-aspect-ratio-number-values-3.0.5.tgz",
+ "integrity": "sha512-zhAe31xaaXOY2Px8IYfoVTB3wglbJUVigGphFLj6exb7cjZRH9A6adyE22XfFK3P2PzwRk0VDeTJmaxpluyrDg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "@csstools/css-parser-algorithms": "^3.0.5",
+ "@csstools/css-tokenizer": "^3.0.4",
+ "@csstools/media-query-list-parser": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/@csstools/postcss-nested-calc": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-nested-calc/-/postcss-nested-calc-4.0.0.tgz",
+ "integrity": "sha512-jMYDdqrQQxE7k9+KjstC3NbsmC063n1FTPLCgCRS2/qHUbHM0mNy9pIn4QIiQGs9I/Bg98vMqw7mJXBxa0N88A==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "@csstools/utilities": "^2.0.0",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/@csstools/postcss-normalize-display-values": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-normalize-display-values/-/postcss-normalize-display-values-4.0.0.tgz",
+ "integrity": "sha512-HlEoG0IDRoHXzXnkV4in47dzsxdsjdz6+j7MLjaACABX2NfvjFS6XVAnpaDyGesz9gK2SC7MbNwdCHusObKJ9Q==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/@csstools/postcss-oklab-function": {
+ "version": "4.0.12",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-oklab-function/-/postcss-oklab-function-4.0.12.tgz",
+ "integrity": "sha512-HhlSmnE1NKBhXsTnNGjxvhryKtO7tJd1w42DKOGFD6jSHtYOrsJTQDKPMwvOfrzUAk8t7GcpIfRyM7ssqHpFjg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "@csstools/css-color-parser": "^3.1.0",
+ "@csstools/css-parser-algorithms": "^3.0.5",
+ "@csstools/css-tokenizer": "^3.0.4",
+ "@csstools/postcss-progressive-custom-properties": "^4.2.1",
+ "@csstools/utilities": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/@csstools/postcss-progressive-custom-properties": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-progressive-custom-properties/-/postcss-progressive-custom-properties-4.2.1.tgz",
+ "integrity": "sha512-uPiiXf7IEKtUQXsxu6uWtOlRMXd2QWWy5fhxHDnPdXKCQckPP3E34ZgDoZ62r2iT+UOgWsSbM4NvHE5m3mAEdw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/@csstools/postcss-random-function": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-random-function/-/postcss-random-function-2.0.1.tgz",
+ "integrity": "sha512-q+FQaNiRBhnoSNo+GzqGOIBKoHQ43lYz0ICrV+UudfWnEF6ksS6DsBIJSISKQT2Bvu3g4k6r7t0zYrk5pDlo8w==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "@csstools/css-calc": "^2.1.4",
+ "@csstools/css-parser-algorithms": "^3.0.5",
+ "@csstools/css-tokenizer": "^3.0.4"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/@csstools/postcss-relative-color-syntax": {
+ "version": "3.0.12",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-relative-color-syntax/-/postcss-relative-color-syntax-3.0.12.tgz",
+ "integrity": "sha512-0RLIeONxu/mtxRtf3o41Lq2ghLimw0w9ByLWnnEVuy89exmEEq8bynveBxNW3nyHqLAFEeNtVEmC1QK9MZ8Huw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "@csstools/css-color-parser": "^3.1.0",
+ "@csstools/css-parser-algorithms": "^3.0.5",
+ "@csstools/css-tokenizer": "^3.0.4",
+ "@csstools/postcss-progressive-custom-properties": "^4.2.1",
+ "@csstools/utilities": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/@csstools/postcss-scope-pseudo-class": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-scope-pseudo-class/-/postcss-scope-pseudo-class-4.0.1.tgz",
+ "integrity": "sha512-IMi9FwtH6LMNuLea1bjVMQAsUhFxJnyLSgOp/cpv5hrzWmrUYU5fm0EguNDIIOHUqzXode8F/1qkC/tEo/qN8Q==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "postcss-selector-parser": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/@csstools/postcss-sign-functions": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-sign-functions/-/postcss-sign-functions-1.1.4.tgz",
+ "integrity": "sha512-P97h1XqRPcfcJndFdG95Gv/6ZzxUBBISem0IDqPZ7WMvc/wlO+yU0c5D/OCpZ5TJoTt63Ok3knGk64N+o6L2Pg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "@csstools/css-calc": "^2.1.4",
+ "@csstools/css-parser-algorithms": "^3.0.5",
+ "@csstools/css-tokenizer": "^3.0.4"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/@csstools/postcss-stepped-value-functions": {
+ "version": "4.0.9",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-stepped-value-functions/-/postcss-stepped-value-functions-4.0.9.tgz",
+ "integrity": "sha512-h9btycWrsex4dNLeQfyU3y3w40LMQooJWFMm/SK9lrKguHDcFl4VMkncKKoXi2z5rM9YGWbUQABI8BT2UydIcA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "@csstools/css-calc": "^2.1.4",
+ "@csstools/css-parser-algorithms": "^3.0.5",
+ "@csstools/css-tokenizer": "^3.0.4"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/@csstools/postcss-text-decoration-shorthand": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-text-decoration-shorthand/-/postcss-text-decoration-shorthand-4.0.3.tgz",
+ "integrity": "sha512-KSkGgZfx0kQjRIYnpsD7X2Om9BUXX/Kii77VBifQW9Ih929hK0KNjVngHDH0bFB9GmfWcR9vJYJJRvw/NQjkrA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "@csstools/color-helpers": "^5.1.0",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/@csstools/postcss-trigonometric-functions": {
+ "version": "4.0.9",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-trigonometric-functions/-/postcss-trigonometric-functions-4.0.9.tgz",
+ "integrity": "sha512-Hnh5zJUdpNrJqK9v1/E3BbrQhaDTj5YiX7P61TOvUhoDHnUmsNNxcDAgkQ32RrcWx9GVUvfUNPcUkn8R3vIX6A==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "@csstools/css-calc": "^2.1.4",
+ "@csstools/css-parser-algorithms": "^3.0.5",
+ "@csstools/css-tokenizer": "^3.0.4"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/@csstools/postcss-unset-value": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-unset-value/-/postcss-unset-value-4.0.0.tgz",
+ "integrity": "sha512-cBz3tOCI5Fw6NIFEwU3RiwK6mn3nKegjpJuzCndoGq3BZPkUjnsq7uQmIeMNeMbMk7YD2MfKcgCpZwX5jyXqCA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/@csstools/selector-resolve-nested": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@csstools/selector-resolve-nested/-/selector-resolve-nested-3.1.0.tgz",
+ "integrity": "sha512-mf1LEW0tJLKfWyvn5KdDrhpxHyuxpbNwTIwOYLIvsTffeyOf85j5oIzfG0yosxDgx/sswlqBnESYUcQH0vgZ0g==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss-selector-parser": "^7.0.0"
+ }
+ },
+ "node_modules/@csstools/selector-specificity": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-5.0.0.tgz",
+ "integrity": "sha512-PCqQV3c4CoVm3kdPhyeZ07VmBRdH2EpMFA/pd9OASpOEC3aXNGoqPDAZ80D0cLpMBxnmk0+yNhGsEx31hq7Gtw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss-selector-parser": "^7.0.0"
+ }
+ },
+ "node_modules/@csstools/utilities": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@csstools/utilities/-/utilities-2.0.0.tgz",
+ "integrity": "sha512-5VdOr0Z71u+Yp3ozOx8T11N703wIFGVRgOWbOZMKgglPJsWA54MRIoMNVMa7shUToIhx5J8vX4sOZgD2XiihiQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/@dabh/diagnostics": {
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.7.tgz",
+ "integrity": "sha512-EnXa4T9/wuLw8iaEUFwIJMo0xNxyE3mzxLL14ixfVToJNQHG2Jwo8xMmafVJwAiAmIyHXsFDZpZR/TJC31857g==",
+ "license": "MIT",
+ "dependencies": {
+ "@so-ric/colorspace": "^1.1.6",
+ "enabled": "2.0.x",
+ "fix-esm": "^1.0.1",
+ "kuler": "^2.0.0"
+ }
+ },
"node_modules/@emnapi/core": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.5.0.tgz",
@@ -176,6 +1766,19 @@
"url": "https://opencollective.com/eslint"
}
},
+ "node_modules/@eslint/eslintrc/node_modules/globals": {
+ "version": "14.0.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
+ "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/@eslint/js": {
"version": "9.36.0",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.36.0.tgz",
@@ -213,6 +1816,100 @@
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
+ "node_modules/@floating-ui/core": {
+ "version": "1.7.3",
+ "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.3.tgz",
+ "integrity": "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==",
+ "license": "MIT",
+ "dependencies": {
+ "@floating-ui/utils": "^0.2.10"
+ }
+ },
+ "node_modules/@floating-ui/dom": {
+ "version": "1.7.4",
+ "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.4.tgz",
+ "integrity": "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==",
+ "license": "MIT",
+ "dependencies": {
+ "@floating-ui/core": "^1.7.3",
+ "@floating-ui/utils": "^0.2.10"
+ }
+ },
+ "node_modules/@floating-ui/react": {
+ "version": "0.26.28",
+ "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.26.28.tgz",
+ "integrity": "sha512-yORQuuAtVpiRjpMhdc0wJj06b9JFjrYF4qp96j++v2NBpbi6SEGF7donUJ3TMieerQ6qVkAv1tgr7L4r5roTqw==",
+ "license": "MIT",
+ "dependencies": {
+ "@floating-ui/react-dom": "^2.1.2",
+ "@floating-ui/utils": "^0.2.8",
+ "tabbable": "^6.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.8.0",
+ "react-dom": ">=16.8.0"
+ }
+ },
+ "node_modules/@floating-ui/react-dom": {
+ "version": "2.1.6",
+ "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.6.tgz",
+ "integrity": "sha512-4JX6rEatQEvlmgU80wZyq9RT96HZJa88q8hp0pBd+LrczeDI4o6uA2M+uvxngVHo4Ihr8uibXxH6+70zhAFrVw==",
+ "license": "MIT",
+ "dependencies": {
+ "@floating-ui/dom": "^1.7.4"
+ },
+ "peerDependencies": {
+ "react": ">=16.8.0",
+ "react-dom": ">=16.8.0"
+ }
+ },
+ "node_modules/@floating-ui/utils": {
+ "version": "0.2.10",
+ "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz",
+ "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==",
+ "license": "MIT"
+ },
+ "node_modules/@headlessui/react": {
+ "version": "2.2.9",
+ "resolved": "https://registry.npmjs.org/@headlessui/react/-/react-2.2.9.tgz",
+ "integrity": "sha512-Mb+Un58gwBn0/yWZfyrCh0TJyurtT+dETj7YHleylHk5od3dv2XqETPGWMyQ5/7sYN7oWdyM1u9MvC0OC8UmzQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@floating-ui/react": "^0.26.16",
+ "@react-aria/focus": "^3.20.2",
+ "@react-aria/interactions": "^3.25.0",
+ "@tanstack/react-virtual": "^3.13.9",
+ "use-sync-external-store": "^1.5.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "react": "^18 || ^19 || ^19.0.0-rc",
+ "react-dom": "^18 || ^19 || ^19.0.0-rc"
+ }
+ },
+ "node_modules/@heroicons/react": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/@heroicons/react/-/react-2.2.0.tgz",
+ "integrity": "sha512-LMcepvRaS9LYHJGsF0zzmgKCUim/X3N/DQKc4jepAXJ7l8QxJ1PmxJzqplF2Z3FE4PqBAIGyJAQ/w4B5dsqbtQ==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": ">= 16 || ^19.0.0-rc"
+ }
+ },
+ "node_modules/@hookform/resolvers": {
+ "version": "5.2.2",
+ "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-5.2.2.tgz",
+ "integrity": "sha512-A/IxlMLShx3KjV/HeTcTfaMxdwy690+L/ZADoeaTltLx+CVuzkeVIPuybK3jrRfw7YZnmdKsVVHAlEPIAEUNlA==",
+ "license": "MIT",
+ "dependencies": {
+ "@standard-schema/utils": "^0.3.0"
+ },
+ "peerDependencies": {
+ "react-hook-form": "^7.55.0"
+ }
+ },
"node_modules/@humanfs/core": {
"version": "0.19.1",
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
@@ -710,7 +2407,6 @@
"version": "0.3.13",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
"integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.5.0",
@@ -721,7 +2417,6 @@
"version": "2.3.5",
"resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
"integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/gen-mapping": "^0.3.5",
@@ -732,7 +2427,6 @@
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">=6.0.0"
@@ -742,20 +2436,215 @@
"version": "1.5.5",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
- "dev": true,
"license": "MIT"
},
"node_modules/@jridgewell/trace-mapping": {
"version": "0.3.31",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
"integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/resolve-uri": "^3.1.0",
"@jridgewell/sourcemap-codec": "^1.4.14"
}
},
+ "node_modules/@lottiefiles/react-lottie-player": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/@lottiefiles/react-lottie-player/-/react-lottie-player-3.6.0.tgz",
+ "integrity": "sha512-WK5TriLJT93VF3w4IjSVyveiedraZCnDhKzCPhpbeLgQeMi6zufxa3dXNc4HmAFRXq+LULPAy+Idv1rAfkReMA==",
+ "license": "MIT",
+ "dependencies": {
+ "lottie-web": "^5.12.2"
+ },
+ "peerDependencies": {
+ "react": "16 - 19"
+ }
+ },
+ "node_modules/@napi-rs/canvas": {
+ "version": "0.1.80",
+ "resolved": "https://registry.npmjs.org/@napi-rs/canvas/-/canvas-0.1.80.tgz",
+ "integrity": "sha512-DxuT1ClnIPts1kQx8FBmkk4BQDTfI5kIzywAaMjQSXfNnra5UFU9PwurXrl+Je3bJ6BGsp/zmshVVFbCmyI+ww==",
+ "license": "MIT",
+ "optional": true,
+ "workspaces": [
+ "e2e/*"
+ ],
+ "engines": {
+ "node": ">= 10"
+ },
+ "optionalDependencies": {
+ "@napi-rs/canvas-android-arm64": "0.1.80",
+ "@napi-rs/canvas-darwin-arm64": "0.1.80",
+ "@napi-rs/canvas-darwin-x64": "0.1.80",
+ "@napi-rs/canvas-linux-arm-gnueabihf": "0.1.80",
+ "@napi-rs/canvas-linux-arm64-gnu": "0.1.80",
+ "@napi-rs/canvas-linux-arm64-musl": "0.1.80",
+ "@napi-rs/canvas-linux-riscv64-gnu": "0.1.80",
+ "@napi-rs/canvas-linux-x64-gnu": "0.1.80",
+ "@napi-rs/canvas-linux-x64-musl": "0.1.80",
+ "@napi-rs/canvas-win32-x64-msvc": "0.1.80"
+ }
+ },
+ "node_modules/@napi-rs/canvas-android-arm64": {
+ "version": "0.1.80",
+ "resolved": "https://registry.npmjs.org/@napi-rs/canvas-android-arm64/-/canvas-android-arm64-0.1.80.tgz",
+ "integrity": "sha512-sk7xhN/MoXeuExlggf91pNziBxLPVUqF2CAVnB57KLG/pz7+U5TKG8eXdc3pm0d7Od0WreB6ZKLj37sX9muGOQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@napi-rs/canvas-darwin-arm64": {
+ "version": "0.1.80",
+ "resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-arm64/-/canvas-darwin-arm64-0.1.80.tgz",
+ "integrity": "sha512-O64APRTXRUiAz0P8gErkfEr3lipLJgM6pjATwavZ22ebhjYl/SUbpgM0xcWPQBNMP1n29afAC/Us5PX1vg+JNQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@napi-rs/canvas-darwin-x64": {
+ "version": "0.1.80",
+ "resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-x64/-/canvas-darwin-x64-0.1.80.tgz",
+ "integrity": "sha512-FqqSU7qFce0Cp3pwnTjVkKjjOtxMqRe6lmINxpIZYaZNnVI0H5FtsaraZJ36SiTHNjZlUB69/HhxNDT1Aaa9vA==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@napi-rs/canvas-linux-arm-gnueabihf": {
+ "version": "0.1.80",
+ "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm-gnueabihf/-/canvas-linux-arm-gnueabihf-0.1.80.tgz",
+ "integrity": "sha512-eyWz0ddBDQc7/JbAtY4OtZ5SpK8tR4JsCYEZjCE3dI8pqoWUC8oMwYSBGCYfsx2w47cQgQCgMVRVTFiiO38hHQ==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@napi-rs/canvas-linux-arm64-gnu": {
+ "version": "0.1.80",
+ "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-gnu/-/canvas-linux-arm64-gnu-0.1.80.tgz",
+ "integrity": "sha512-qwA63t8A86bnxhuA/GwOkK3jvb+XTQaTiVML0vAWoHyoZYTjNs7BzoOONDgTnNtr8/yHrq64XXzUoLqDzU+Uuw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@napi-rs/canvas-linux-arm64-musl": {
+ "version": "0.1.80",
+ "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-musl/-/canvas-linux-arm64-musl-0.1.80.tgz",
+ "integrity": "sha512-1XbCOz/ymhj24lFaIXtWnwv/6eFHXDrjP0jYkc6iHQ9q8oXKzUX1Lc6bu+wuGiLhGh2GS/2JlfORC5ZcXimRcg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@napi-rs/canvas-linux-riscv64-gnu": {
+ "version": "0.1.80",
+ "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-riscv64-gnu/-/canvas-linux-riscv64-gnu-0.1.80.tgz",
+ "integrity": "sha512-XTzR125w5ZMs0lJcxRlS1K3P5RaZ9RmUsPtd1uGt+EfDyYMu4c6SEROYsxyatbbu/2+lPe7MPHOO/0a0x7L/gw==",
+ "cpu": [
+ "riscv64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@napi-rs/canvas-linux-x64-gnu": {
+ "version": "0.1.80",
+ "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-gnu/-/canvas-linux-x64-gnu-0.1.80.tgz",
+ "integrity": "sha512-BeXAmhKg1kX3UCrJsYbdQd3hIMDH/K6HnP/pG2LuITaXhXBiNdh//TVVVVCBbJzVQaV5gK/4ZOCMrQW9mvuTqA==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@napi-rs/canvas-linux-x64-musl": {
+ "version": "0.1.80",
+ "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-musl/-/canvas-linux-x64-musl-0.1.80.tgz",
+ "integrity": "sha512-x0XvZWdHbkgdgucJsRxprX/4o4sEed7qo9rCQA9ugiS9qE2QvP0RIiEugtZhfLH3cyI+jIRFJHV4Fuz+1BHHMg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@napi-rs/canvas-win32-x64-msvc": {
+ "version": "0.1.80",
+ "resolved": "https://registry.npmjs.org/@napi-rs/canvas-win32-x64-msvc/-/canvas-win32-x64-msvc-0.1.80.tgz",
+ "integrity": "sha512-Z8jPsM6df5V8B1HrCHB05+bDiCxjE9QA//3YrkKIdVDEwn5RKaqOxCJDRJkl48cJbylcrJbW4HxZbTte8juuPg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
"node_modules/@napi-rs/wasm-runtime": {
"version": "0.2.12",
"resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz",
@@ -961,6 +2850,277 @@
"node": ">=12.4.0"
}
},
+ "node_modules/@react-aria/focus": {
+ "version": "3.21.1",
+ "resolved": "https://registry.npmjs.org/@react-aria/focus/-/focus-3.21.1.tgz",
+ "integrity": "sha512-hmH1IhHlcQ2lSIxmki1biWzMbGgnhdxJUM0MFfzc71Rv6YAzhlx4kX3GYn4VNcjCeb6cdPv4RZ5vunV4kgMZYQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/interactions": "^3.25.5",
+ "@react-aria/utils": "^3.30.1",
+ "@react-types/shared": "^3.32.0",
+ "@swc/helpers": "^0.5.0",
+ "clsx": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-aria/interactions": {
+ "version": "3.25.5",
+ "resolved": "https://registry.npmjs.org/@react-aria/interactions/-/interactions-3.25.5.tgz",
+ "integrity": "sha512-EweYHOEvMwef/wsiEqV73KurX/OqnmbzKQa2fLxdULbec5+yDj6wVGaRHIzM4NiijIDe+bldEl5DG05CAKOAHA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/ssr": "^3.9.10",
+ "@react-aria/utils": "^3.30.1",
+ "@react-stately/flags": "^3.1.2",
+ "@react-types/shared": "^3.32.0",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-aria/ssr": {
+ "version": "3.9.10",
+ "resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.9.10.tgz",
+ "integrity": "sha512-hvTm77Pf+pMBhuBm760Li0BVIO38jv1IBws1xFm1NoL26PU+fe+FMW5+VZWyANR6nYL65joaJKZqOdTQMkO9IQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@swc/helpers": "^0.5.0"
+ },
+ "engines": {
+ "node": ">= 12"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-aria/utils": {
+ "version": "3.30.1",
+ "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.30.1.tgz",
+ "integrity": "sha512-zETcbDd6Vf9GbLndO6RiWJadIZsBU2MMm23rBACXLmpRztkrIqPEb2RVdlLaq1+GklDx0Ii6PfveVjx+8S5U6A==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/ssr": "^3.9.10",
+ "@react-stately/flags": "^3.1.2",
+ "@react-stately/utils": "^3.10.8",
+ "@react-types/shared": "^3.32.0",
+ "@swc/helpers": "^0.5.0",
+ "clsx": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-pdf/fns": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@react-pdf/fns/-/fns-3.1.2.tgz",
+ "integrity": "sha512-qTKGUf0iAMGg2+OsUcp9ffKnKi41RukM/zYIWMDJ4hRVYSr89Q7e3wSDW/Koqx3ea3Uy/z3h2y3wPX6Bdfxk6g==",
+ "license": "MIT"
+ },
+ "node_modules/@react-pdf/font": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/@react-pdf/font/-/font-4.0.3.tgz",
+ "integrity": "sha512-N1qQDZr6phXYQOp033Hvm2nkUkx2LkszjGPbmRavs9VOYzi4sp31MaccMKptL24ii6UhBh/z9yPUhnuNe/qHwA==",
+ "license": "MIT",
+ "dependencies": {
+ "@react-pdf/pdfkit": "^4.0.4",
+ "@react-pdf/types": "^2.9.1",
+ "fontkit": "^2.0.2",
+ "is-url": "^1.2.4"
+ }
+ },
+ "node_modules/@react-pdf/image": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/@react-pdf/image/-/image-3.0.3.tgz",
+ "integrity": "sha512-lvP5ryzYM3wpbO9bvqLZYwEr5XBDX9jcaRICvtnoRqdJOo7PRrMnmB4MMScyb+Xw10mGeIubZAAomNAG5ONQZQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@react-pdf/png-js": "^3.0.0",
+ "jay-peg": "^1.1.1"
+ }
+ },
+ "node_modules/@react-pdf/layout": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/@react-pdf/layout/-/layout-4.4.1.tgz",
+ "integrity": "sha512-GVzdlWoZWldRDzlWj3SttRXmVDxg7YfraAohwy+o9gb9hrbDJaaAV6jV3pc630Evd3K46OAzk8EFu8EgPDuVuA==",
+ "license": "MIT",
+ "dependencies": {
+ "@react-pdf/fns": "3.1.2",
+ "@react-pdf/image": "^3.0.3",
+ "@react-pdf/primitives": "^4.1.1",
+ "@react-pdf/stylesheet": "^6.1.1",
+ "@react-pdf/textkit": "^6.0.0",
+ "@react-pdf/types": "^2.9.1",
+ "emoji-regex-xs": "^1.0.0",
+ "queue": "^6.0.1",
+ "yoga-layout": "^3.2.1"
+ }
+ },
+ "node_modules/@react-pdf/pdfkit": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/@react-pdf/pdfkit/-/pdfkit-4.0.4.tgz",
+ "integrity": "sha512-/nITLggsPlB66bVLnm0X7MNdKQxXelLGZG6zB5acF5cCgkFwmXHnLNyxYOUD4GMOMg1HOPShXDKWrwk2ZeHsvw==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.20.13",
+ "@react-pdf/png-js": "^3.0.0",
+ "browserify-zlib": "^0.2.0",
+ "crypto-js": "^4.2.0",
+ "fontkit": "^2.0.2",
+ "jay-peg": "^1.1.1",
+ "linebreak": "^1.1.0",
+ "vite-compatible-readable-stream": "^3.6.1"
+ }
+ },
+ "node_modules/@react-pdf/png-js": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/@react-pdf/png-js/-/png-js-3.0.0.tgz",
+ "integrity": "sha512-eSJnEItZ37WPt6Qv5pncQDxLJRK15eaRwPT+gZoujP548CodenOVp49GST8XJvKMFt9YqIBzGBV/j9AgrOQzVA==",
+ "license": "MIT",
+ "dependencies": {
+ "browserify-zlib": "^0.2.0"
+ }
+ },
+ "node_modules/@react-pdf/primitives": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/@react-pdf/primitives/-/primitives-4.1.1.tgz",
+ "integrity": "sha512-IuhxYls1luJb7NUWy6q5avb1XrNaVj9bTNI40U9qGRuS6n7Hje/8H8Qi99Z9UKFV74bBP3DOf3L1wV2qZVgVrQ==",
+ "license": "MIT"
+ },
+ "node_modules/@react-pdf/reconciler": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/@react-pdf/reconciler/-/reconciler-1.1.4.tgz",
+ "integrity": "sha512-oTQDiR/t4Z/Guxac88IavpU2UgN7eR0RMI9DRKvKnvPz2DUasGjXfChAdMqDNmJJxxV26mMy9xQOUV2UU5/okg==",
+ "license": "MIT",
+ "dependencies": {
+ "object-assign": "^4.1.1",
+ "scheduler": "0.25.0-rc-603e6108-20241029"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/@react-pdf/reconciler/node_modules/scheduler": {
+ "version": "0.25.0-rc-603e6108-20241029",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.25.0-rc-603e6108-20241029.tgz",
+ "integrity": "sha512-pFwF6H1XrSdYYNLfOcGlM28/j8CGLu8IvdrxqhjWULe2bPcKiKW4CV+OWqR/9fT52mywx65l7ysNkjLKBda7eA==",
+ "license": "MIT"
+ },
+ "node_modules/@react-pdf/render": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/@react-pdf/render/-/render-4.3.1.tgz",
+ "integrity": "sha512-v1WAaAhQShQZGcBxfjkEThGCHVH9CSuitrZ1bIOLvB5iBKM14abYK5D6djKhWCwF6FTzYeT2WRjRMVgze/ND2A==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.20.13",
+ "@react-pdf/fns": "3.1.2",
+ "@react-pdf/primitives": "^4.1.1",
+ "@react-pdf/textkit": "^6.0.0",
+ "@react-pdf/types": "^2.9.1",
+ "abs-svg-path": "^0.1.1",
+ "color-string": "^1.9.1",
+ "normalize-svg-path": "^1.1.0",
+ "parse-svg-path": "^0.1.2",
+ "svg-arc-to-cubic-bezier": "^3.2.0"
+ }
+ },
+ "node_modules/@react-pdf/renderer": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/@react-pdf/renderer/-/renderer-4.3.1.tgz",
+ "integrity": "sha512-dPKHiwGTaOsKqNWCHPYYrx8CDfAGsUnV4tvRsEu0VPGxuot1AOq/M+YgfN/Pb+MeXCTe2/lv6NvA8haUtj3tsA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.20.13",
+ "@react-pdf/fns": "3.1.2",
+ "@react-pdf/font": "^4.0.3",
+ "@react-pdf/layout": "^4.4.1",
+ "@react-pdf/pdfkit": "^4.0.4",
+ "@react-pdf/primitives": "^4.1.1",
+ "@react-pdf/reconciler": "^1.1.4",
+ "@react-pdf/render": "^4.3.1",
+ "@react-pdf/types": "^2.9.1",
+ "events": "^3.3.0",
+ "object-assign": "^4.1.1",
+ "prop-types": "^15.6.2",
+ "queue": "^6.0.1"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/@react-pdf/stylesheet": {
+ "version": "6.1.1",
+ "resolved": "https://registry.npmjs.org/@react-pdf/stylesheet/-/stylesheet-6.1.1.tgz",
+ "integrity": "sha512-Iyw0A3wRIeQLN4EkaKf8yF9MvdMxiZ8JjoyzLzDHSxnKYoOA4UGu84veCb8dT9N8MxY5x7a0BUv/avTe586Plg==",
+ "license": "MIT",
+ "dependencies": {
+ "@react-pdf/fns": "3.1.2",
+ "@react-pdf/types": "^2.9.1",
+ "color-string": "^1.9.1",
+ "hsl-to-hex": "^1.0.0",
+ "media-engine": "^1.0.3",
+ "postcss-value-parser": "^4.1.0"
+ }
+ },
+ "node_modules/@react-pdf/textkit": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/@react-pdf/textkit/-/textkit-6.0.0.tgz",
+ "integrity": "sha512-fDt19KWaJRK/n2AaFoVm31hgGmpygmTV7LsHGJNGZkgzXcFyLsx+XUl63DTDPH3iqxj3xUX128t104GtOz8tTw==",
+ "license": "MIT",
+ "dependencies": {
+ "@react-pdf/fns": "3.1.2",
+ "bidi-js": "^1.0.2",
+ "hyphen": "^1.6.4",
+ "unicode-properties": "^1.4.1"
+ }
+ },
+ "node_modules/@react-pdf/types": {
+ "version": "2.9.1",
+ "resolved": "https://registry.npmjs.org/@react-pdf/types/-/types-2.9.1.tgz",
+ "integrity": "sha512-5GoCgG0G5NMgpPuHbKG2xcVRQt7+E5pg3IyzVIIozKG3nLcnsXW4zy25vG1ZBQA0jmo39q34au/sOnL/0d1A4w==",
+ "license": "MIT",
+ "dependencies": {
+ "@react-pdf/font": "^4.0.3",
+ "@react-pdf/primitives": "^4.1.1",
+ "@react-pdf/stylesheet": "^6.1.1"
+ }
+ },
+ "node_modules/@react-stately/flags": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@react-stately/flags/-/flags-3.1.2.tgz",
+ "integrity": "sha512-2HjFcZx1MyQXoPqcBGALwWWmgFVUk2TuKVIQxCbRq7fPyWXIl6VHcakCLurdtYC2Iks7zizvz0Idv48MQ38DWg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@swc/helpers": "^0.5.0"
+ }
+ },
+ "node_modules/@react-stately/utils": {
+ "version": "3.10.8",
+ "resolved": "https://registry.npmjs.org/@react-stately/utils/-/utils-3.10.8.tgz",
+ "integrity": "sha512-SN3/h7SzRsusVQjQ4v10LaVsDc81jyyR0DD5HnsQitm/I5WDpaSr2nRHtyloPFU48jlql1XX/S04T2DLQM7Y3g==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-types/shared": {
+ "version": "3.32.0",
+ "resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.32.0.tgz",
+ "integrity": "sha512-t+cligIJsZYFMSPFMvsJMjzlzde06tZMOIOFa1OV5Z0BcMowrb2g4mB57j/9nP28iJIRYn10xCniQts+qadrqQ==",
+ "license": "Apache-2.0",
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
"node_modules/@rtsao/scc": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz",
@@ -975,6 +3135,22 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/@so-ric/colorspace": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/@so-ric/colorspace/-/colorspace-1.1.6.tgz",
+ "integrity": "sha512-/KiKkpHNOBgkFJwu9sh48LkHSMYGyuTcSFK/qMBdnOAlrRJzRSXAOFB5qwzaVQuDl8wAvHVMkaASQDReTahxuw==",
+ "license": "MIT",
+ "dependencies": {
+ "color": "^5.0.2",
+ "text-hex": "1.0.x"
+ }
+ },
+ "node_modules/@standard-schema/utils": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz",
+ "integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==",
+ "license": "MIT"
+ },
"node_modules/@swc/helpers": {
"version": "0.5.15",
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz",
@@ -984,6 +3160,18 @@
"tslib": "^2.8.0"
}
},
+ "node_modules/@tailwindcss/forms": {
+ "version": "0.5.10",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.10.tgz",
+ "integrity": "sha512-utI1ONF6uf/pPNO68kmN1b8rEwNXv3czukalo8VtJH8ksIkZXr3Q3VYudZLkCsDd4Wku120uF02hYK25XGPorw==",
+ "license": "MIT",
+ "dependencies": {
+ "mini-svg-data-uri": "^1.2.3"
+ },
+ "peerDependencies": {
+ "tailwindcss": ">=3.0.0 || >= 3.0.0-alpha.1 || >= 4.0.0-alpha.20 || >= 4.0.0-beta.1"
+ }
+ },
"node_modules/@tailwindcss/node": {
"version": "4.1.13",
"resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.13.tgz",
@@ -1260,6 +3448,76 @@
"tailwindcss": "4.1.13"
}
},
+ "node_modules/@tailwindcss/typography": {
+ "version": "0.5.19",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.19.tgz",
+ "integrity": "sha512-w31dd8HOx3k9vPtcQh5QHP9GwKcgbMp87j58qi6xgiBnFFtKEAgCWnDw4qUT8aHwkCp8bKvb/KGKWWHedP0AAg==",
+ "license": "MIT",
+ "dependencies": {
+ "postcss-selector-parser": "6.0.10"
+ },
+ "peerDependencies": {
+ "tailwindcss": ">=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1"
+ }
+ },
+ "node_modules/@tailwindcss/typography/node_modules/postcss-selector-parser": {
+ "version": "6.0.10",
+ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz",
+ "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==",
+ "license": "MIT",
+ "dependencies": {
+ "cssesc": "^3.0.0",
+ "util-deprecate": "^1.0.2"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/@tailwindplus/elements": {
+ "version": "1.0.15",
+ "resolved": "https://registry.npmjs.org/@tailwindplus/elements/-/elements-1.0.15.tgz",
+ "integrity": "sha512-fFpo4G+HOqDm94pOPxXZi3V6I360FzXXvOUtx2QABKAqKKSWrzSwQfLBaFmNo7k1IGzTG+mygCWDkZm6GreBlg==",
+ "license": "SEE LICENSE IN LICENSE.md"
+ },
+ "node_modules/@tailwindui/react": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/@tailwindui/react/-/react-0.1.1.tgz",
+ "integrity": "sha512-2VoUokHT/EYHaWQjH49gwKqYtwii7snb1SIp93ewfuI9fSy4x6DYocs4v4WNfyiE971ixc/PT+cs6tCi+zabSA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "react": ">=16"
+ }
+ },
+ "node_modules/@tanstack/react-virtual": {
+ "version": "3.13.12",
+ "resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.13.12.tgz",
+ "integrity": "sha512-Gd13QdxPSukP8ZrkbgS2RwoZseTTbQPLnQEn7HY/rqtM+8Zt95f7xKC7N0EsKs7aoz0WzZ+fditZux+F8EzYxA==",
+ "license": "MIT",
+ "dependencies": {
+ "@tanstack/virtual-core": "3.13.12"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/tannerlinsley"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/@tanstack/virtual-core": {
+ "version": "3.13.12",
+ "resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.13.12.tgz",
+ "integrity": "sha512-1YBOJfRHV4sXUmWsFSf5rQor4Ss82G8dQWLRbnk3GA4jeP8hQt1hxXh0tmflpC0dz3VgEv/1+qwPyLeWkQuPFA==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/tannerlinsley"
+ }
+ },
"node_modules/@tybys/wasm-util": {
"version": "0.10.1",
"resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz",
@@ -1306,7 +3564,7 @@
"version": "19.1.15",
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.15.tgz",
"integrity": "sha512-+kLxJpaJzXybyDyFXYADyP1cznTO8HSuBpenGlnKOAkH4hyNINiywvXS/tGJhsrGGP/gM185RA3xpjY0Yg4erA==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"dependencies": {
"csstype": "^3.0.2"
@@ -1322,6 +3580,12 @@
"@types/react": "^19.0.0"
}
},
+ "node_modules/@types/triple-beam": {
+ "version": "1.3.5",
+ "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz",
+ "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==",
+ "license": "MIT"
+ },
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "8.44.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.44.1.tgz",
@@ -1879,6 +4143,12 @@
"win32"
]
},
+ "node_modules/abs-svg-path": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/abs-svg-path/-/abs-svg-path-0.1.1.tgz",
+ "integrity": "sha512-d8XPSGjfyzlXC3Xx891DJRyZfqk5JU0BJrDQcsWomFIV1/BIzPW5HDH5iDdWpqWaav0YVIEzT1RHTwWr0FFshA==",
+ "license": "MIT"
+ },
"node_modules/acorn": {
"version": "8.15.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
@@ -2119,6 +4389,12 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/async": {
+ "version": "3.2.6",
+ "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz",
+ "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==",
+ "license": "MIT"
+ },
"node_modules/async-function": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz",
@@ -2129,6 +4405,12 @@
"node": ">= 0.4"
}
},
+ "node_modules/asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
+ "license": "MIT"
+ },
"node_modules/autoprefixer": {
"version": "10.4.21",
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.21.tgz",
@@ -2193,6 +4475,17 @@
"node": ">=4"
}
},
+ "node_modules/axios": {
+ "version": "1.12.2",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.12.2.tgz",
+ "integrity": "sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==",
+ "license": "MIT",
+ "dependencies": {
+ "follow-redirects": "^1.15.6",
+ "form-data": "^4.0.4",
+ "proxy-from-env": "^1.1.0"
+ }
+ },
"node_modules/axobject-query": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz",
@@ -2210,16 +4503,44 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/base64-js": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
+ "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
"node_modules/baseline-browser-mapping": {
"version": "2.8.9",
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.9.tgz",
"integrity": "sha512-hY/u2lxLrbecMEWSB0IpGzGyDyeoMFQhCvZd2jGFSE5I17Fh01sYUBPCJtkWERw7zrac9+cIghxm/ytJa2X8iA==",
- "dev": true,
"license": "Apache-2.0",
"bin": {
"baseline-browser-mapping": "dist/cli.js"
}
},
+ "node_modules/bidi-js": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz",
+ "integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==",
+ "license": "MIT",
+ "dependencies": {
+ "require-from-string": "^2.0.2"
+ }
+ },
"node_modules/brace-expansion": {
"version": "1.1.12",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
@@ -2244,11 +4565,28 @@
"node": ">=8"
}
},
+ "node_modules/brotli": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/brotli/-/brotli-1.3.3.tgz",
+ "integrity": "sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==",
+ "license": "MIT",
+ "dependencies": {
+ "base64-js": "^1.1.2"
+ }
+ },
+ "node_modules/browserify-zlib": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz",
+ "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==",
+ "license": "MIT",
+ "dependencies": {
+ "pako": "~1.0.5"
+ }
+ },
"node_modules/browserslist": {
"version": "4.26.2",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.26.2.tgz",
"integrity": "sha512-ECFzp6uFOSB+dcZ5BK/IBaGWssbSYBHvuMeMt3MMFyhI0Z8SqGgEkBLARgpRH3hutIgPVsALcMwbDrJqPxQ65A==",
- "dev": true,
"funding": [
{
"type": "opencollective",
@@ -2301,7 +4639,6 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
- "dev": true,
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
@@ -2385,12 +4722,49 @@
"node": ">=18"
}
},
+ "node_modules/classnames": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz",
+ "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==",
+ "license": "MIT"
+ },
"node_modules/client-only": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz",
"integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==",
"license": "MIT"
},
+ "node_modules/clone": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz",
+ "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
+ "node_modules/clsx": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
+ "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/color": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/color/-/color-5.0.2.tgz",
+ "integrity": "sha512-e2hz5BzbUPcYlIRHo8ieAhYgoajrJr+hWoceg6E345TPsATMUKqDgzt8fSXZJJbxfpiPzkWyphz8yn8At7q3fA==",
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^3.0.1",
+ "color-string": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
"node_modules/color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
@@ -2408,9 +4782,63 @@
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
- "dev": true,
"license": "MIT"
},
+ "node_modules/color-string": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz",
+ "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==",
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "^1.0.0",
+ "simple-swizzle": "^0.2.2"
+ }
+ },
+ "node_modules/color/node_modules/color-convert": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-3.1.2.tgz",
+ "integrity": "sha512-UNqkvCDXstVck3kdowtOTWROIJQwafjOfXSmddoDrXo4cewMKmusCeF22Q24zvjR8nwWib/3S/dfyzPItPEiJg==",
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=14.6"
+ }
+ },
+ "node_modules/color/node_modules/color-name": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.0.2.tgz",
+ "integrity": "sha512-9vEt7gE16EW7Eu7pvZnR0abW9z6ufzhXxGXZEVU9IqPdlsUiMwJeJfRtq0zePUmnbHGT9zajca7mX8zgoayo4A==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.20"
+ }
+ },
+ "node_modules/color/node_modules/color-string": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/color-string/-/color-string-2.1.2.tgz",
+ "integrity": "sha512-RxmjYxbWemV9gKu4zPgiZagUxbH3RQpEIO77XoSSX0ivgABDZ+h8Zuash/EMFLTI4N9QgFPOJ6JQpPZKFxa+dA==",
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/combined-stream": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+ "license": "MIT",
+ "dependencies": {
+ "delayed-stream": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@@ -2418,6 +4846,24 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/convert-source-map": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
+ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
+ "license": "MIT"
+ },
+ "node_modules/country-flag-icons": {
+ "version": "1.5.21",
+ "resolved": "https://registry.npmjs.org/country-flag-icons/-/country-flag-icons-1.5.21.tgz",
+ "integrity": "sha512-0KmU4oeiyAM+F+atzK99ghQDQJKxEY3tiDhnRraVFL4o65rZgrmrx7xKi0b+hxcVpcEpuUbu+KCC6TKTZQTDcA==",
+ "license": "MIT"
+ },
+ "node_modules/country-select-js": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/country-select-js/-/country-select-js-2.1.0.tgz",
+ "integrity": "sha512-T7gM2MT6S06lGqqkkBCmWFlyryKuaBgbeJFFxZttT+GT6pwl63r5KuLQszkfbtL9YEu+8JvrRayfvyrZd9I++g==",
+ "license": "MIT"
+ },
"node_modules/cross-spawn": {
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
@@ -2433,11 +4879,122 @@
"node": ">= 8"
}
},
+ "node_modules/crypto-js": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz",
+ "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==",
+ "license": "MIT"
+ },
+ "node_modules/css-blank-pseudo": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/css-blank-pseudo/-/css-blank-pseudo-7.0.1.tgz",
+ "integrity": "sha512-jf+twWGDf6LDoXDUode+nc7ZlrqfaNphrBIBrcmeP3D8yw1uPaix1gCC8LUQUGQ6CycuK2opkbFFWFuq/a94ag==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "postcss-selector-parser": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/css-has-pseudo": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/css-has-pseudo/-/css-has-pseudo-7.0.3.tgz",
+ "integrity": "sha512-oG+vKuGyqe/xvEMoxAQrhi7uY16deJR3i7wwhBerVrGQKSqUC5GiOVxTpM9F9B9hw0J+eKeOWLH7E9gZ1Dr5rA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "@csstools/selector-specificity": "^5.0.0",
+ "postcss-selector-parser": "^7.0.0",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/css-prefers-color-scheme": {
+ "version": "10.0.0",
+ "resolved": "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-10.0.0.tgz",
+ "integrity": "sha512-VCtXZAWivRglTZditUfB4StnsWr6YVZ2PRtuxQLKTNRdtAf8tpzaVPE9zXIF3VaSc7O70iK/j1+NXxyQCqdPjQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/cssdb": {
+ "version": "8.4.2",
+ "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-8.4.2.tgz",
+ "integrity": "sha512-PzjkRkRUS+IHDJohtxkIczlxPPZqRo0nXplsYXOMBRPjcVRjj1W4DfvRgshUYTVuUigU7ptVYkFJQ7abUB0nyg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ }
+ ],
+ "license": "MIT-0"
+ },
+ "node_modules/cssesc": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
+ "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
+ "license": "MIT",
+ "bin": {
+ "cssesc": "bin/cssesc"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
"node_modules/csstype": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
- "dev": true,
"license": "MIT"
},
"node_modules/damerau-levenshtein": {
@@ -2505,7 +5062,6 @@
"version": "4.4.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
- "dev": true,
"license": "MIT",
"dependencies": {
"ms": "^2.1.3"
@@ -2562,6 +5118,24 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/dequal": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
+ "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/detect-libc": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.1.tgz",
@@ -2572,6 +5146,12 @@
"node": ">=8"
}
},
+ "node_modules/dfa": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/dfa/-/dfa-1.2.0.tgz",
+ "integrity": "sha512-ED3jP8saaweFTjeGX8HQPjeC1YYyZs98jGNZx6IiBvxW7JG5v492kamAQB3m2wop07CvU/RQmzcKr6bgcC5D/Q==",
+ "license": "MIT"
+ },
"node_modules/doctrine": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
@@ -2589,7 +5169,6 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
- "dev": true,
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.1",
@@ -2604,7 +5183,6 @@
"version": "1.5.227",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.227.tgz",
"integrity": "sha512-ITxuoPfJu3lsNWUi2lBM2PaBPYgH3uqmxut5vmBxgYvyI4AlJ6P3Cai1O76mOrkJCBzq0IxWg/NtqOrpu/0gKA==",
- "dev": true,
"license": "ISC"
},
"node_modules/emoji-regex": {
@@ -2614,6 +5192,18 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/emoji-regex-xs": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex-xs/-/emoji-regex-xs-1.0.0.tgz",
+ "integrity": "sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg==",
+ "license": "MIT"
+ },
+ "node_modules/enabled": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz",
+ "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==",
+ "license": "MIT"
+ },
"node_modules/enhanced-resolve": {
"version": "5.18.3",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz",
@@ -2701,7 +5291,6 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
@@ -2711,7 +5300,6 @@
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
@@ -2749,7 +5337,6 @@
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
- "dev": true,
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0"
@@ -2762,7 +5349,6 @@
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
"integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
- "dev": true,
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
@@ -2809,7 +5395,6 @@
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
"integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
@@ -3254,11 +5839,19 @@
"node": ">=0.10.0"
}
},
+ "node_modules/events": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
+ "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8.x"
+ }
+ },
"node_modules/fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
- "dev": true,
"license": "MIT"
},
"node_modules/fast-glob": {
@@ -3315,6 +5908,12 @@
"reusify": "^1.0.4"
}
},
+ "node_modules/fecha": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz",
+ "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==",
+ "license": "MIT"
+ },
"node_modules/file-entry-cache": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
@@ -3358,6 +5957,17 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/fix-esm": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/fix-esm/-/fix-esm-1.0.1.tgz",
+ "integrity": "sha512-EZtb7wPXZS54GaGxaWxMlhd1DUDCnAg5srlYdu/1ZVeW+7wwR3Tp59nu52dXByFs3MBRq+SByx1wDOJpRvLEXw==",
+ "license": "WTFPL OR CC0-1.0",
+ "dependencies": {
+ "@babel/core": "^7.14.6",
+ "@babel/plugin-proposal-export-namespace-from": "^7.14.5",
+ "@babel/plugin-transform-modules-commonjs": "^7.14.5"
+ }
+ },
"node_modules/flat-cache": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
@@ -3379,6 +5989,49 @@
"dev": true,
"license": "ISC"
},
+ "node_modules/fn.name": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz",
+ "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==",
+ "license": "MIT"
+ },
+ "node_modules/follow-redirects": {
+ "version": "1.15.11",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz",
+ "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/RubenVerborgh"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=4.0"
+ },
+ "peerDependenciesMeta": {
+ "debug": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/fontkit": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/fontkit/-/fontkit-2.0.4.tgz",
+ "integrity": "sha512-syetQadaUEDNdxdugga9CpEYVaQIxOwk7GlwZWWZ19//qW4zE5bknOKeMBDYAASwnpaSHKJITRLMF9m1fp3s6g==",
+ "license": "MIT",
+ "dependencies": {
+ "@swc/helpers": "^0.5.12",
+ "brotli": "^1.3.2",
+ "clone": "^2.1.2",
+ "dfa": "^1.2.0",
+ "fast-deep-equal": "^3.1.3",
+ "restructure": "^3.0.0",
+ "tiny-inflate": "^1.0.3",
+ "unicode-properties": "^1.4.0",
+ "unicode-trie": "^2.0.0"
+ }
+ },
"node_modules/for-each": {
"version": "0.3.5",
"resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz",
@@ -3395,6 +6048,22 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/form-data": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz",
+ "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==",
+ "license": "MIT",
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "es-set-tostringtag": "^2.1.0",
+ "hasown": "^2.0.2",
+ "mime-types": "^2.1.12"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
"node_modules/fraction.js": {
"version": "4.3.7",
"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz",
@@ -3409,11 +6078,37 @@
"url": "https://github.com/sponsors/rawify"
}
},
+ "node_modules/framer-motion": {
+ "version": "12.23.22",
+ "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.23.22.tgz",
+ "integrity": "sha512-ZgGvdxXCw55ZYvhoZChTlG6pUuehecgvEAJz0BHoC5pQKW1EC5xf1Mul1ej5+ai+pVY0pylyFfdl45qnM1/GsA==",
+ "license": "MIT",
+ "dependencies": {
+ "motion-dom": "^12.23.21",
+ "motion-utils": "^12.23.6",
+ "tslib": "^2.4.0"
+ },
+ "peerDependencies": {
+ "@emotion/is-prop-valid": "*",
+ "react": "^18.0.0 || ^19.0.0",
+ "react-dom": "^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@emotion/is-prop-valid": {
+ "optional": true
+ },
+ "react": {
+ "optional": true
+ },
+ "react-dom": {
+ "optional": true
+ }
+ }
+ },
"node_modules/function-bind": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
- "dev": true,
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/ljharb"
@@ -3450,11 +6145,19 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/gensync": {
+ "version": "1.0.0-beta.2",
+ "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
+ "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
"node_modules/get-intrinsic": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
- "dev": true,
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.2",
@@ -3479,7 +6182,6 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
- "dev": true,
"license": "MIT",
"dependencies": {
"dunder-proto": "^1.0.1",
@@ -3534,9 +6236,9 @@
}
},
"node_modules/globals": {
- "version": "14.0.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
- "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
+ "version": "16.4.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-16.4.0.tgz",
+ "integrity": "sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==",
"dev": true,
"license": "MIT",
"engines": {
@@ -3563,11 +6265,19 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/goober": {
+ "version": "2.1.16",
+ "resolved": "https://registry.npmjs.org/goober/-/goober-2.1.16.tgz",
+ "integrity": "sha512-erjk19y1U33+XAMe1VTvIONHYoSqE4iS7BYUZfHaqeohLmnC0FdxEh7rQU+6MZ4OajItzjZFSRtVANrQwNq6/g==",
+ "license": "MIT",
+ "peerDependencies": {
+ "csstype": "^3.0.10"
+ }
+ },
"node_modules/gopd": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
@@ -3646,7 +6356,6 @@
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
@@ -3659,7 +6368,6 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
"integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
- "dev": true,
"license": "MIT",
"dependencies": {
"has-symbols": "^1.0.3"
@@ -3675,7 +6383,6 @@
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
- "dev": true,
"license": "MIT",
"dependencies": {
"function-bind": "^1.1.2"
@@ -3684,6 +6391,27 @@
"node": ">= 0.4"
}
},
+ "node_modules/hsl-to-hex": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/hsl-to-hex/-/hsl-to-hex-1.0.0.tgz",
+ "integrity": "sha512-K6GVpucS5wFf44X0h2bLVRDsycgJmf9FF2elg+CrqD8GcFU8c6vYhgXn8NjUkFCwj+xDFb70qgLbTUm6sxwPmA==",
+ "license": "MIT",
+ "dependencies": {
+ "hsl-to-rgb-for-reals": "^1.1.0"
+ }
+ },
+ "node_modules/hsl-to-rgb-for-reals": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/hsl-to-rgb-for-reals/-/hsl-to-rgb-for-reals-1.1.1.tgz",
+ "integrity": "sha512-LgOWAkrN0rFaQpfdWBQlv/VhkOxb5AsBjk6NQVx4yEzWS923T07X0M1Y0VNko2H52HeSpZrZNNMJ0aFqsdVzQg==",
+ "license": "ISC"
+ },
+ "node_modules/hyphen": {
+ "version": "1.10.6",
+ "resolved": "https://registry.npmjs.org/hyphen/-/hyphen-1.10.6.tgz",
+ "integrity": "sha512-fXHXcGFTXOvZTSkPJuGOQf5Lv5T/R2itiiCVPg9LxAje5D00O0pP83yJShFq5V89Ly//Gt6acj7z8pbBr34stw==",
+ "license": "ISC"
+ },
"node_modules/ignore": {
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
@@ -3721,6 +6449,33 @@
"node": ">=0.8.19"
}
},
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "license": "ISC"
+ },
+ "node_modules/input-format": {
+ "version": "0.3.14",
+ "resolved": "https://registry.npmjs.org/input-format/-/input-format-0.3.14.tgz",
+ "integrity": "sha512-gHMrgrbCgmT4uK5Um5eVDUohuV9lcs95ZUUN9Px2Y0VIfjTzT2wF8Q3Z4fwLFm7c5Z2OXCm53FHoovj6SlOKdg==",
+ "license": "MIT",
+ "dependencies": {
+ "prop-types": "^15.8.1"
+ },
+ "peerDependencies": {
+ "react": ">=18.1.0",
+ "react-dom": ">=18.1.0"
+ },
+ "peerDependenciesMeta": {
+ "react": {
+ "optional": true
+ },
+ "react-dom": {
+ "optional": true
+ }
+ }
+ },
"node_modules/internal-slot": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz",
@@ -3736,6 +6491,12 @@
"node": ">= 0.4"
}
},
+ "node_modules/intl-tel-input": {
+ "version": "25.11.2",
+ "resolved": "https://registry.npmjs.org/intl-tel-input/-/intl-tel-input-25.11.2.tgz",
+ "integrity": "sha512-3a9+bbtR6s7E8TjZauqodMz+MRMd31OcUhTJuQOg95lA+viZ53OTU8XzVuyldEE089nMtLhPF1NbRU1ff2Sf7g==",
+ "license": "MIT"
+ },
"node_modules/is-array-buffer": {
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz",
@@ -3754,6 +6515,12 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/is-arrayish": {
+ "version": "0.3.4",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.4.tgz",
+ "integrity": "sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==",
+ "license": "MIT"
+ },
"node_modules/is-async-function": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz",
@@ -4040,6 +6807,18 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/is-stream": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
+ "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/is-string": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz",
@@ -4091,6 +6870,12 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/is-url": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz",
+ "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==",
+ "license": "MIT"
+ },
"node_modules/is-weakmap": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz",
@@ -4169,6 +6954,15 @@
"node": ">= 0.4"
}
},
+ "node_modules/jay-peg": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/jay-peg/-/jay-peg-1.1.1.tgz",
+ "integrity": "sha512-D62KEuBxz/ip2gQKOEhk/mx14o7eiFRaU+VNNSP4MOiIkwb/D6B3G1Mfas7C/Fit8EsSV2/IWjZElx/Gs6A4ww==",
+ "license": "MIT",
+ "dependencies": {
+ "restructure": "^3.0.0"
+ }
+ },
"node_modules/jiti": {
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.0.tgz",
@@ -4183,7 +6977,6 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
- "dev": true,
"license": "MIT"
},
"node_modules/js-yaml": {
@@ -4199,6 +6992,18 @@
"js-yaml": "bin/js-yaml.js"
}
},
+ "node_modules/jsesc": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
+ "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
+ "license": "MIT",
+ "bin": {
+ "jsesc": "bin/jsesc"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/json-buffer": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
@@ -4259,6 +7064,12 @@
"json-buffer": "3.0.1"
}
},
+ "node_modules/kuler": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz",
+ "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==",
+ "license": "MIT"
+ },
"node_modules/language-subtag-registry": {
"version": "0.3.23",
"resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz",
@@ -4293,6 +7104,12 @@
"node": ">= 0.8.0"
}
},
+ "node_modules/libphonenumber-js": {
+ "version": "1.12.23",
+ "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.12.23.tgz",
+ "integrity": "sha512-RN3q3gImZ91BvRDYjWp7ICz3gRn81mW5L4SW+2afzNCC0I/nkXstBgZThQGTE3S/9q5J90FH4dP+TXx8NhdZKg==",
+ "license": "MIT"
+ },
"node_modules/lightningcss": {
"version": "1.30.1",
"resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.1.tgz",
@@ -4532,6 +7349,25 @@
"url": "https://opencollective.com/parcel"
}
},
+ "node_modules/linebreak": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/linebreak/-/linebreak-1.1.0.tgz",
+ "integrity": "sha512-MHp03UImeVhB7XZtjd0E4n6+3xr5Dq/9xI/5FptGk5FrbDR3zagPa2DS6U8ks/3HjbKWG9Q1M2ufOzxV2qLYSQ==",
+ "license": "MIT",
+ "dependencies": {
+ "base64-js": "0.0.8",
+ "unicode-trie": "^2.0.0"
+ }
+ },
+ "node_modules/linebreak/node_modules/base64-js": {
+ "version": "0.0.8",
+ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-0.0.8.tgz",
+ "integrity": "sha512-3XSA2cR/h/73EzlXXdU6YNycmYI7+kicTxks4eJg2g39biHR84slg2+des+p7iHYhbRg/udIS4TD53WabcOUkw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/locate-path": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
@@ -4555,11 +7391,27 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/logform": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/logform/-/logform-2.7.0.tgz",
+ "integrity": "sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@colors/colors": "1.6.0",
+ "@types/triple-beam": "^1.3.2",
+ "fecha": "^4.2.0",
+ "ms": "^2.1.1",
+ "safe-stable-stringify": "^2.3.1",
+ "triple-beam": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ }
+ },
"node_modules/loose-envify": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
- "dev": true,
"license": "MIT",
"dependencies": {
"js-tokens": "^3.0.0 || ^4.0.0"
@@ -4568,6 +7420,27 @@
"loose-envify": "cli.js"
}
},
+ "node_modules/lottie-web": {
+ "version": "5.13.0",
+ "resolved": "https://registry.npmjs.org/lottie-web/-/lottie-web-5.13.0.tgz",
+ "integrity": "sha512-+gfBXl6sxXMPe8tKQm7qzLnUy5DUPJPKIyRHwtpCpyUEYjHYRJC/5gjUvdkuO2c3JllrPtHXH5UJJK8LRYl5yQ==",
+ "license": "MIT"
+ },
+ "node_modules/lru-cache": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+ "license": "ISC",
+ "dependencies": {
+ "yallist": "^3.0.2"
+ }
+ },
+ "node_modules/lru-cache/node_modules/yallist": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+ "license": "ISC"
+ },
"node_modules/magic-string": {
"version": "0.30.19",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.19.tgz",
@@ -4578,16 +7451,56 @@
"@jridgewell/sourcemap-codec": "^1.5.5"
}
},
+ "node_modules/make-cancellable-promise": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/make-cancellable-promise/-/make-cancellable-promise-2.0.0.tgz",
+ "integrity": "sha512-3SEQqTpV9oqVsIWqAcmDuaNeo7yBO3tqPtqGRcKkEo0lrzD3wqbKG9mkxO65KoOgXqj+zH2phJ2LiAsdzlogSw==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/wojtekmaj/make-cancellable-promise?sponsor=1"
+ }
+ },
+ "node_modules/make-event-props": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/make-event-props/-/make-event-props-2.0.0.tgz",
+ "integrity": "sha512-G/hncXrl4Qt7mauJEXSg3AcdYzmpkIITTNl5I+rH9sog5Yw0kK6vseJjCaPfOXqOqQuPUP89Rkhfz5kPS8ijtw==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/wojtekmaj/make-event-props?sponsor=1"
+ }
+ },
"node_modules/math-intrinsics": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
+ "node_modules/media-engine": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/media-engine/-/media-engine-1.0.3.tgz",
+ "integrity": "sha512-aa5tG6sDoK+k70B9iEX1NeyfT8ObCKhNDs6lJVpwF6r8vhUfuKMslIcirq6HIUYuuUYLefcEQOn9bSBOvawtwg==",
+ "license": "MIT"
+ },
+ "node_modules/merge-refs": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/merge-refs/-/merge-refs-2.0.0.tgz",
+ "integrity": "sha512-3+B21mYK2IqUWnd2EivABLT7ueDhb0b8/dGK8LoFQPrU61YITeCMn14F7y7qZafWNZhUEKb24cJdiT5Wxs3prg==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/wojtekmaj/merge-refs?sponsor=1"
+ },
+ "peerDependencies": {
+ "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
"node_modules/merge2": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
@@ -4612,6 +7525,36 @@
"node": ">=8.6"
}
},
+ "node_modules/mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "license": "MIT",
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mini-svg-data-uri": {
+ "version": "1.4.4",
+ "resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz",
+ "integrity": "sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==",
+ "license": "MIT",
+ "bin": {
+ "mini-svg-data-uri": "cli.js"
+ }
+ },
"node_modules/minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
@@ -4658,11 +7601,51 @@
"node": ">= 18"
}
},
+ "node_modules/motion": {
+ "version": "12.23.22",
+ "resolved": "https://registry.npmjs.org/motion/-/motion-12.23.22.tgz",
+ "integrity": "sha512-iSq6X9vLHbeYwmHvhK//+U74ROaPnZmBuy60XZzqNl0QtZkWfoZyMDHYnpKuWFv0sNMqHgED8aCXk94LCoQPGg==",
+ "license": "MIT",
+ "dependencies": {
+ "framer-motion": "^12.23.22",
+ "tslib": "^2.4.0"
+ },
+ "peerDependencies": {
+ "@emotion/is-prop-valid": "*",
+ "react": "^18.0.0 || ^19.0.0",
+ "react-dom": "^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@emotion/is-prop-valid": {
+ "optional": true
+ },
+ "react": {
+ "optional": true
+ },
+ "react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/motion-dom": {
+ "version": "12.23.21",
+ "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.23.21.tgz",
+ "integrity": "sha512-5xDXx/AbhrfgsQmSE7YESMn4Dpo6x5/DTZ4Iyy4xqDvVHWvFVoV+V2Ri2S/ksx+D40wrZ7gPYiMWshkdoqNgNQ==",
+ "license": "MIT",
+ "dependencies": {
+ "motion-utils": "^12.23.6"
+ }
+ },
+ "node_modules/motion-utils": {
+ "version": "12.23.6",
+ "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.23.6.tgz",
+ "integrity": "sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ==",
+ "license": "MIT"
+ },
"node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
- "dev": true,
"license": "MIT"
},
"node_modules/nanoid": {
@@ -4790,7 +7773,6 @@
"version": "2.0.21",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.21.tgz",
"integrity": "sha512-5b0pgg78U3hwXkCM8Z9b2FJdPZlr9Psr9V2gQPESdGHqbntyFJKFW4r5TeWGFzafGY3hzs1JC62VEQMbl1JFkw==",
- "dev": true,
"license": "MIT"
},
"node_modules/normalize-range": {
@@ -4803,11 +7785,19 @@
"node": ">=0.10.0"
}
},
+ "node_modules/normalize-svg-path": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/normalize-svg-path/-/normalize-svg-path-1.1.0.tgz",
+ "integrity": "sha512-r9KHKG2UUeB5LoTouwDzBy2VxXlHsiM6fyLQvnJa0S5hrhzqElH/CH7TUGhT1fVvIYBIKf3OpY4YJ4CK+iaqHg==",
+ "license": "MIT",
+ "dependencies": {
+ "svg-arc-to-cubic-bezier": "^3.0.0"
+ }
+ },
"node_modules/object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
@@ -4926,6 +7916,15 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/one-time": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz",
+ "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==",
+ "license": "MIT",
+ "dependencies": {
+ "fn.name": "1.x.x"
+ }
+ },
"node_modules/optionator": {
"version": "0.9.4",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
@@ -4994,6 +7993,12 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/pako": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
+ "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
+ "license": "(MIT AND Zlib)"
+ },
"node_modules/parent-module": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
@@ -5007,6 +8012,12 @@
"node": ">=6"
}
},
+ "node_modules/parse-svg-path": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/parse-svg-path/-/parse-svg-path-0.1.2.tgz",
+ "integrity": "sha512-JyPSBnkTJ0AI8GGJLfMXvKq42cj5c006fnLz6fXy6zfoVjJizi8BNTpu8on8ziI1cKy9d9DGNuY17Ce7wuejpQ==",
+ "license": "MIT"
+ },
"node_modules/path-exists": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
@@ -5034,6 +8045,18 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/pdfjs-dist": {
+ "version": "5.4.149",
+ "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-5.4.149.tgz",
+ "integrity": "sha512-Xe8/1FMJEQPUVSti25AlDpwpUm2QAVmNOpFP0SIahaPIOKBKICaefbzogLdwey3XGGoaP4Lb9wqiw2e9Jqp0LA==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=20.16.0 || >=22.3.0"
+ },
+ "optionalDependencies": {
+ "@napi-rs/canvas": "^0.1.77"
+ }
+ },
"node_modules/picocolors": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
@@ -5092,11 +8115,727 @@
"node": "^10 || ^12 || >=14"
}
},
+ "node_modules/postcss-attribute-case-insensitive": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-7.0.1.tgz",
+ "integrity": "sha512-Uai+SupNSqzlschRyNx3kbCTWgY/2hcwtHEI/ej2LJWc9JJ77qKgGptd8DHwY1mXtZ7Aoh4z4yxfwMBue9eNgw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "postcss-selector-parser": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/postcss-clamp": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/postcss-clamp/-/postcss-clamp-4.1.0.tgz",
+ "integrity": "sha512-ry4b1Llo/9zz+PKC+030KUnPITTJAHeOwjfAyyB60eT0AorGLdzp52s31OsPRHRf8NchkgFoG2y6fCfn1IV1Ow==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": ">=7.6.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.6"
+ }
+ },
+ "node_modules/postcss-color-functional-notation": {
+ "version": "7.0.12",
+ "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-7.0.12.tgz",
+ "integrity": "sha512-TLCW9fN5kvO/u38/uesdpbx3e8AkTYhMvDZYa9JpmImWuTE99bDQ7GU7hdOADIZsiI9/zuxfAJxny/khknp1Zw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "@csstools/css-color-parser": "^3.1.0",
+ "@csstools/css-parser-algorithms": "^3.0.5",
+ "@csstools/css-tokenizer": "^3.0.4",
+ "@csstools/postcss-progressive-custom-properties": "^4.2.1",
+ "@csstools/utilities": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/postcss-color-hex-alpha": {
+ "version": "10.0.0",
+ "resolved": "https://registry.npmjs.org/postcss-color-hex-alpha/-/postcss-color-hex-alpha-10.0.0.tgz",
+ "integrity": "sha512-1kervM2cnlgPs2a8Vt/Qbe5cQ++N7rkYo/2rz2BkqJZIHQwaVuJgQH38REHrAi4uM0b1fqxMkWYmese94iMp3w==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "@csstools/utilities": "^2.0.0",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/postcss-color-rebeccapurple": {
+ "version": "10.0.0",
+ "resolved": "https://registry.npmjs.org/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-10.0.0.tgz",
+ "integrity": "sha512-JFta737jSP+hdAIEhk1Vs0q0YF5P8fFcj+09pweS8ktuGuZ8pPlykHsk6mPxZ8awDl4TrcxUqJo9l1IhVr/OjQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "@csstools/utilities": "^2.0.0",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/postcss-custom-media": {
+ "version": "11.0.6",
+ "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-11.0.6.tgz",
+ "integrity": "sha512-C4lD4b7mUIw+RZhtY7qUbf4eADmb7Ey8BFA2px9jUbwg7pjTZDl4KY4bvlUV+/vXQvzQRfiGEVJyAbtOsCMInw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "@csstools/cascade-layer-name-parser": "^2.0.5",
+ "@csstools/css-parser-algorithms": "^3.0.5",
+ "@csstools/css-tokenizer": "^3.0.4",
+ "@csstools/media-query-list-parser": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/postcss-custom-properties": {
+ "version": "14.0.6",
+ "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-14.0.6.tgz",
+ "integrity": "sha512-fTYSp3xuk4BUeVhxCSJdIPhDLpJfNakZKoiTDx7yRGCdlZrSJR7mWKVOBS4sBF+5poPQFMj2YdXx1VHItBGihQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "@csstools/cascade-layer-name-parser": "^2.0.5",
+ "@csstools/css-parser-algorithms": "^3.0.5",
+ "@csstools/css-tokenizer": "^3.0.4",
+ "@csstools/utilities": "^2.0.0",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/postcss-custom-selectors": {
+ "version": "8.0.5",
+ "resolved": "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-8.0.5.tgz",
+ "integrity": "sha512-9PGmckHQswiB2usSO6XMSswO2yFWVoCAuih1yl9FVcwkscLjRKjwsjM3t+NIWpSU2Jx3eOiK2+t4vVTQaoCHHg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "@csstools/cascade-layer-name-parser": "^2.0.5",
+ "@csstools/css-parser-algorithms": "^3.0.5",
+ "@csstools/css-tokenizer": "^3.0.4",
+ "postcss-selector-parser": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/postcss-dir-pseudo-class": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-9.0.1.tgz",
+ "integrity": "sha512-tRBEK0MHYvcMUrAuYMEOa0zg9APqirBcgzi6P21OhxtJyJADo/SWBwY1CAwEohQ/6HDaa9jCjLRG7K3PVQYHEA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "postcss-selector-parser": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/postcss-double-position-gradients": {
+ "version": "6.0.4",
+ "resolved": "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-6.0.4.tgz",
+ "integrity": "sha512-m6IKmxo7FxSP5nF2l63QbCC3r+bWpFUWmZXZf096WxG0m7Vl1Q1+ruFOhpdDRmKrRS+S3Jtk+TVk/7z0+BVK6g==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "@csstools/postcss-progressive-custom-properties": "^4.2.1",
+ "@csstools/utilities": "^2.0.0",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/postcss-focus-visible": {
+ "version": "10.0.1",
+ "resolved": "https://registry.npmjs.org/postcss-focus-visible/-/postcss-focus-visible-10.0.1.tgz",
+ "integrity": "sha512-U58wyjS/I1GZgjRok33aE8juW9qQgQUNwTSdxQGuShHzwuYdcklnvK/+qOWX1Q9kr7ysbraQ6ht6r+udansalA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "postcss-selector-parser": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/postcss-focus-within": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/postcss-focus-within/-/postcss-focus-within-9.0.1.tgz",
+ "integrity": "sha512-fzNUyS1yOYa7mOjpci/bR+u+ESvdar6hk8XNK/TRR0fiGTp2QT5N+ducP0n3rfH/m9I7H/EQU6lsa2BrgxkEjw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "postcss-selector-parser": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/postcss-font-variant": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/postcss-font-variant/-/postcss-font-variant-5.0.0.tgz",
+ "integrity": "sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "postcss": "^8.1.0"
+ }
+ },
+ "node_modules/postcss-gap-properties": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-6.0.0.tgz",
+ "integrity": "sha512-Om0WPjEwiM9Ru+VhfEDPZJAKWUd0mV1HmNXqp2C29z80aQ2uP9UVhLc7e3aYMIor/S5cVhoPgYQ7RtfeZpYTRw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/postcss-image-set-function": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/postcss-image-set-function/-/postcss-image-set-function-7.0.0.tgz",
+ "integrity": "sha512-QL7W7QNlZuzOwBTeXEmbVckNt1FSmhQtbMRvGGqqU4Nf4xk6KUEQhAoWuMzwbSv5jxiRiSZ5Tv7eiDB9U87znA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "@csstools/utilities": "^2.0.0",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/postcss-lab-function": {
+ "version": "7.0.12",
+ "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-7.0.12.tgz",
+ "integrity": "sha512-tUcyRk1ZTPec3OuKFsqtRzW2Go5lehW29XA21lZ65XmzQkz43VY2tyWEC202F7W3mILOjw0voOiuxRGTsN+J9w==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "@csstools/css-color-parser": "^3.1.0",
+ "@csstools/css-parser-algorithms": "^3.0.5",
+ "@csstools/css-tokenizer": "^3.0.4",
+ "@csstools/postcss-progressive-custom-properties": "^4.2.1",
+ "@csstools/utilities": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/postcss-logical": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-8.1.0.tgz",
+ "integrity": "sha512-pL1hXFQ2fEXNKiNiAgtfA005T9FBxky5zkX6s4GZM2D8RkVgRqz3f4g1JUoq925zXv495qk8UNldDwh8uGEDoA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/postcss-nesting": {
+ "version": "13.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-13.0.2.tgz",
+ "integrity": "sha512-1YCI290TX+VP0U/K/aFxzHzQWHWURL+CtHMSbex1lCdpXD1SoR2sYuxDu5aNI9lPoXpKTCggFZiDJbwylU0LEQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "@csstools/selector-resolve-nested": "^3.1.0",
+ "@csstools/selector-specificity": "^5.0.0",
+ "postcss-selector-parser": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/postcss-opacity-percentage": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/postcss-opacity-percentage/-/postcss-opacity-percentage-3.0.0.tgz",
+ "integrity": "sha512-K6HGVzyxUxd/VgZdX04DCtdwWJ4NGLG212US4/LA1TLAbHgmAsTWVR86o+gGIbFtnTkfOpb9sCRBx8K7HO66qQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "kofi",
+ "url": "https://ko-fi.com/mrcgrtz"
+ },
+ {
+ "type": "liberapay",
+ "url": "https://liberapay.com/mrcgrtz"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/postcss-overflow-shorthand": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/postcss-overflow-shorthand/-/postcss-overflow-shorthand-6.0.0.tgz",
+ "integrity": "sha512-BdDl/AbVkDjoTofzDQnwDdm/Ym6oS9KgmO7Gr+LHYjNWJ6ExORe4+3pcLQsLA9gIROMkiGVjjwZNoL/mpXHd5Q==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/postcss-page-break": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-3.0.4.tgz",
+ "integrity": "sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "postcss": "^8"
+ }
+ },
+ "node_modules/postcss-place": {
+ "version": "10.0.0",
+ "resolved": "https://registry.npmjs.org/postcss-place/-/postcss-place-10.0.0.tgz",
+ "integrity": "sha512-5EBrMzat2pPAxQNWYavwAfoKfYcTADJ8AXGVPcUZ2UkNloUTWzJQExgrzrDkh3EKzmAx1evfTAzF9I8NGcc+qw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/postcss-preset-env": {
+ "version": "10.4.0",
+ "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-10.4.0.tgz",
+ "integrity": "sha512-2kqpOthQ6JhxqQq1FSAAZGe9COQv75Aw8WbsOvQVNJ2nSevc9Yx/IKZGuZ7XJ+iOTtVon7LfO7ELRzg8AZ+sdw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "@csstools/postcss-alpha-function": "^1.0.1",
+ "@csstools/postcss-cascade-layers": "^5.0.2",
+ "@csstools/postcss-color-function": "^4.0.12",
+ "@csstools/postcss-color-function-display-p3-linear": "^1.0.1",
+ "@csstools/postcss-color-mix-function": "^3.0.12",
+ "@csstools/postcss-color-mix-variadic-function-arguments": "^1.0.2",
+ "@csstools/postcss-content-alt-text": "^2.0.8",
+ "@csstools/postcss-contrast-color-function": "^2.0.12",
+ "@csstools/postcss-exponential-functions": "^2.0.9",
+ "@csstools/postcss-font-format-keywords": "^4.0.0",
+ "@csstools/postcss-gamut-mapping": "^2.0.11",
+ "@csstools/postcss-gradients-interpolation-method": "^5.0.12",
+ "@csstools/postcss-hwb-function": "^4.0.12",
+ "@csstools/postcss-ic-unit": "^4.0.4",
+ "@csstools/postcss-initial": "^2.0.1",
+ "@csstools/postcss-is-pseudo-class": "^5.0.3",
+ "@csstools/postcss-light-dark-function": "^2.0.11",
+ "@csstools/postcss-logical-float-and-clear": "^3.0.0",
+ "@csstools/postcss-logical-overflow": "^2.0.0",
+ "@csstools/postcss-logical-overscroll-behavior": "^2.0.0",
+ "@csstools/postcss-logical-resize": "^3.0.0",
+ "@csstools/postcss-logical-viewport-units": "^3.0.4",
+ "@csstools/postcss-media-minmax": "^2.0.9",
+ "@csstools/postcss-media-queries-aspect-ratio-number-values": "^3.0.5",
+ "@csstools/postcss-nested-calc": "^4.0.0",
+ "@csstools/postcss-normalize-display-values": "^4.0.0",
+ "@csstools/postcss-oklab-function": "^4.0.12",
+ "@csstools/postcss-progressive-custom-properties": "^4.2.1",
+ "@csstools/postcss-random-function": "^2.0.1",
+ "@csstools/postcss-relative-color-syntax": "^3.0.12",
+ "@csstools/postcss-scope-pseudo-class": "^4.0.1",
+ "@csstools/postcss-sign-functions": "^1.1.4",
+ "@csstools/postcss-stepped-value-functions": "^4.0.9",
+ "@csstools/postcss-text-decoration-shorthand": "^4.0.3",
+ "@csstools/postcss-trigonometric-functions": "^4.0.9",
+ "@csstools/postcss-unset-value": "^4.0.0",
+ "autoprefixer": "^10.4.21",
+ "browserslist": "^4.26.0",
+ "css-blank-pseudo": "^7.0.1",
+ "css-has-pseudo": "^7.0.3",
+ "css-prefers-color-scheme": "^10.0.0",
+ "cssdb": "^8.4.2",
+ "postcss-attribute-case-insensitive": "^7.0.1",
+ "postcss-clamp": "^4.1.0",
+ "postcss-color-functional-notation": "^7.0.12",
+ "postcss-color-hex-alpha": "^10.0.0",
+ "postcss-color-rebeccapurple": "^10.0.0",
+ "postcss-custom-media": "^11.0.6",
+ "postcss-custom-properties": "^14.0.6",
+ "postcss-custom-selectors": "^8.0.5",
+ "postcss-dir-pseudo-class": "^9.0.1",
+ "postcss-double-position-gradients": "^6.0.4",
+ "postcss-focus-visible": "^10.0.1",
+ "postcss-focus-within": "^9.0.1",
+ "postcss-font-variant": "^5.0.0",
+ "postcss-gap-properties": "^6.0.0",
+ "postcss-image-set-function": "^7.0.0",
+ "postcss-lab-function": "^7.0.12",
+ "postcss-logical": "^8.1.0",
+ "postcss-nesting": "^13.0.2",
+ "postcss-opacity-percentage": "^3.0.0",
+ "postcss-overflow-shorthand": "^6.0.0",
+ "postcss-page-break": "^3.0.4",
+ "postcss-place": "^10.0.0",
+ "postcss-pseudo-class-any-link": "^10.0.1",
+ "postcss-replace-overflow-wrap": "^4.0.0",
+ "postcss-selector-not": "^8.0.1"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/postcss-pseudo-class-any-link": {
+ "version": "10.0.1",
+ "resolved": "https://registry.npmjs.org/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-10.0.1.tgz",
+ "integrity": "sha512-3el9rXlBOqTFaMFkWDOkHUTQekFIYnaQY55Rsp8As8QQkpiSgIYEcF/6Ond93oHiDsGb4kad8zjt+NPlOC1H0Q==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "postcss-selector-parser": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/postcss-replace-overflow-wrap": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-4.0.0.tgz",
+ "integrity": "sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "postcss": "^8.0.3"
+ }
+ },
+ "node_modules/postcss-selector-not": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/postcss-selector-not/-/postcss-selector-not-8.0.1.tgz",
+ "integrity": "sha512-kmVy/5PYVb2UOhy0+LqUYAhKj7DUGDpSWa5LZqlkWJaaAV+dxxsOG3+St0yNLu6vsKD7Dmqx+nWQt0iil89+WA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "postcss-selector-parser": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/postcss-selector-parser": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz",
+ "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cssesc": "^3.0.0",
+ "util-deprecate": "^1.0.2"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
"node_modules/postcss-value-parser": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
- "dev": true,
"license": "MIT"
},
"node_modules/prelude-ls": {
@@ -5113,7 +8852,6 @@
"version": "15.8.1",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
- "dev": true,
"license": "MIT",
"dependencies": {
"loose-envify": "^1.4.0",
@@ -5121,6 +8859,18 @@
"react-is": "^16.13.1"
}
},
+ "node_modules/property-expr": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.6.tgz",
+ "integrity": "sha512-SVtmxhRE/CGkn3eZY1T6pC8Nln6Fr/lu1mKSgRud0eC73whjGfoAogbn78LkD8aFL0zz3bAFerKSnOl7NlErBA==",
+ "license": "MIT"
+ },
+ "node_modules/proxy-from-env": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
+ "license": "MIT"
+ },
"node_modules/punycode": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
@@ -5131,6 +8881,15 @@
"node": ">=6"
}
},
+ "node_modules/queue": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz",
+ "integrity": "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==",
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "~2.0.3"
+ }
+ },
"node_modules/queue-microtask": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
@@ -5173,13 +8932,130 @@
"react": "^19.1.0"
}
},
+ "node_modules/react-hook-form": {
+ "version": "7.63.0",
+ "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.63.0.tgz",
+ "integrity": "sha512-ZwueDMvUeucovM2VjkCf7zIHcs1aAlDimZu2Hvel5C5907gUzMpm4xCrQXtRzCvsBqFjonB4m3x4LzCFI1ZKWA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/react-hook-form"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17 || ^18 || ^19"
+ }
+ },
+ "node_modules/react-hot-toast": {
+ "version": "2.6.0",
+ "resolved": "https://registry.npmjs.org/react-hot-toast/-/react-hot-toast-2.6.0.tgz",
+ "integrity": "sha512-bH+2EBMZ4sdyou/DPrfgIouFpcRLCJ+HoCA32UoAYHn6T3Ur5yfcDCeSr5mwldl6pFOsiocmrXMuoCJ1vV8bWg==",
+ "license": "MIT",
+ "dependencies": {
+ "csstype": "^3.1.3",
+ "goober": "^2.1.16"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "react": ">=16",
+ "react-dom": ">=16"
+ }
+ },
"node_modules/react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
- "dev": true,
"license": "MIT"
},
+ "node_modules/react-pdf": {
+ "version": "10.1.0",
+ "resolved": "https://registry.npmjs.org/react-pdf/-/react-pdf-10.1.0.tgz",
+ "integrity": "sha512-iUI1YqWgwwZcsXjrehTp3Yi8nT/bvTaWULaRMMyJWvoqqSlopk4LQQ9GDqUnDtX3gzT2glrqrLbjIPl56a+Q3w==",
+ "license": "MIT",
+ "dependencies": {
+ "clsx": "^2.0.0",
+ "dequal": "^2.0.3",
+ "make-cancellable-promise": "^2.0.0",
+ "make-event-props": "^2.0.0",
+ "merge-refs": "^2.0.0",
+ "pdfjs-dist": "5.3.93",
+ "tiny-invariant": "^1.0.0",
+ "warning": "^4.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/wojtekmaj/react-pdf?sponsor=1"
+ },
+ "peerDependencies": {
+ "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/react-pdf/node_modules/pdfjs-dist": {
+ "version": "5.3.93",
+ "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-5.3.93.tgz",
+ "integrity": "sha512-w3fQKVL1oGn8FRyx5JUG5tnbblggDqyx2XzA5brsJ5hSuS+I0NdnJANhmeWKLjotdbPQucLBug5t0MeWr0AAdg==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=20.16.0 || >=22.3.0"
+ },
+ "optionalDependencies": {
+ "@napi-rs/canvas": "^0.1.71"
+ }
+ },
+ "node_modules/react-phone-number-input": {
+ "version": "3.4.12",
+ "resolved": "https://registry.npmjs.org/react-phone-number-input/-/react-phone-number-input-3.4.12.tgz",
+ "integrity": "sha512-Raob77KdtLGm49iC6nuOX9qy6Mg16idkgC7Y1mHmvG2WBYoauHpzxYNlfmFskQKeiztrJIwPhPzBhjFwjenNCA==",
+ "license": "MIT",
+ "dependencies": {
+ "classnames": "^2.5.1",
+ "country-flag-icons": "^1.5.17",
+ "input-format": "^0.3.10",
+ "libphonenumber-js": "^1.11.20",
+ "prop-types": "^15.8.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.8",
+ "react-dom": ">=16.8"
+ }
+ },
+ "node_modules/react-toastify": {
+ "version": "11.0.5",
+ "resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-11.0.5.tgz",
+ "integrity": "sha512-EpqHBGvnSTtHYhCPLxML05NLY2ZX0JURbAdNYa6BUkk+amz4wbKBQvoKQAB0ardvSarUBuY4Q4s1sluAzZwkmA==",
+ "license": "MIT",
+ "dependencies": {
+ "clsx": "^2.1.1"
+ },
+ "peerDependencies": {
+ "react": "^18 || ^19",
+ "react-dom": "^18 || ^19"
+ }
+ },
+ "node_modules/readable-stream": {
+ "version": "3.6.2",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+ "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
"node_modules/reflect.getprototypeof": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz",
@@ -5224,6 +9100,15 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/require-from-string": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
+ "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/resolve": {
"version": "1.22.10",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
@@ -5265,6 +9150,12 @@
"url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
}
},
+ "node_modules/restructure": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/restructure/-/restructure-3.0.2.tgz",
+ "integrity": "sha512-gSfoiOEA0VPE6Tukkrr7I0RBdE0s7H1eFCDBk05l1KIQT1UIKNc5JZy6jdyW6eYH3aR3g5b3PuL77rq0hvwtAw==",
+ "license": "MIT"
+ },
"node_modules/reusify": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
@@ -5320,6 +9211,26 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
"node_modules/safe-push-apply": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz",
@@ -5355,6 +9266,15 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/safe-stable-stringify": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz",
+ "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ }
+ },
"node_modules/scheduler": {
"version": "0.26.0",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz",
@@ -5565,6 +9485,15 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/simple-swizzle": {
+ "version": "0.2.4",
+ "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.4.tgz",
+ "integrity": "sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw==",
+ "license": "MIT",
+ "dependencies": {
+ "is-arrayish": "^0.3.1"
+ }
+ },
"node_modules/source-map-js": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
@@ -5581,6 +9510,15 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/stack-trace": {
+ "version": "0.0.10",
+ "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz",
+ "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==",
+ "license": "MIT",
+ "engines": {
+ "node": "*"
+ }
+ },
"node_modules/stop-iteration-iterator": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz",
@@ -5595,6 +9533,15 @@
"node": ">= 0.4"
}
},
+ "node_modules/string_decoder": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
+ "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+ "license": "MIT",
+ "dependencies": {
+ "safe-buffer": "~5.2.0"
+ }
+ },
"node_modules/string.prototype.includes": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz",
@@ -5780,11 +9727,22 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/svg-arc-to-cubic-bezier": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/svg-arc-to-cubic-bezier/-/svg-arc-to-cubic-bezier-3.2.0.tgz",
+ "integrity": "sha512-djbJ/vZKZO+gPoSDThGNpKDO+o+bAeA4XQKovvkNCqnIS2t+S4qnLAGQhyyrulhCFRl1WWzAp0wUDV8PpTVU3g==",
+ "license": "ISC"
+ },
+ "node_modules/tabbable": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz",
+ "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==",
+ "license": "MIT"
+ },
"node_modules/tailwindcss": {
"version": "4.1.13",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.13.tgz",
"integrity": "sha512-i+zidfmTqtwquj4hMEwdjshYYgMbOrPzb9a0M3ZgNa0JMoZeFC6bxZvO8yr8ozS6ix2SDz0+mvryPeBs2TFE+w==",
- "dev": true,
"license": "MIT"
},
"node_modules/tapable": {
@@ -5818,6 +9776,30 @@
"node": ">=18"
}
},
+ "node_modules/text-hex": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz",
+ "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==",
+ "license": "MIT"
+ },
+ "node_modules/tiny-case": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/tiny-case/-/tiny-case-1.0.3.tgz",
+ "integrity": "sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q==",
+ "license": "MIT"
+ },
+ "node_modules/tiny-inflate": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz",
+ "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==",
+ "license": "MIT"
+ },
+ "node_modules/tiny-invariant": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz",
+ "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==",
+ "license": "MIT"
+ },
"node_modules/tinyglobby": {
"version": "0.2.15",
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
@@ -5879,6 +9861,21 @@
"node": ">=8.0"
}
},
+ "node_modules/toposort": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz",
+ "integrity": "sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==",
+ "license": "MIT"
+ },
+ "node_modules/triple-beam": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz",
+ "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 14.0.0"
+ }
+ },
"node_modules/ts-api-utils": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz",
@@ -5924,6 +9921,18 @@
"node": ">= 0.8.0"
}
},
+ "node_modules/type-fest": {
+ "version": "2.19.0",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz",
+ "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==",
+ "license": "(MIT OR CC0-1.0)",
+ "engines": {
+ "node": ">=12.20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/typed-array-buffer": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz",
@@ -6042,6 +10051,32 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/unicode-properties": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/unicode-properties/-/unicode-properties-1.4.1.tgz",
+ "integrity": "sha512-CLjCCLQ6UuMxWnbIylkisbRj31qxHPAurvena/0iwSVbQ2G1VY5/HjV0IRabOEbDHlzZlRdCrD4NhB0JtU40Pg==",
+ "license": "MIT",
+ "dependencies": {
+ "base64-js": "^1.3.0",
+ "unicode-trie": "^2.0.0"
+ }
+ },
+ "node_modules/unicode-trie": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/unicode-trie/-/unicode-trie-2.0.0.tgz",
+ "integrity": "sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ==",
+ "license": "MIT",
+ "dependencies": {
+ "pako": "^0.2.5",
+ "tiny-inflate": "^1.0.0"
+ }
+ },
+ "node_modules/unicode-trie/node_modules/pako": {
+ "version": "0.2.9",
+ "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz",
+ "integrity": "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==",
+ "license": "MIT"
+ },
"node_modules/unrs-resolver": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.11.1.tgz",
@@ -6081,7 +10116,6 @@
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz",
"integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==",
- "dev": true,
"funding": [
{
"type": "opencollective",
@@ -6118,6 +10152,44 @@
"punycode": "^2.1.0"
}
},
+ "node_modules/use-sync-external-store": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz",
+ "integrity": "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
+ "license": "MIT"
+ },
+ "node_modules/vite-compatible-readable-stream": {
+ "version": "3.6.1",
+ "resolved": "https://registry.npmjs.org/vite-compatible-readable-stream/-/vite-compatible-readable-stream-3.6.1.tgz",
+ "integrity": "sha512-t20zYkrSf868+j/p31cRIGN28Phrjm3nRSLR2fyc2tiWi4cZGVdv68yNlwnIINTkMTmPoMiSlc0OadaO7DXZaQ==",
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/warning": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
+ "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==",
+ "license": "MIT",
+ "dependencies": {
+ "loose-envify": "^1.0.0"
+ }
+ },
"node_modules/which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
@@ -6223,6 +10295,42 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/winston": {
+ "version": "3.18.2",
+ "resolved": "https://registry.npmjs.org/winston/-/winston-3.18.2.tgz",
+ "integrity": "sha512-+yDkrWD2rWkv6XjSgK2QyujZDNsHE9YLa8S284TpVrYdaloMkZ7NvHzfnETYlSPOX9h5j5VJ+Ro9J872O8CC/g==",
+ "license": "MIT",
+ "dependencies": {
+ "@colors/colors": "^1.6.0",
+ "@dabh/diagnostics": "^2.0.7",
+ "async": "^3.2.3",
+ "is-stream": "^2.0.0",
+ "logform": "^2.7.0",
+ "one-time": "^1.0.0",
+ "readable-stream": "^3.4.0",
+ "safe-stable-stringify": "^2.3.1",
+ "stack-trace": "0.0.x",
+ "triple-beam": "^1.3.0",
+ "winston-transport": "^4.9.0"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ }
+ },
+ "node_modules/winston-transport": {
+ "version": "4.9.0",
+ "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.9.0.tgz",
+ "integrity": "sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==",
+ "license": "MIT",
+ "dependencies": {
+ "logform": "^2.7.0",
+ "readable-stream": "^3.6.2",
+ "triple-beam": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ }
+ },
"node_modules/word-wrap": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
@@ -6255,6 +10363,53 @@
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
+ },
+ "node_modules/yoga-layout": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/yoga-layout/-/yoga-layout-3.2.1.tgz",
+ "integrity": "sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ==",
+ "license": "MIT"
+ },
+ "node_modules/yup": {
+ "version": "1.7.1",
+ "resolved": "https://registry.npmjs.org/yup/-/yup-1.7.1.tgz",
+ "integrity": "sha512-GKHFX2nXul2/4Dtfxhozv701jLQHdf6J34YDh2cEkpqoo8le5Mg6/LrdseVLrFarmFygZTlfIhHx/QKfb/QWXw==",
+ "license": "MIT",
+ "dependencies": {
+ "property-expr": "^2.0.5",
+ "tiny-case": "^1.0.3",
+ "toposort": "^2.0.2",
+ "type-fest": "^2.19.0"
+ }
+ },
+ "node_modules/zustand": {
+ "version": "5.0.8",
+ "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.8.tgz",
+ "integrity": "sha512-gyPKpIaxY9XcO2vSMrLbiER7QMAMGOQZVRdJ6Zi782jkbzZygq5GI9nG8g+sMgitRtndwaBSl7uiqC49o1SSiw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.20.0"
+ },
+ "peerDependencies": {
+ "@types/react": ">=18.0.0",
+ "immer": ">=9.0.6",
+ "react": ">=18.0.0",
+ "use-sync-external-store": ">=1.2.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "immer": {
+ "optional": true
+ },
+ "react": {
+ "optional": true
+ },
+ "use-sync-external-store": {
+ "optional": true
+ }
+ }
}
}
}
diff --git a/package.json b/package.json
index e227ef0..0d49287 100644
--- a/package.json
+++ b/package.json
@@ -9,12 +9,37 @@
"lint": "eslint"
},
"dependencies": {
+ "@headlessui/react": "^2.2.9",
+ "@heroicons/react": "^2.2.0",
+ "@hookform/resolvers": "^5.2.2",
+ "@lottiefiles/react-lottie-player": "^3.6.0",
+ "@react-pdf/renderer": "^4.3.0",
+ "@tailwindcss/forms": "^0.5.10",
+ "@tailwindcss/typography": "^0.5.19",
+ "@tailwindplus/elements": "^1.0.15",
+ "@tailwindui/react": "^0.1.1",
+ "axios": "^1.12.2",
+ "clsx": "^2.1.1",
+ "country-flag-icons": "^1.5.21",
+ "country-select-js": "^2.1.0",
+ "intl-tel-input": "^25.10.11",
+ "motion": "^12.23.22",
"next": "15.5.4",
+ "pdfjs-dist": "^5.4.149",
"react": "19.1.0",
- "react-dom": "19.1.0"
+ "react-dom": "19.1.0",
+ "react-hook-form": "^7.63.0",
+ "react-hot-toast": "^2.6.0",
+ "react-pdf": "^10.1.0",
+ "react-phone-number-input": "^3.4.12",
+ "react-toastify": "^11.0.5",
+ "winston": "^3.17.0",
+ "yup": "^1.7.1",
+ "zustand": "^5.0.8"
},
"devDependencies": {
"@eslint/eslintrc": "^3",
+ "@eslint/js": "^9.36.0",
"@tailwindcss/postcss": "^4",
"@types/node": "^20",
"@types/react": "^19",
@@ -22,7 +47,10 @@
"autoprefixer": "^10.4.21",
"eslint": "^9",
"eslint-config-next": "15.5.4",
+ "eslint-plugin-react-hooks": "^5.2.0",
+ "globals": "^16.4.0",
"postcss": "^8.5.6",
+ "postcss-preset-env": "^10.4.0",
"tailwindcss": "^4.1.13",
"typescript": "^5"
}
diff --git a/public/images/logos/pp_logo_gold_transparent.png b/public/images/logos/pp_logo_gold_transparent.png
new file mode 100644
index 0000000..4b02702
Binary files /dev/null and b/public/images/logos/pp_logo_gold_transparent.png differ
diff --git a/public/images/misc/coffeebeans_background.png b/public/images/misc/coffeebeans_background.png
new file mode 100644
index 0000000..7fa9648
Binary files /dev/null and b/public/images/misc/coffeebeans_background.png differ
diff --git a/src/app/ClientWrapper.tsx b/src/app/ClientWrapper.tsx
new file mode 100644
index 0000000..76d850b
--- /dev/null
+++ b/src/app/ClientWrapper.tsx
@@ -0,0 +1,7 @@
+'use client';
+
+import { I18nProvider } from './i18n/useTranslation';
+
+export default function ClientWrapper({ children }: { children: React.ReactNode }) {
+ return {children};
+}
\ No newline at end of file
diff --git a/src/app/background/GlobalAnimatedBackground.tsx b/src/app/background/GlobalAnimatedBackground.tsx
new file mode 100644
index 0000000..98fc880
--- /dev/null
+++ b/src/app/background/GlobalAnimatedBackground.tsx
@@ -0,0 +1,109 @@
+import React from "react";
+
+// Utility to detect mobile devices
+function isMobileDevice() {
+ if (typeof navigator === "undefined") return false;
+ return /Mobi|Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
+ navigator.userAgent
+ );
+}
+
+function GlobalAnimatedBackground() {
+ // Always use dashboard style for a uniform look
+ const bgGradient = "linear-gradient(135deg, #1e293b 0%, #334155 100%)";
+
+ // Detect small screens (mobile/tablet)
+ const isMobile = isMobileDevice();
+
+ if (isMobile) {
+ // Render only the static background gradient and overlay, no animation
+ return (
+
+ );
+ }
+
+ return (
+
+ {/* Overlays */}
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+export default GlobalAnimatedBackground;
\ No newline at end of file
diff --git a/src/app/community/page.tsx b/src/app/community/page.tsx
new file mode 100644
index 0000000..75dd3bd
--- /dev/null
+++ b/src/app/community/page.tsx
@@ -0,0 +1,284 @@
+'use client'
+
+import { useEffect } from 'react'
+import { useRouter } from 'next/navigation'
+import useAuthStore from '../store/authStore'
+import Header from '../components/nav/Header'
+import Footer from '../components/Footer'
+import {
+ UsersIcon,
+ ChatBubbleLeftRightIcon,
+ HeartIcon,
+ FireIcon,
+ TrophyIcon,
+ UserGroupIcon,
+ PlusIcon,
+ ArrowRightIcon
+} from '@heroicons/react/24/outline'
+
+export default function CommunityPage() {
+ const router = useRouter()
+ const user = useAuthStore(state => state.user)
+
+ // Redirect if not logged in
+ useEffect(() => {
+ if (!user) {
+ router.push('/login')
+ }
+ }, [user, router])
+
+ // Don't render if no user
+ if (!user) {
+ return (
+
+ )
+ }
+
+ // Mock community data
+ const communityStats = [
+ { label: 'Members', value: '12,487', icon: UsersIcon, color: 'text-blue-600' },
+ { label: 'Active Groups', value: '156', icon: UserGroupIcon, color: 'text-green-600' },
+ { label: 'Discussions', value: '3,421', icon: ChatBubbleLeftRightIcon, color: 'text-purple-600' },
+ { label: 'Daily Active', value: '2,186', icon: FireIcon, color: 'text-orange-600' }
+ ]
+
+ const trendingGroups = [
+ {
+ name: 'Eco Warriors',
+ members: '1,284',
+ category: 'Sustainability',
+ image: '🌱',
+ description: 'Join fellow eco-enthusiasts in making the world greener'
+ },
+ {
+ name: 'Zero Waste Living',
+ members: '892',
+ category: 'Lifestyle',
+ image: '♻️',
+ description: 'Tips and tricks for living a zero-waste lifestyle'
+ },
+ {
+ name: 'Sustainable Fashion',
+ members: '756',
+ category: 'Fashion',
+ image: '👕',
+ description: 'Ethical fashion choices and sustainable brands'
+ },
+ {
+ name: 'Green Tech',
+ members: '634',
+ category: 'Technology',
+ image: '💚',
+ description: 'Discuss the latest in green technology and innovation'
+ }
+ ]
+
+ const recentPosts = [
+ {
+ user: 'Sarah M.',
+ group: 'Eco Warriors',
+ time: '2 hours ago',
+ content: 'Just discovered a fantastic new way to upcycle old furniture! Has anyone tried painting with eco-friendly paints?',
+ likes: 23,
+ comments: 8
+ },
+ {
+ user: 'David K.',
+ group: 'Zero Waste Living',
+ time: '4 hours ago',
+ content: 'Week 3 of my zero-waste challenge! Managed to produce only 1 small jar of waste. Here are my top tips...',
+ likes: 45,
+ comments: 12
+ },
+ {
+ user: 'Maria L.',
+ group: 'Sustainable Fashion',
+ time: '6 hours ago',
+ content: 'Found an amazing local brand that makes clothes from recycled ocean plastic. Their quality is incredible!',
+ likes: 38,
+ comments: 15
+ }
+ ]
+
+ return (
+
+
+
+
+
+ {/* Header Section */}
+
+
+ Welcome to Profit Planet Community 🌍
+
+
+ Connect with like-minded individuals, share sustainable practices, and make a positive impact together.
+
+
+
+ {/* Community Stats */}
+
+ {communityStats.map((stat, index) => (
+
+
+
{stat.value}
+
{stat.label}
+
+ ))}
+
+
+
+ {/* Main Content */}
+
+ {/* Trending Groups */}
+
+
+
+
+ Trending Groups
+
+
+
+
+
+ {trendingGroups.map((group, index) => (
+
+
+
{group.image}
+
+
{group.name}
+
{group.category}
+
{group.description}
+
{group.members} members
+
+
+
+
+ ))}
+
+
+
+ {/* Recent Discussions */}
+
+
+
+
+ Recent Discussions
+
+
+
+
+
+ {recentPosts.map((post, index) => (
+
+
+
+
+ {post.user.charAt(0)}
+
+
+
+
+ {post.user}
+ •
+ {post.group}
+ •
+ {post.time}
+
+
{post.content}
+
+
+
+
+
+
+
+ ))}
+
+
+
+
+ {/* Sidebar */}
+
+ {/* Quick Actions */}
+
+
Quick Actions
+
+
+
+
+
+
+
+ {/* My Groups */}
+
+
My Groups
+
+
+
🌱
+
+
Eco Warriors
+
1,284 members
+
+
+
+
♻️
+
+
Zero Waste Living
+
892 members
+
+
+
+
+
+ {/* Community Guidelines */}
+
+
Community Guidelines
+
+ - • Be respectful and kind
+ - • Stay on topic
+ - • Share authentic experiences
+ - • Help others learn and grow
+
+
+
+
+
+
+
+
+
+
+ )
+}
\ No newline at end of file
diff --git a/src/app/components/Footer.tsx b/src/app/components/Footer.tsx
new file mode 100644
index 0000000..62625cc
--- /dev/null
+++ b/src/app/components/Footer.tsx
@@ -0,0 +1,26 @@
+import React from 'react';
+import { useTranslation } from '../i18n/useTranslation';
+
+export default function Footer() {
+ const { t } = useTranslation();
+ return (
+
+ );
+}
\ No newline at end of file
diff --git a/src/app/components/LanguageSwitcher.tsx b/src/app/components/LanguageSwitcher.tsx
new file mode 100644
index 0000000..5711f00
--- /dev/null
+++ b/src/app/components/LanguageSwitcher.tsx
@@ -0,0 +1,96 @@
+'use client';
+
+import { Menu, MenuButton, MenuItem, MenuItems } from '@headlessui/react';
+import { ChevronDownIcon } from '@heroicons/react/20/solid';
+import { useTranslation } from '../i18n/useTranslation';
+import { SUPPORTED_LANGUAGES, LANGUAGE_NAMES } from '../i18n/config';
+
+interface LanguageSwitcherProps {
+ variant?: 'light' | 'dark';
+}
+
+// Flag Icons mit Emoji (viel sauberer als selbst gezeichnete CSS-Flaggen)
+const FlagIcon = ({ countryCode, className = "size-5" }: { countryCode: string; className?: string }) => {
+ const flags = {
+ 'de': '🇩🇪',
+ 'en': '🇬🇧'
+ };
+
+ return (
+
+ {flags[countryCode as keyof typeof flags] || '🏳️'}
+
+ );
+};
+
+export default function LanguageSwitcher({ variant = 'light' }: LanguageSwitcherProps) {
+ const { language, setLanguage } = useTranslation();
+
+ const getButtonStyles = () => {
+ if (variant === 'dark') {
+ return 'inline-flex w-full justify-center gap-x-1.5 rounded-md bg-white/10 px-3 py-2 text-sm font-semibold text-white inset-ring-1 inset-ring-white/5 hover:bg-white/20';
+ }
+ return 'inline-flex w-full justify-center gap-x-1.5 rounded-md bg-gray-100 px-3 py-2 text-sm font-semibold text-gray-900 ring-1 ring-gray-300 hover:bg-gray-200';
+ };
+
+ const getMenuStyles = () => {
+ if (variant === 'dark') {
+ return 'absolute right-0 z-10 mt-2 w-48 origin-top-right divide-y divide-white/10 rounded-md bg-gray-800 outline-1 -outline-offset-1 outline-white/10 transition data-closed:scale-95 data-closed:transform data-closed:opacity-0 data-enter:duration-100 data-enter:ease-out data-leave:duration-75 data-leave:ease-in';
+ }
+ return 'absolute right-0 z-10 mt-2 w-48 origin-top-right divide-y divide-gray-100 rounded-md bg-white outline-1 -outline-offset-1 outline-gray-200 transition data-closed:scale-95 data-closed:transform data-closed:opacity-0 data-enter:duration-100 data-enter:ease-out data-leave:duration-75 data-leave:ease-in';
+ };
+
+ const getItemStyles = (isActive: boolean) => {
+ if (variant === 'dark') {
+ return `group flex items-center px-4 py-2 text-sm ${
+ isActive
+ ? 'bg-[#8D6B1D] text-white'
+ : 'text-gray-300 data-focus:bg-white/5 data-focus:text-white data-focus:outline-hidden'
+ }`;
+ }
+ return `group flex items-center px-4 py-2 text-sm ${
+ isActive
+ ? 'bg-[#8D6B1D] text-white'
+ : 'text-gray-700 data-focus:bg-gray-100 data-focus:text-gray-900 data-focus:outline-hidden'
+ }`;
+ };
+
+ return (
+
+ );
+}
\ No newline at end of file
diff --git a/src/app/components/PageLayout.tsx b/src/app/components/PageLayout.tsx
new file mode 100644
index 0000000..bc8a390
--- /dev/null
+++ b/src/app/components/PageLayout.tsx
@@ -0,0 +1,48 @@
+'use client';
+
+import React from 'react';
+import Header from './nav/Header';
+import Footer from './Footer';
+import GlobalAnimatedBackground from '../background/GlobalAnimatedBackground';
+
+// Utility to detect mobile devices
+function isMobileDevice() {
+ if (typeof navigator === 'undefined') return false;
+ return /Mobi|Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
+}
+
+interface PageLayoutProps {
+ children: React.ReactNode;
+ showHeader?: boolean;
+ showFooter?: boolean;
+}
+
+export default function PageLayout({
+ children,
+ showHeader = true,
+ showFooter = true
+}: PageLayoutProps) {
+ const isMobile = isMobileDevice();
+
+ return (
+
+
+ {showHeader && (
+
+
+
+ )}
+
+ {/* Main content */}
+
+ {children}
+
+
+ {showFooter && (
+
+
+
+ )}
+
+ );
+}
\ No newline at end of file
diff --git a/src/app/components/alert.tsx b/src/app/components/alert.tsx
new file mode 100644
index 0000000..9369921
--- /dev/null
+++ b/src/app/components/alert.tsx
@@ -0,0 +1,95 @@
+import * as Headless from '@headlessui/react'
+import clsx from 'clsx'
+import type React from 'react'
+import { Text } from './text'
+
+const sizes = {
+ xs: 'sm:max-w-xs',
+ sm: 'sm:max-w-sm',
+ md: 'sm:max-w-md',
+ lg: 'sm:max-w-lg',
+ xl: 'sm:max-w-xl',
+ '2xl': 'sm:max-w-2xl',
+ '3xl': 'sm:max-w-3xl',
+ '4xl': 'sm:max-w-4xl',
+ '5xl': 'sm:max-w-5xl',
+}
+
+export function Alert({
+ size = 'md',
+ className,
+ children,
+ ...props
+}: { size?: keyof typeof sizes; className?: string; children: React.ReactNode } & Omit<
+ Headless.DialogProps,
+ 'as' | 'className'
+>) {
+ return (
+
+
+
+
+
+ )
+}
+
+export function AlertTitle({
+ className,
+ ...props
+}: { className?: string } & Omit) {
+ return (
+
+ )
+}
+
+export function AlertDescription({
+ className,
+ ...props
+}: { className?: string } & Omit, 'as' | 'className'>) {
+ return (
+
+ )
+}
+
+export function AlertBody({ className, ...props }: React.ComponentPropsWithoutRef<'div'>) {
+ return
+}
+
+export function AlertActions({ className, ...props }: React.ComponentPropsWithoutRef<'div'>) {
+ return (
+
+ )
+}
diff --git a/src/app/components/auth-layout.tsx b/src/app/components/auth-layout.tsx
new file mode 100644
index 0000000..52309ea
--- /dev/null
+++ b/src/app/components/auth-layout.tsx
@@ -0,0 +1,11 @@
+import type React from 'react'
+
+export function AuthLayout({ children }: { children: React.ReactNode }) {
+ return (
+
+
+ {children}
+
+
+ )
+}
diff --git a/src/app/components/avatar.tsx b/src/app/components/avatar.tsx
new file mode 100644
index 0000000..2816edc
--- /dev/null
+++ b/src/app/components/avatar.tsx
@@ -0,0 +1,87 @@
+import * as Headless from '@headlessui/react'
+import clsx from 'clsx'
+import React, { forwardRef } from 'react'
+import { TouchTarget } from './button'
+import { Link } from './link'
+
+type AvatarProps = {
+ src?: string | null
+ square?: boolean
+ initials?: string
+ alt?: string
+ className?: string
+}
+
+export function Avatar({
+ src = null,
+ square = false,
+ initials,
+ alt = '',
+ className,
+ ...props
+}: AvatarProps & React.ComponentPropsWithoutRef<'span'>) {
+ return (
+
+ {initials && (
+
+ )}
+ {src &&
}
+
+ )
+}
+
+export const AvatarButton = forwardRef(function AvatarButton(
+ {
+ src,
+ square = false,
+ initials,
+ alt,
+ className,
+ ...props
+ }: AvatarProps &
+ (
+ | ({ href?: never } & Omit)
+ | ({ href: string } & Omit, 'className'>)
+ ),
+ ref: React.ForwardedRef
+) {
+ let classes = clsx(
+ className,
+ square ? 'rounded-[20%]' : 'rounded-full',
+ 'relative inline-grid focus:not-data-focus:outline-hidden data-focus:outline-2 data-focus:outline-offset-2 data-focus:outline-blue-500'
+ )
+
+ return typeof props.href === 'string' ? (
+ }>
+
+
+
+
+ ) : (
+
+
+
+
+
+ )
+})
diff --git a/src/app/components/badge.tsx b/src/app/components/badge.tsx
new file mode 100644
index 0000000..d81cc96
--- /dev/null
+++ b/src/app/components/badge.tsx
@@ -0,0 +1,82 @@
+import * as Headless from '@headlessui/react'
+import clsx from 'clsx'
+import React, { forwardRef } from 'react'
+import { TouchTarget } from './button'
+import { Link } from './link'
+
+const colors = {
+ red: 'bg-red-500/15 text-red-700 group-data-hover:bg-red-500/25 dark:bg-red-500/10 dark:text-red-400 dark:group-data-hover:bg-red-500/20',
+ orange:
+ 'bg-orange-500/15 text-orange-700 group-data-hover:bg-orange-500/25 dark:bg-orange-500/10 dark:text-orange-400 dark:group-data-hover:bg-orange-500/20',
+ amber:
+ 'bg-amber-400/20 text-amber-700 group-data-hover:bg-amber-400/30 dark:bg-amber-400/10 dark:text-amber-400 dark:group-data-hover:bg-amber-400/15',
+ yellow:
+ 'bg-yellow-400/20 text-yellow-700 group-data-hover:bg-yellow-400/30 dark:bg-yellow-400/10 dark:text-yellow-300 dark:group-data-hover:bg-yellow-400/15',
+ lime: 'bg-lime-400/20 text-lime-700 group-data-hover:bg-lime-400/30 dark:bg-lime-400/10 dark:text-lime-300 dark:group-data-hover:bg-lime-400/15',
+ green:
+ 'bg-green-500/15 text-green-700 group-data-hover:bg-green-500/25 dark:bg-green-500/10 dark:text-green-400 dark:group-data-hover:bg-green-500/20',
+ emerald:
+ 'bg-emerald-500/15 text-emerald-700 group-data-hover:bg-emerald-500/25 dark:bg-emerald-500/10 dark:text-emerald-400 dark:group-data-hover:bg-emerald-500/20',
+ teal: 'bg-teal-500/15 text-teal-700 group-data-hover:bg-teal-500/25 dark:bg-teal-500/10 dark:text-teal-300 dark:group-data-hover:bg-teal-500/20',
+ cyan: 'bg-cyan-400/20 text-cyan-700 group-data-hover:bg-cyan-400/30 dark:bg-cyan-400/10 dark:text-cyan-300 dark:group-data-hover:bg-cyan-400/15',
+ sky: 'bg-sky-500/15 text-sky-700 group-data-hover:bg-sky-500/25 dark:bg-sky-500/10 dark:text-sky-300 dark:group-data-hover:bg-sky-500/20',
+ blue: 'bg-blue-500/15 text-blue-700 group-data-hover:bg-blue-500/25 dark:text-blue-400 dark:group-data-hover:bg-blue-500/25',
+ indigo:
+ 'bg-indigo-500/15 text-indigo-700 group-data-hover:bg-indigo-500/25 dark:text-indigo-400 dark:group-data-hover:bg-indigo-500/20',
+ violet:
+ 'bg-violet-500/15 text-violet-700 group-data-hover:bg-violet-500/25 dark:text-violet-400 dark:group-data-hover:bg-violet-500/20',
+ purple:
+ 'bg-purple-500/15 text-purple-700 group-data-hover:bg-purple-500/25 dark:text-purple-400 dark:group-data-hover:bg-purple-500/20',
+ fuchsia:
+ 'bg-fuchsia-400/15 text-fuchsia-700 group-data-hover:bg-fuchsia-400/25 dark:bg-fuchsia-400/10 dark:text-fuchsia-400 dark:group-data-hover:bg-fuchsia-400/20',
+ pink: 'bg-pink-400/15 text-pink-700 group-data-hover:bg-pink-400/25 dark:bg-pink-400/10 dark:text-pink-400 dark:group-data-hover:bg-pink-400/20',
+ rose: 'bg-rose-400/15 text-rose-700 group-data-hover:bg-rose-400/25 dark:bg-rose-400/10 dark:text-rose-400 dark:group-data-hover:bg-rose-400/20',
+ zinc: 'bg-zinc-600/10 text-zinc-700 group-data-hover:bg-zinc-600/20 dark:bg-white/5 dark:text-zinc-400 dark:group-data-hover:bg-white/10',
+}
+
+type BadgeProps = { color?: keyof typeof colors }
+
+export function Badge({ color = 'zinc', className, ...props }: BadgeProps & React.ComponentPropsWithoutRef<'span'>) {
+ return (
+
+ )
+}
+
+export const BadgeButton = forwardRef(function BadgeButton(
+ {
+ color = 'zinc',
+ className,
+ children,
+ ...props
+ }: BadgeProps & { className?: string; children: React.ReactNode } & (
+ | ({ href?: never } & Omit)
+ | ({ href: string } & Omit, 'className'>)
+ ),
+ ref: React.ForwardedRef
+) {
+ let classes = clsx(
+ className,
+ 'group relative inline-flex rounded-md focus:not-data-focus:outline-hidden data-focus:outline-2 data-focus:outline-offset-2 data-focus:outline-blue-500'
+ )
+
+ return typeof props.href === 'string' ? (
+ }>
+
+ {children}
+
+
+ ) : (
+
+
+ {children}
+
+
+ )
+})
diff --git a/src/app/components/button.tsx b/src/app/components/button.tsx
new file mode 100644
index 0000000..fe085a4
--- /dev/null
+++ b/src/app/components/button.tsx
@@ -0,0 +1,204 @@
+import * as Headless from '@headlessui/react'
+import clsx from 'clsx'
+import React, { forwardRef } from 'react'
+import { Link } from './link'
+
+const styles = {
+ base: [
+ // Base
+ 'relative isolate inline-flex items-baseline justify-center gap-x-2 rounded-lg border text-base/6 font-semibold',
+ // Sizing
+ 'px-[calc(--spacing(3.5)-1px)] py-[calc(--spacing(2.5)-1px)] sm:px-[calc(--spacing(3)-1px)] sm:py-[calc(--spacing(1.5)-1px)] sm:text-sm/6',
+ // Focus
+ 'focus:not-data-focus:outline-hidden data-focus:outline-2 data-focus:outline-offset-2 data-focus:outline-blue-500',
+ // Disabled
+ 'data-disabled:opacity-50',
+ // Icon
+ '*:data-[slot=icon]:-mx-0.5 *:data-[slot=icon]:my-0.5 *:data-[slot=icon]:size-5 *:data-[slot=icon]:shrink-0 *:data-[slot=icon]:self-center *:data-[slot=icon]:text-(--btn-icon) sm:*:data-[slot=icon]:my-1 sm:*:data-[slot=icon]:size-4 forced-colors:[--btn-icon:ButtonText] forced-colors:data-hover:[--btn-icon:ButtonText]',
+ ],
+ solid: [
+ // Optical border, implemented as the button background to avoid corner artifacts
+ 'border-transparent bg-(--btn-border)',
+ // Dark mode: border is rendered on `after` so background is set to button background
+ 'dark:bg-(--btn-bg)',
+ // Button background, implemented as foreground layer to stack on top of pseudo-border layer
+ 'before:absolute before:inset-0 before:-z-10 before:rounded-[calc(var(--radius-lg)-1px)] before:bg-(--btn-bg)',
+ // Drop shadow, applied to the inset `before` layer so it blends with the border
+ 'before:shadow-sm',
+ // Background color is moved to control and shadow is removed in dark mode so hide `before` pseudo
+ 'dark:before:hidden',
+ // Dark mode: Subtle white outline is applied using a border
+ 'dark:border-white/5',
+ // Shim/overlay, inset to match button foreground and used for hover state + highlight shadow
+ 'after:absolute after:inset-0 after:-z-10 after:rounded-[calc(var(--radius-lg)-1px)]',
+ // Inner highlight shadow
+ 'after:shadow-[inset_0_1px_--theme(--color-white/15%)]',
+ // White overlay on hover
+ 'data-active:after:bg-(--btn-hover-overlay) data-hover:after:bg-(--btn-hover-overlay)',
+ // Dark mode: `after` layer expands to cover entire button
+ 'dark:after:-inset-px dark:after:rounded-lg',
+ // Disabled
+ 'data-disabled:before:shadow-none data-disabled:after:shadow-none',
+ ],
+ outline: [
+ // Base
+ 'border-zinc-950/10 text-zinc-950 data-active:bg-zinc-950/2.5 data-hover:bg-zinc-950/2.5',
+ // Dark mode
+ 'dark:border-white/15 dark:text-white dark:[--btn-bg:transparent] dark:data-active:bg-white/5 dark:data-hover:bg-white/5',
+ // Icon
+ '[--btn-icon:var(--color-zinc-500)] data-active:[--btn-icon:var(--color-zinc-700)] data-hover:[--btn-icon:var(--color-zinc-700)] dark:data-active:[--btn-icon:var(--color-zinc-400)] dark:data-hover:[--btn-icon:var(--color-zinc-400)]',
+ ],
+ plain: [
+ // Base
+ 'border-transparent text-zinc-950 data-active:bg-zinc-950/5 data-hover:bg-zinc-950/5',
+ // Dark mode
+ 'dark:text-white dark:data-active:bg-white/10 dark:data-hover:bg-white/10',
+ // Icon
+ '[--btn-icon:var(--color-zinc-500)] data-active:[--btn-icon:var(--color-zinc-700)] data-hover:[--btn-icon:var(--color-zinc-700)] dark:[--btn-icon:var(--color-zinc-500)] dark:data-active:[--btn-icon:var(--color-zinc-400)] dark:data-hover:[--btn-icon:var(--color-zinc-400)]',
+ ],
+ colors: {
+ 'dark/zinc': [
+ 'text-white [--btn-bg:var(--color-zinc-900)] [--btn-border:var(--color-zinc-950)]/90 [--btn-hover-overlay:var(--color-white)]/10',
+ 'dark:text-white dark:[--btn-bg:var(--color-zinc-600)] dark:[--btn-hover-overlay:var(--color-white)]/5',
+ '[--btn-icon:var(--color-zinc-400)] data-active:[--btn-icon:var(--color-zinc-300)] data-hover:[--btn-icon:var(--color-zinc-300)]',
+ ],
+ light: [
+ 'text-zinc-950 [--btn-bg:white] [--btn-border:var(--color-zinc-950)]/10 [--btn-hover-overlay:var(--color-zinc-950)]/2.5 data-active:[--btn-border:var(--color-zinc-950)]/15 data-hover:[--btn-border:var(--color-zinc-950)]/15',
+ 'dark:text-white dark:[--btn-hover-overlay:var(--color-white)]/5 dark:[--btn-bg:var(--color-zinc-800)]',
+ '[--btn-icon:var(--color-zinc-500)] data-active:[--btn-icon:var(--color-zinc-700)] data-hover:[--btn-icon:var(--color-zinc-700)] dark:[--btn-icon:var(--color-zinc-500)] dark:data-active:[--btn-icon:var(--color-zinc-400)] dark:data-hover:[--btn-icon:var(--color-zinc-400)]',
+ ],
+ 'dark/white': [
+ 'text-white [--btn-bg:var(--color-zinc-900)] [--btn-border:var(--color-zinc-950)]/90 [--btn-hover-overlay:var(--color-white)]/10',
+ 'dark:text-zinc-950 dark:[--btn-bg:white] dark:[--btn-hover-overlay:var(--color-zinc-950)]/5',
+ '[--btn-icon:var(--color-zinc-400)] data-active:[--btn-icon:var(--color-zinc-300)] data-hover:[--btn-icon:var(--color-zinc-300)] dark:[--btn-icon:var(--color-zinc-500)] dark:data-active:[--btn-icon:var(--color-zinc-400)] dark:data-hover:[--btn-icon:var(--color-zinc-400)]',
+ ],
+ dark: [
+ 'text-white [--btn-bg:var(--color-zinc-900)] [--btn-border:var(--color-zinc-950)]/90 [--btn-hover-overlay:var(--color-white)]/10',
+ 'dark:[--btn-hover-overlay:var(--color-white)]/5 dark:[--btn-bg:var(--color-zinc-800)]',
+ '[--btn-icon:var(--color-zinc-400)] data-active:[--btn-icon:var(--color-zinc-300)] data-hover:[--btn-icon:var(--color-zinc-300)]',
+ ],
+ white: [
+ 'text-zinc-950 [--btn-bg:white] [--btn-border:var(--color-zinc-950)]/10 [--btn-hover-overlay:var(--color-zinc-950)]/2.5 data-active:[--btn-border:var(--color-zinc-950)]/15 data-hover:[--btn-border:var(--color-zinc-950)]/15',
+ 'dark:[--btn-hover-overlay:var(--color-zinc-950)]/5',
+ '[--btn-icon:var(--color-zinc-400)] data-active:[--btn-icon:var(--color-zinc-500)] data-hover:[--btn-icon:var(--color-zinc-500)]',
+ ],
+ zinc: [
+ 'text-white [--btn-hover-overlay:var(--color-white)]/10 [--btn-bg:var(--color-zinc-600)] [--btn-border:var(--color-zinc-700)]/90',
+ 'dark:[--btn-hover-overlay:var(--color-white)]/5',
+ '[--btn-icon:var(--color-zinc-400)] data-active:[--btn-icon:var(--color-zinc-300)] data-hover:[--btn-icon:var(--color-zinc-300)]',
+ ],
+ indigo: [
+ 'text-white [--btn-hover-overlay:var(--color-white)]/10 [--btn-bg:var(--color-indigo-500)] [--btn-border:var(--color-indigo-600)]/90',
+ '[--btn-icon:var(--color-indigo-300)] data-active:[--btn-icon:var(--color-indigo-200)] data-hover:[--btn-icon:var(--color-indigo-200)]',
+ ],
+ cyan: [
+ 'text-cyan-950 [--btn-bg:var(--color-cyan-300)] [--btn-border:var(--color-cyan-400)]/80 [--btn-hover-overlay:var(--color-white)]/25',
+ '[--btn-icon:var(--color-cyan-500)]',
+ ],
+ red: [
+ 'text-white [--btn-hover-overlay:var(--color-white)]/10 [--btn-bg:var(--color-red-600)] [--btn-border:var(--color-red-700)]/90',
+ '[--btn-icon:var(--color-red-300)] data-active:[--btn-icon:var(--color-red-200)] data-hover:[--btn-icon:var(--color-red-200)]',
+ ],
+ orange: [
+ 'text-white [--btn-hover-overlay:var(--color-white)]/10 [--btn-bg:var(--color-orange-500)] [--btn-border:var(--color-orange-600)]/90',
+ '[--btn-icon:var(--color-orange-300)] data-active:[--btn-icon:var(--color-orange-200)] data-hover:[--btn-icon:var(--color-orange-200)]',
+ ],
+ amber: [
+ 'text-amber-950 [--btn-hover-overlay:var(--color-white)]/25 [--btn-bg:var(--color-amber-400)] [--btn-border:var(--color-amber-500)]/80',
+ '[--btn-icon:var(--color-amber-600)]',
+ ],
+ yellow: [
+ 'text-yellow-950 [--btn-hover-overlay:var(--color-white)]/25 [--btn-bg:var(--color-yellow-300)] [--btn-border:var(--color-yellow-400)]/80',
+ '[--btn-icon:var(--color-yellow-600)] data-active:[--btn-icon:var(--color-yellow-700)] data-hover:[--btn-icon:var(--color-yellow-700)]',
+ ],
+ lime: [
+ 'text-lime-950 [--btn-hover-overlay:var(--color-white)]/25 [--btn-bg:var(--color-lime-300)] [--btn-border:var(--color-lime-400)]/80',
+ '[--btn-icon:var(--color-lime-600)] data-active:[--btn-icon:var(--color-lime-700)] data-hover:[--btn-icon:var(--color-lime-700)]',
+ ],
+ green: [
+ 'text-white [--btn-hover-overlay:var(--color-white)]/10 [--btn-bg:var(--color-green-600)] [--btn-border:var(--color-green-700)]/90',
+ '[--btn-icon:var(--color-white)]/60 data-active:[--btn-icon:var(--color-white)]/80 data-hover:[--btn-icon:var(--color-white)]/80',
+ ],
+ emerald: [
+ 'text-white [--btn-hover-overlay:var(--color-white)]/10 [--btn-bg:var(--color-emerald-600)] [--btn-border:var(--color-emerald-700)]/90',
+ '[--btn-icon:var(--color-white)]/60 data-active:[--btn-icon:var(--color-white)]/80 data-hover:[--btn-icon:var(--color-white)]/80',
+ ],
+ teal: [
+ 'text-white [--btn-hover-overlay:var(--color-white)]/10 [--btn-bg:var(--color-teal-600)] [--btn-border:var(--color-teal-700)]/90',
+ '[--btn-icon:var(--color-white)]/60 data-active:[--btn-icon:var(--color-white)]/80 data-hover:[--btn-icon:var(--color-white)]/80',
+ ],
+ sky: [
+ 'text-white [--btn-hover-overlay:var(--color-white)]/10 [--btn-bg:var(--color-sky-500)] [--btn-border:var(--color-sky-600)]/80',
+ '[--btn-icon:var(--color-white)]/60 data-active:[--btn-icon:var(--color-white)]/80 data-hover:[--btn-icon:var(--color-white)]/80',
+ ],
+ blue: [
+ 'text-white [--btn-hover-overlay:var(--color-white)]/10 [--btn-bg:var(--color-blue-600)] [--btn-border:var(--color-blue-700)]/90',
+ '[--btn-icon:var(--color-blue-400)] data-active:[--btn-icon:var(--color-blue-300)] data-hover:[--btn-icon:var(--color-blue-300)]',
+ ],
+ violet: [
+ 'text-white [--btn-hover-overlay:var(--color-white)]/10 [--btn-bg:var(--color-violet-500)] [--btn-border:var(--color-violet-600)]/90',
+ '[--btn-icon:var(--color-violet-300)] data-active:[--btn-icon:var(--color-violet-200)] data-hover:[--btn-icon:var(--color-violet-200)]',
+ ],
+ purple: [
+ 'text-white [--btn-hover-overlay:var(--color-white)]/10 [--btn-bg:var(--color-purple-500)] [--btn-border:var(--color-purple-600)]/90',
+ '[--btn-icon:var(--color-purple-300)] data-active:[--btn-icon:var(--color-purple-200)] data-hover:[--btn-icon:var(--color-purple-200)]',
+ ],
+ fuchsia: [
+ 'text-white [--btn-hover-overlay:var(--color-white)]/10 [--btn-bg:var(--color-fuchsia-500)] [--btn-border:var(--color-fuchsia-600)]/90',
+ '[--btn-icon:var(--color-fuchsia-300)] data-active:[--btn-icon:var(--color-fuchsia-200)] data-hover:[--btn-icon:var(--color-fuchsia-200)]',
+ ],
+ pink: [
+ 'text-white [--btn-hover-overlay:var(--color-white)]/10 [--btn-bg:var(--color-pink-500)] [--btn-border:var(--color-pink-600)]/90',
+ '[--btn-icon:var(--color-pink-300)] data-active:[--btn-icon:var(--color-pink-200)] data-hover:[--btn-icon:var(--color-pink-200)]',
+ ],
+ rose: [
+ 'text-white [--btn-hover-overlay:var(--color-white)]/10 [--btn-bg:var(--color-rose-500)] [--btn-border:var(--color-rose-600)]/90',
+ '[--btn-icon:var(--color-rose-300)] data-active:[--btn-icon:var(--color-rose-200)] data-hover:[--btn-icon:var(--color-rose-200)]',
+ ],
+ },
+}
+
+type ButtonProps = (
+ | { color?: keyof typeof styles.colors; outline?: never; plain?: never }
+ | { color?: never; outline: true; plain?: never }
+ | { color?: never; outline?: never; plain: true }
+) & { className?: string; children: React.ReactNode } & (
+ | ({ href?: never } & Omit)
+ | ({ href: string } & Omit, 'className'>)
+ )
+
+export const Button = forwardRef(function Button(
+ { color, outline, plain, className, children, ...props }: ButtonProps,
+ ref: React.ForwardedRef
+) {
+ let classes = clsx(
+ className,
+ styles.base,
+ outline ? styles.outline : plain ? styles.plain : clsx(styles.solid, styles.colors[color ?? 'dark/zinc'])
+ )
+
+ return typeof props.href === 'string' ? (
+ }>
+ {children}
+
+ ) : (
+
+ {children}
+
+ )
+})
+
+/**
+ * Expand the hit area to at least 44×44px on touch devices
+ */
+export function TouchTarget({ children }: { children: React.ReactNode }) {
+ return (
+ <>
+
+ {children}
+ >
+ )
+}
diff --git a/src/app/components/checkbox.tsx b/src/app/components/checkbox.tsx
new file mode 100644
index 0000000..64a5e80
--- /dev/null
+++ b/src/app/components/checkbox.tsx
@@ -0,0 +1,157 @@
+import * as Headless from '@headlessui/react'
+import clsx from 'clsx'
+import type React from 'react'
+
+export function CheckboxGroup({ className, ...props }: React.ComponentPropsWithoutRef<'div'>) {
+ return (
+
+ )
+}
+
+export function CheckboxField({
+ className,
+ ...props
+}: { className?: string } & Omit) {
+ return (
+
+ )
+}
+
+const base = [
+ // Basic layout
+ 'relative isolate flex size-4.5 items-center justify-center rounded-[0.3125rem] sm:size-4',
+ // Background color + shadow applied to inset pseudo element, so shadow blends with border in light mode
+ 'before:absolute before:inset-0 before:-z-10 before:rounded-[calc(0.3125rem-1px)] before:bg-white before:shadow-sm',
+ // Background color when checked
+ 'group-data-checked:before:bg-(--checkbox-checked-bg)',
+ // Background color is moved to control and shadow is removed in dark mode so hide `before` pseudo
+ 'dark:before:hidden',
+ // Background color applied to control in dark mode
+ 'dark:bg-white/5 dark:group-data-checked:bg-(--checkbox-checked-bg)',
+ // Border
+ 'border border-zinc-950/15 group-data-checked:border-transparent group-data-hover:group-data-checked:border-transparent group-data-hover:border-zinc-950/30 group-data-checked:bg-(--checkbox-checked-border)',
+ 'dark:border-white/15 dark:group-data-checked:border-white/5 dark:group-data-hover:group-data-checked:border-white/5 dark:group-data-hover:border-white/30',
+ // Inner highlight shadow
+ 'after:absolute after:inset-0 after:rounded-[calc(0.3125rem-1px)] after:shadow-[inset_0_1px_--theme(--color-white/15%)]',
+ 'dark:after:-inset-px dark:after:hidden dark:after:rounded-[0.3125rem] dark:group-data-checked:after:block',
+ // Focus ring
+ 'group-data-focus:outline-2 group-data-focus:outline-offset-2 group-data-focus:outline-blue-500',
+ // Disabled state
+ 'group-data-disabled:opacity-50',
+ 'group-data-disabled:border-zinc-950/25 group-data-disabled:bg-zinc-950/5 group-data-disabled:[--checkbox-check:var(--color-zinc-950)]/50 group-data-disabled:before:bg-transparent',
+ 'dark:group-data-disabled:border-white/20 dark:group-data-disabled:bg-white/2.5 dark:group-data-disabled:[--checkbox-check:var(--color-white)]/50 dark:group-data-checked:group-data-disabled:after:hidden',
+ // Forced colors mode
+ 'forced-colors:[--checkbox-check:HighlightText] forced-colors:[--checkbox-checked-bg:Highlight] forced-colors:group-data-disabled:[--checkbox-check:Highlight]',
+ 'dark:forced-colors:[--checkbox-check:HighlightText] dark:forced-colors:[--checkbox-checked-bg:Highlight] dark:forced-colors:group-data-disabled:[--checkbox-check:Highlight]',
+]
+
+const colors = {
+ 'dark/zinc': [
+ '[--checkbox-check:var(--color-white)] [--checkbox-checked-bg:var(--color-zinc-900)] [--checkbox-checked-border:var(--color-zinc-950)]/90',
+ 'dark:[--checkbox-checked-bg:var(--color-zinc-600)]',
+ ],
+ 'dark/white': [
+ '[--checkbox-check:var(--color-white)] [--checkbox-checked-bg:var(--color-zinc-900)] [--checkbox-checked-border:var(--color-zinc-950)]/90',
+ 'dark:[--checkbox-check:var(--color-zinc-900)] dark:[--checkbox-checked-bg:var(--color-white)] dark:[--checkbox-checked-border:var(--color-zinc-950)]/15',
+ ],
+ white:
+ '[--checkbox-check:var(--color-zinc-900)] [--checkbox-checked-bg:var(--color-white)] [--checkbox-checked-border:var(--color-zinc-950)]/15',
+ dark: '[--checkbox-check:var(--color-white)] [--checkbox-checked-bg:var(--color-zinc-900)] [--checkbox-checked-border:var(--color-zinc-950)]/90',
+ zinc: '[--checkbox-check:var(--color-white)] [--checkbox-checked-bg:var(--color-zinc-600)] [--checkbox-checked-border:var(--color-zinc-700)]/90',
+ red: '[--checkbox-check:var(--color-white)] [--checkbox-checked-bg:var(--color-red-600)] [--checkbox-checked-border:var(--color-red-700)]/90',
+ orange:
+ '[--checkbox-check:var(--color-white)] [--checkbox-checked-bg:var(--color-orange-500)] [--checkbox-checked-border:var(--color-orange-600)]/90',
+ amber:
+ '[--checkbox-check:var(--color-amber-950)] [--checkbox-checked-bg:var(--color-amber-400)] [--checkbox-checked-border:var(--color-amber-500)]/80',
+ yellow:
+ '[--checkbox-check:var(--color-yellow-950)] [--checkbox-checked-bg:var(--color-yellow-300)] [--checkbox-checked-border:var(--color-yellow-400)]/80',
+ lime: '[--checkbox-check:var(--color-lime-950)] [--checkbox-checked-bg:var(--color-lime-300)] [--checkbox-checked-border:var(--color-lime-400)]/80',
+ green:
+ '[--checkbox-check:var(--color-white)] [--checkbox-checked-bg:var(--color-green-600)] [--checkbox-checked-border:var(--color-green-700)]/90',
+ emerald:
+ '[--checkbox-check:var(--color-white)] [--checkbox-checked-bg:var(--color-emerald-600)] [--checkbox-checked-border:var(--color-emerald-700)]/90',
+ teal: '[--checkbox-check:var(--color-white)] [--checkbox-checked-bg:var(--color-teal-600)] [--checkbox-checked-border:var(--color-teal-700)]/90',
+ cyan: '[--checkbox-check:var(--color-cyan-950)] [--checkbox-checked-bg:var(--color-cyan-300)] [--checkbox-checked-border:var(--color-cyan-400)]/80',
+ sky: '[--checkbox-check:var(--color-white)] [--checkbox-checked-bg:var(--color-sky-500)] [--checkbox-checked-border:var(--color-sky-600)]/80',
+ blue: '[--checkbox-check:var(--color-white)] [--checkbox-checked-bg:var(--color-blue-600)] [--checkbox-checked-border:var(--color-blue-700)]/90',
+ indigo:
+ '[--checkbox-check:var(--color-white)] [--checkbox-checked-bg:var(--color-indigo-500)] [--checkbox-checked-border:var(--color-indigo-600)]/90',
+ violet:
+ '[--checkbox-check:var(--color-white)] [--checkbox-checked-bg:var(--color-violet-500)] [--checkbox-checked-border:var(--color-violet-600)]/90',
+ purple:
+ '[--checkbox-check:var(--color-white)] [--checkbox-checked-bg:var(--color-purple-500)] [--checkbox-checked-border:var(--color-purple-600)]/90',
+ fuchsia:
+ '[--checkbox-check:var(--color-white)] [--checkbox-checked-bg:var(--color-fuchsia-500)] [--checkbox-checked-border:var(--color-fuchsia-600)]/90',
+ pink: '[--checkbox-check:var(--color-white)] [--checkbox-checked-bg:var(--color-pink-500)] [--checkbox-checked-border:var(--color-pink-600)]/90',
+ rose: '[--checkbox-check:var(--color-white)] [--checkbox-checked-bg:var(--color-rose-500)] [--checkbox-checked-border:var(--color-rose-600)]/90',
+}
+
+type Color = keyof typeof colors
+
+export function Checkbox({
+ color = 'dark/zinc',
+ className,
+ ...props
+}: {
+ color?: Color
+ className?: string
+} & Omit) {
+ return (
+
+
+
+
+
+ )
+}
diff --git a/src/app/components/combobox.tsx b/src/app/components/combobox.tsx
new file mode 100644
index 0000000..bf649b4
--- /dev/null
+++ b/src/app/components/combobox.tsx
@@ -0,0 +1,188 @@
+'use client'
+
+import * as Headless from '@headlessui/react'
+import clsx from 'clsx'
+import { useState } from 'react'
+
+export function Combobox({
+ options,
+ displayValue,
+ filter,
+ anchor = 'bottom',
+ className,
+ placeholder,
+ autoFocus,
+ 'aria-label': ariaLabel,
+ children,
+ ...props
+}: {
+ options: T[]
+ displayValue: (value: T | null) => string | undefined
+ filter?: (value: T, query: string) => boolean
+ className?: string
+ placeholder?: string
+ autoFocus?: boolean
+ 'aria-label'?: string
+ children: (value: NonNullable) => React.ReactElement
+} & Omit, 'as' | 'multiple' | 'children'> & { anchor?: 'top' | 'bottom' }) {
+ const [query, setQuery] = useState('')
+
+ const filteredOptions =
+ query === ''
+ ? options
+ : options.filter((option) =>
+ filter ? filter(option, query) : displayValue(option)?.toLowerCase().includes(query.toLowerCase())
+ )
+
+ return (
+ setQuery('')}>
+
+ displayValue(option) ?? ''}
+ onChange={(event) => setQuery(event.target.value)}
+ placeholder={placeholder}
+ className={clsx([
+ className,
+ // Basic layout
+ 'relative block w-full appearance-none rounded-lg py-[calc(--spacing(2.5)-1px)] sm:py-[calc(--spacing(1.5)-1px)]',
+ // Horizontal padding
+ 'pr-[calc(--spacing(10)-1px)] pl-[calc(--spacing(3.5)-1px)] sm:pr-[calc(--spacing(9)-1px)] sm:pl-[calc(--spacing(3)-1px)]',
+ // Typography
+ 'text-base/6 text-zinc-950 placeholder:text-zinc-500 sm:text-sm/6 dark:text-white',
+ // Border
+ 'border border-zinc-950/10 data-hover:border-zinc-950/20 dark:border-white/10 dark:data-hover:border-white/20',
+ // Background color
+ 'bg-transparent dark:bg-white/5',
+ // Hide default focus styles
+ 'focus:outline-hidden',
+ // Invalid state
+ 'data-invalid:border-red-500 data-invalid:data-hover:border-red-500 dark:data-invalid:border-red-500 dark:data-invalid:data-hover:border-red-500',
+ // Disabled state
+ 'data-disabled:border-zinc-950/20 dark:data-disabled:border-white/15 dark:data-disabled:bg-white/2.5 dark:data-hover:data-disabled:border-white/15',
+ // System icons
+ 'dark:scheme-dark',
+ ])}
+ />
+
+
+
+
+
+ {({ option }) => children(option)}
+
+
+ )
+}
+
+export function ComboboxOption({
+ children,
+ className,
+ ...props
+}: { className?: string; children?: React.ReactNode } & Omit<
+ Headless.ComboboxOptionProps<'div', T>,
+ 'as' | 'className'
+>) {
+ let sharedClasses = clsx(
+ // Base
+ 'flex min-w-0 items-center',
+ // Icons
+ '*:data-[slot=icon]:size-5 *:data-[slot=icon]:shrink-0 sm:*:data-[slot=icon]:size-4',
+ '*:data-[slot=icon]:text-zinc-500 group-data-focus/option:*:data-[slot=icon]:text-white dark:*:data-[slot=icon]:text-zinc-400',
+ 'forced-colors:*:data-[slot=icon]:text-[CanvasText] forced-colors:group-data-focus/option:*:data-[slot=icon]:text-[Canvas]',
+ // Avatars
+ '*:data-[slot=avatar]:-mx-0.5 *:data-[slot=avatar]:size-6 sm:*:data-[slot=avatar]:size-5'
+ )
+
+ return (
+
+ {children}
+
+
+ )
+}
+
+export function ComboboxLabel({ className, ...props }: React.ComponentPropsWithoutRef<'span'>) {
+ return
+}
+
+export function ComboboxDescription({ className, children, ...props }: React.ComponentPropsWithoutRef<'span'>) {
+ return (
+
+ {children}
+
+ )
+}
diff --git a/src/app/components/description-list.tsx b/src/app/components/description-list.tsx
new file mode 100644
index 0000000..b19b3b5
--- /dev/null
+++ b/src/app/components/description-list.tsx
@@ -0,0 +1,37 @@
+import clsx from 'clsx'
+
+export function DescriptionList({ className, ...props }: React.ComponentPropsWithoutRef<'dl'>) {
+ return (
+
+ )
+}
+
+export function DescriptionTerm({ className, ...props }: React.ComponentPropsWithoutRef<'dt'>) {
+ return (
+
+ )
+}
+
+export function DescriptionDetails({ className, ...props }: React.ComponentPropsWithoutRef<'dd'>) {
+ return (
+
+ )
+}
diff --git a/src/app/components/dialog.tsx b/src/app/components/dialog.tsx
new file mode 100644
index 0000000..eb55aa3
--- /dev/null
+++ b/src/app/components/dialog.tsx
@@ -0,0 +1,86 @@
+import * as Headless from '@headlessui/react'
+import clsx from 'clsx'
+import type React from 'react'
+import { Text } from './text'
+
+const sizes = {
+ xs: 'sm:max-w-xs',
+ sm: 'sm:max-w-sm',
+ md: 'sm:max-w-md',
+ lg: 'sm:max-w-lg',
+ xl: 'sm:max-w-xl',
+ '2xl': 'sm:max-w-2xl',
+ '3xl': 'sm:max-w-3xl',
+ '4xl': 'sm:max-w-4xl',
+ '5xl': 'sm:max-w-5xl',
+}
+
+export function Dialog({
+ size = 'lg',
+ className,
+ children,
+ ...props
+}: { size?: keyof typeof sizes; className?: string; children: React.ReactNode } & Omit<
+ Headless.DialogProps,
+ 'as' | 'className'
+>) {
+ return (
+
+
+
+
+
+ )
+}
+
+export function DialogTitle({
+ className,
+ ...props
+}: { className?: string } & Omit) {
+ return (
+
+ )
+}
+
+export function DialogDescription({
+ className,
+ ...props
+}: { className?: string } & Omit, 'as' | 'className'>) {
+ return
+}
+
+export function DialogBody({ className, ...props }: React.ComponentPropsWithoutRef<'div'>) {
+ return
+}
+
+export function DialogActions({ className, ...props }: React.ComponentPropsWithoutRef<'div'>) {
+ return (
+
+ )
+}
diff --git a/src/app/components/divider.tsx b/src/app/components/divider.tsx
new file mode 100644
index 0000000..dd84b10
--- /dev/null
+++ b/src/app/components/divider.tsx
@@ -0,0 +1,20 @@
+import clsx from 'clsx'
+
+export function Divider({
+ soft = false,
+ className,
+ ...props
+}: { soft?: boolean } & React.ComponentPropsWithoutRef<'hr'>) {
+ return (
+
+ )
+}
diff --git a/src/app/components/dropdown.tsx b/src/app/components/dropdown.tsx
new file mode 100644
index 0000000..ad96c15
--- /dev/null
+++ b/src/app/components/dropdown.tsx
@@ -0,0 +1,183 @@
+'use client'
+
+import * as Headless from '@headlessui/react'
+import clsx from 'clsx'
+import type React from 'react'
+import { Button } from './button'
+import { Link } from './link'
+
+export function Dropdown(props: Headless.MenuProps) {
+ return
+}
+
+export function DropdownButton({
+ as = Button,
+ ...props
+}: { className?: string } & Omit, 'className'>) {
+ return
+}
+
+export function DropdownMenu({
+ anchor = 'bottom',
+ className,
+ ...props
+}: { className?: string } & Omit) {
+ return (
+
+ )
+}
+
+export function DropdownItem({
+ className,
+ ...props
+}: { className?: string } & (
+ | ({ href?: never } & Omit, 'as' | 'className'>)
+ | ({ href: string } & Omit, 'as' | 'className'>)
+)) {
+ let classes = clsx(
+ className,
+ // Base styles
+ 'group cursor-default rounded-lg px-3.5 py-2.5 focus:outline-hidden sm:px-3 sm:py-1.5',
+ // Text styles
+ 'text-left text-base/6 text-zinc-950 sm:text-sm/6 dark:text-white forced-colors:text-[CanvasText]',
+ // Focus
+ 'data-focus:bg-blue-500 data-focus:text-white',
+ // Disabled state
+ 'data-disabled:opacity-50',
+ // Forced colors mode
+ 'forced-color-adjust-none forced-colors:data-focus:bg-[Highlight] forced-colors:data-focus:text-[HighlightText] forced-colors:data-focus:*:data-[slot=icon]:text-[HighlightText]',
+ // Use subgrid when available but fallback to an explicit grid layout if not
+ 'col-span-full grid grid-cols-[auto_1fr_1.5rem_0.5rem_auto] items-center supports-[grid-template-columns:subgrid]:grid-cols-subgrid',
+ // Icons
+ '*:data-[slot=icon]:col-start-1 *:data-[slot=icon]:row-start-1 *:data-[slot=icon]:mr-2.5 *:data-[slot=icon]:-ml-0.5 *:data-[slot=icon]:size-5 sm:*:data-[slot=icon]:mr-2 sm:*:data-[slot=icon]:size-4',
+ '*:data-[slot=icon]:text-zinc-500 data-focus:*:data-[slot=icon]:text-white dark:*:data-[slot=icon]:text-zinc-400 dark:data-focus:*:data-[slot=icon]:text-white',
+ // Avatar
+ '*:data-[slot=avatar]:mr-2.5 *:data-[slot=avatar]:-ml-1 *:data-[slot=avatar]:size-6 sm:*:data-[slot=avatar]:mr-2 sm:*:data-[slot=avatar]:size-5'
+ )
+
+ return typeof props.href === 'string' ? (
+
+ ) : (
+
+ )
+}
+
+export function DropdownHeader({ className, ...props }: React.ComponentPropsWithoutRef<'div'>) {
+ return
+}
+
+export function DropdownSection({
+ className,
+ ...props
+}: { className?: string } & Omit) {
+ return (
+
+ )
+}
+
+export function DropdownHeading({
+ className,
+ ...props
+}: { className?: string } & Omit) {
+ return (
+
+ )
+}
+
+export function DropdownDivider({
+ className,
+ ...props
+}: { className?: string } & Omit) {
+ return (
+
+ )
+}
+
+export function DropdownLabel({ className, ...props }: React.ComponentPropsWithoutRef<'div'>) {
+ return
+}
+
+export function DropdownDescription({
+ className,
+ ...props
+}: { className?: string } & Omit) {
+ return (
+
+ )
+}
+
+export function DropdownShortcut({
+ keys,
+ className,
+ ...props
+}: { keys: string | string[]; className?: string } & Omit, 'as' | 'className'>) {
+ return (
+
+ {(Array.isArray(keys) ? keys : keys.split('')).map((char, index) => (
+ 0 && char.length > 1 && 'pl-1',
+ ])}
+ >
+ {char}
+
+ ))}
+
+ )
+}
diff --git a/src/app/components/fieldset.tsx b/src/app/components/fieldset.tsx
new file mode 100644
index 0000000..155ca20
--- /dev/null
+++ b/src/app/components/fieldset.tsx
@@ -0,0 +1,91 @@
+import * as Headless from '@headlessui/react'
+import clsx from 'clsx'
+import type React from 'react'
+
+export function Fieldset({
+ className,
+ ...props
+}: { className?: string } & Omit) {
+ return (
+ *+[data-slot=control]]:mt-6')}
+ />
+ )
+}
+
+export function Legend({
+ className,
+ ...props
+}: { className?: string } & Omit) {
+ return (
+
+ )
+}
+
+export function FieldGroup({ className, ...props }: React.ComponentPropsWithoutRef<'div'>) {
+ return
+}
+
+export function Field({ className, ...props }: { className?: string } & Omit) {
+ return (
+ [data-slot=label]+[data-slot=control]]:mt-3',
+ '[&>[data-slot=label]+[data-slot=description]]:mt-1',
+ '[&>[data-slot=description]+[data-slot=control]]:mt-3',
+ '[&>[data-slot=control]+[data-slot=description]]:mt-3',
+ '[&>[data-slot=control]+[data-slot=error]]:mt-3',
+ '*:data-[slot=label]:font-medium'
+ )}
+ />
+ )
+}
+
+export function Label({ className, ...props }: { className?: string } & Omit) {
+ return (
+
+ )
+}
+
+export function Description({
+ className,
+ ...props
+}: { className?: string } & Omit) {
+ return (
+
+ )
+}
+
+export function ErrorMessage({
+ className,
+ ...props
+}: { className?: string } & Omit) {
+ return (
+
+ )
+}
diff --git a/src/app/components/heading.tsx b/src/app/components/heading.tsx
new file mode 100644
index 0000000..5ccc9d1
--- /dev/null
+++ b/src/app/components/heading.tsx
@@ -0,0 +1,27 @@
+import clsx from 'clsx'
+
+type HeadingProps = { level?: 1 | 2 | 3 | 4 | 5 | 6 } & React.ComponentPropsWithoutRef<
+ 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6'
+>
+
+export function Heading({ className, level = 1, ...props }: HeadingProps) {
+ let Element: `h${typeof level}` = `h${level}`
+
+ return (
+
+ )
+}
+
+export function Subheading({ className, level = 2, ...props }: HeadingProps) {
+ let Element: `h${typeof level}` = `h${level}`
+
+ return (
+
+ )
+}
diff --git a/src/app/components/input.tsx b/src/app/components/input.tsx
new file mode 100644
index 0000000..72816fb
--- /dev/null
+++ b/src/app/components/input.tsx
@@ -0,0 +1,92 @@
+import * as Headless from '@headlessui/react'
+import clsx from 'clsx'
+import React, { forwardRef } from 'react'
+
+export function InputGroup({ children }: React.ComponentPropsWithoutRef<'span'>) {
+ return (
+ [data-slot=icon]:first-child]:left-3 sm:[&>[data-slot=icon]:first-child]:left-2.5 [&>[data-slot=icon]:last-child]:right-3 sm:[&>[data-slot=icon]:last-child]:right-2.5',
+ '*:data-[slot=icon]:text-zinc-500 dark:*:data-[slot=icon]:text-zinc-400'
+ )}
+ >
+ {children}
+
+ )
+}
+
+const dateTypes = ['date', 'datetime-local', 'month', 'time', 'week']
+type DateType = (typeof dateTypes)[number]
+
+export const Input = forwardRef(function Input(
+ {
+ className,
+ ...props
+ }: {
+ className?: string
+ type?: 'email' | 'number' | 'password' | 'search' | 'tel' | 'text' | 'url' | DateType
+ } & Omit,
+ ref: React.ForwardedRef
+) {
+ return (
+
+
+
+ )
+})
diff --git a/src/app/components/link.tsx b/src/app/components/link.tsx
new file mode 100644
index 0000000..2d0bb47
--- /dev/null
+++ b/src/app/components/link.tsx
@@ -0,0 +1,14 @@
+import * as Headless from '@headlessui/react'
+import NextLink, { type LinkProps } from 'next/link'
+import React, { forwardRef } from 'react'
+
+export const Link = forwardRef(function Link(
+ props: LinkProps & React.ComponentPropsWithoutRef<'a'>,
+ ref: React.ForwardedRef
+) {
+ return (
+
+
+
+ )
+})
\ No newline at end of file
diff --git a/src/app/components/listbox.tsx b/src/app/components/listbox.tsx
new file mode 100644
index 0000000..ecd0a44
--- /dev/null
+++ b/src/app/components/listbox.tsx
@@ -0,0 +1,177 @@
+'use client'
+
+import * as Headless from '@headlessui/react'
+import clsx from 'clsx'
+import { Fragment } from 'react'
+
+export function Listbox({
+ className,
+ placeholder,
+ autoFocus,
+ 'aria-label': ariaLabel,
+ children: options,
+ ...props
+}: {
+ className?: string
+ placeholder?: React.ReactNode
+ autoFocus?: boolean
+ 'aria-label'?: string
+ children?: React.ReactNode
+} & Omit, 'as' | 'multiple'>) {
+ return (
+
+
+ {placeholder}}
+ className={clsx([
+ // Basic layout
+ 'relative block w-full appearance-none rounded-lg py-[calc(--spacing(2.5)-1px)] sm:py-[calc(--spacing(1.5)-1px)]',
+ // Set minimum height for when no value is selected
+ 'min-h-11 sm:min-h-9',
+ // Horizontal padding
+ 'pr-[calc(--spacing(7)-1px)] pl-[calc(--spacing(3.5)-1px)] sm:pl-[calc(--spacing(3)-1px)]',
+ // Typography
+ 'text-left text-base/6 text-zinc-950 placeholder:text-zinc-500 sm:text-sm/6 dark:text-white forced-colors:text-[CanvasText]',
+ // Border
+ 'border border-zinc-950/10 group-data-active:border-zinc-950/20 group-data-hover:border-zinc-950/20 dark:border-white/10 dark:group-data-active:border-white/20 dark:group-data-hover:border-white/20',
+ // Background color
+ 'bg-transparent dark:bg-white/5',
+ // Invalid state
+ 'group-data-invalid:border-red-500 group-data-hover:group-data-invalid:border-red-500 dark:group-data-invalid:border-red-600 dark:data-hover:group-data-invalid:border-red-600',
+ // Disabled state
+ 'group-data-disabled:border-zinc-950/20 group-data-disabled:opacity-100 dark:group-data-disabled:border-white/15 dark:group-data-disabled:bg-white/2.5 dark:group-data-disabled:data-hover:border-white/15',
+ ])}
+ />
+
+
+
+
+
+ {options}
+
+
+ )
+}
+
+export function ListboxOption({
+ children,
+ className,
+ ...props
+}: { className?: string; children?: React.ReactNode } & Omit<
+ Headless.ListboxOptionProps<'div', T>,
+ 'as' | 'className'
+>) {
+ let sharedClasses = clsx(
+ // Base
+ 'flex min-w-0 items-center',
+ // Icons
+ '*:data-[slot=icon]:size-5 *:data-[slot=icon]:shrink-0 sm:*:data-[slot=icon]:size-4',
+ '*:data-[slot=icon]:text-zinc-500 group-data-focus/option:*:data-[slot=icon]:text-white dark:*:data-[slot=icon]:text-zinc-400',
+ 'forced-colors:*:data-[slot=icon]:text-[CanvasText] forced-colors:group-data-focus/option:*:data-[slot=icon]:text-[Canvas]',
+ // Avatars
+ '*:data-[slot=avatar]:-mx-0.5 *:data-[slot=avatar]:size-6 sm:*:data-[slot=avatar]:size-5'
+ )
+
+ return (
+
+ {({ selectedOption }) => {
+ if (selectedOption) {
+ return {children}
+ }
+
+ return (
+
+ )
+ }}
+
+ )
+}
+
+export function ListboxLabel({ className, ...props }: React.ComponentPropsWithoutRef<'span'>) {
+ return
+}
+
+export function ListboxDescription({ className, children, ...props }: React.ComponentPropsWithoutRef<'span'>) {
+ return (
+
+ {children}
+
+ )
+}
diff --git a/src/app/components/nav/Header.tsx b/src/app/components/nav/Header.tsx
new file mode 100644
index 0000000..23d097c
--- /dev/null
+++ b/src/app/components/nav/Header.tsx
@@ -0,0 +1,338 @@
+'use client'
+
+import { useState } from 'react'
+import { useRouter } from 'next/navigation';
+import Image from 'next/image';
+import {
+ Dialog,
+ DialogPanel,
+ Disclosure,
+ DisclosureButton,
+ DisclosurePanel,
+ Popover,
+ PopoverButton,
+ PopoverGroup,
+ PopoverPanel,
+} from '@headlessui/react'
+import {
+ Bars3Icon,
+ ShoppingBagIcon,
+ UsersIcon,
+ HomeIcon,
+ UserCircleIcon,
+ XMarkIcon,
+ ArrowRightOnRectangleIcon
+} from '@heroicons/react/24/outline'
+import { ChevronDownIcon } from '@heroicons/react/20/solid'
+import useAuthStore from '../../store/authStore';
+import { Avatar } from '../avatar';
+import LanguageSwitcher from '../LanguageSwitcher';
+
+const products = [
+ {
+ name: 'Shop',
+ description: 'Browse our exclusive product catalog',
+ href: '/shop',
+ icon: ShoppingBagIcon,
+ },
+ {
+ name: 'Community',
+ description: 'Connect with other members',
+ href: '/community',
+ icon: UsersIcon,
+ },
+ {
+ name: 'Dashboard',
+ description: 'Manage your account and activities',
+ href: '/dashboard',
+ icon: HomeIcon
+ },
+]
+
+export default function Header() {
+ const [mobileMenuOpen, setMobileMenuOpen] = useState(false)
+ const user = useAuthStore((s) => s.user);
+ const logout = useAuthStore((s) => s.logout);
+ const router = useRouter();
+
+ const handleLogout = async () => {
+ try {
+ await logout();
+ router.push('/login');
+ } catch (err) {
+ console.error('Logout failed:', err);
+ }
+ };
+
+ // Helper to get user initials for profile icon
+ const getUserInitials = () => {
+ if (!user) return 'U';
+ if (user.firstName || user.lastName) {
+ return (
+ (user.firstName?.[0] || '') +
+ (user.lastName?.[0] || '')
+ ).toUpperCase();
+ }
+ if (user.email) {
+ return user.email[0].toUpperCase();
+ }
+ return 'U';
+ };
+
+ return (
+
+ )
+}
\ No newline at end of file
diff --git a/src/app/components/navbar.tsx b/src/app/components/navbar.tsx
new file mode 100644
index 0000000..a1dfa6a
--- /dev/null
+++ b/src/app/components/navbar.tsx
@@ -0,0 +1,96 @@
+'use client'
+
+import * as Headless from '@headlessui/react'
+import clsx from 'clsx'
+import { LayoutGroup, motion } from 'motion/react'
+import React, { forwardRef, useId } from 'react'
+import { TouchTarget } from './button'
+import { Link } from './link'
+
+export function Navbar({ className, ...props }: React.ComponentPropsWithoutRef<'nav'>) {
+ return
+}
+
+export function NavbarDivider({ className, ...props }: React.ComponentPropsWithoutRef<'div'>) {
+ return
+}
+
+export function NavbarSection({ className, ...props }: React.ComponentPropsWithoutRef<'div'>) {
+ let id = useId()
+
+ return (
+
+
+
+ )
+}
+
+export function NavbarSpacer({ className, ...props }: React.ComponentPropsWithoutRef<'div'>) {
+ return
+}
+
+export const NavbarItem = forwardRef(function NavbarItem(
+ {
+ current,
+ className,
+ children,
+ ...props
+ }: { current?: boolean; className?: string; children: React.ReactNode } & (
+ | ({ href?: never } & Omit)
+ | ({ href: string } & Omit, 'className'>)
+ ),
+ ref: React.ForwardedRef
+) {
+ let classes = clsx(
+ // Base
+ 'relative flex min-w-0 items-center gap-3 rounded-lg p-2 text-left text-base/6 font-medium text-zinc-950 sm:text-sm/5',
+ // Leading icon/icon-only
+ '*:data-[slot=icon]:size-6 *:data-[slot=icon]:shrink-0 *:data-[slot=icon]:fill-zinc-500 sm:*:data-[slot=icon]:size-5',
+ // Trailing icon (down chevron or similar)
+ '*:not-nth-2:last:data-[slot=icon]:ml-auto *:not-nth-2:last:data-[slot=icon]:size-5 sm:*:not-nth-2:last:data-[slot=icon]:size-4',
+ // Avatar
+ '*:data-[slot=avatar]:-m-0.5 *:data-[slot=avatar]:size-7 *:data-[slot=avatar]:[--avatar-radius:var(--radius-md)] sm:*:data-[slot=avatar]:size-6',
+ // Hover
+ 'data-hover:bg-zinc-950/5 data-hover:*:data-[slot=icon]:fill-zinc-950',
+ // Active
+ 'data-active:bg-zinc-950/5 data-active:*:data-[slot=icon]:fill-zinc-950',
+ // Dark mode
+ 'dark:text-white dark:*:data-[slot=icon]:fill-zinc-400',
+ 'dark:data-hover:bg-white/5 dark:data-hover:*:data-[slot=icon]:fill-white',
+ 'dark:data-active:bg-white/5 dark:data-active:*:data-[slot=icon]:fill-white'
+ )
+
+ return (
+
+ {current && (
+
+ )}
+ {typeof props.href === 'string' ? (
+ }
+ >
+ {children}
+
+ ) : (
+
+ {children}
+
+ )}
+
+ )
+})
+
+export function NavbarLabel({ className, ...props }: React.ComponentPropsWithoutRef<'span'>) {
+ return
+}
diff --git a/src/app/components/pagination.tsx b/src/app/components/pagination.tsx
new file mode 100644
index 0000000..540224f
--- /dev/null
+++ b/src/app/components/pagination.tsx
@@ -0,0 +1,98 @@
+import clsx from 'clsx'
+import type React from 'react'
+import { Button } from './button'
+
+export function Pagination({
+ 'aria-label': ariaLabel = 'Page navigation',
+ className,
+ ...props
+}: React.ComponentPropsWithoutRef<'nav'>) {
+ return
+}
+
+export function PaginationPrevious({
+ href = null,
+ className,
+ children = 'Previous',
+}: React.PropsWithChildren<{ href?: string | null; className?: string }>) {
+ return (
+
+
+
+ )
+}
+
+export function PaginationNext({
+ href = null,
+ className,
+ children = 'Next',
+}: React.PropsWithChildren<{ href?: string | null; className?: string }>) {
+ return (
+
+
+
+ )
+}
+
+export function PaginationList({ className, ...props }: React.ComponentPropsWithoutRef<'span'>) {
+ return
+}
+
+export function PaginationPage({
+ href,
+ className,
+ current = false,
+ children,
+}: React.PropsWithChildren<{ href: string; className?: string; current?: boolean }>) {
+ return (
+
+ )
+}
+
+export function PaginationGap({
+ className,
+ children = <>…>,
+ ...props
+}: React.ComponentPropsWithoutRef<'span'>) {
+ return (
+
+ {children}
+
+ )
+}
diff --git a/src/app/components/radio.tsx b/src/app/components/radio.tsx
new file mode 100644
index 0000000..c06307b
--- /dev/null
+++ b/src/app/components/radio.tsx
@@ -0,0 +1,142 @@
+import * as Headless from '@headlessui/react'
+import clsx from 'clsx'
+
+export function RadioGroup({
+ className,
+ ...props
+}: { className?: string } & Omit) {
+ return (
+
+ )
+}
+
+export function RadioField({
+ className,
+ ...props
+}: { className?: string } & Omit) {
+ return (
+
+ )
+}
+
+const base = [
+ // Basic layout
+ 'relative isolate flex size-4.75 shrink-0 rounded-full sm:size-4.25',
+ // Background color + shadow applied to inset pseudo element, so shadow blends with border in light mode
+ 'before:absolute before:inset-0 before:-z-10 before:rounded-full before:bg-white before:shadow-sm',
+ // Background color when checked
+ 'group-data-checked:before:bg-(--radio-checked-bg)',
+ // Background color is moved to control and shadow is removed in dark mode so hide `before` pseudo
+ 'dark:before:hidden',
+ // Background color applied to control in dark mode
+ 'dark:bg-white/5 dark:group-data-checked:bg-(--radio-checked-bg)',
+ // Border
+ 'border border-zinc-950/15 group-data-checked:border-transparent group-data-hover:group-data-checked:border-transparent group-data-hover:border-zinc-950/30 group-data-checked:bg-(--radio-checked-border)',
+ 'dark:border-white/15 dark:group-data-checked:border-white/5 dark:group-data-hover:group-data-checked:border-white/5 dark:group-data-hover:border-white/30',
+ // Inner highlight shadow
+ 'after:absolute after:inset-0 after:rounded-full after:shadow-[inset_0_1px_--theme(--color-white/15%)]',
+ 'dark:after:-inset-px dark:after:hidden dark:after:rounded-full dark:group-data-checked:after:block',
+ // Indicator color (light mode)
+ '[--radio-indicator:transparent] group-data-checked:[--radio-indicator:var(--radio-checked-indicator)] group-data-hover:group-data-checked:[--radio-indicator:var(--radio-checked-indicator)] group-data-hover:[--radio-indicator:var(--color-zinc-900)]/10',
+ // Indicator color (dark mode)
+ 'dark:group-data-hover:group-data-checked:[--radio-indicator:var(--radio-checked-indicator)] dark:group-data-hover:[--radio-indicator:var(--color-zinc-700)]',
+ // Focus ring
+ 'group-data-focus:outline group-data-focus:outline-2 group-data-focus:outline-offset-2 group-data-focus:outline-blue-500',
+ // Disabled state
+ 'group-data-disabled:opacity-50',
+ 'group-data-disabled:border-zinc-950/25 group-data-disabled:bg-zinc-950/5 group-data-disabled:[--radio-checked-indicator:var(--color-zinc-950)]/50 group-data-disabled:before:bg-transparent',
+ 'dark:group-data-disabled:border-white/20 dark:group-data-disabled:bg-white/2.5 dark:group-data-disabled:[--radio-checked-indicator:var(--color-white)]/50 dark:group-data-checked:group-data-disabled:after:hidden',
+]
+
+const colors = {
+ 'dark/zinc': [
+ '[--radio-checked-bg:var(--color-zinc-900)] [--radio-checked-border:var(--color-zinc-950)]/90 [--radio-checked-indicator:var(--color-white)]',
+ 'dark:[--radio-checked-bg:var(--color-zinc-600)]',
+ ],
+ 'dark/white': [
+ '[--radio-checked-bg:var(--color-zinc-900)] [--radio-checked-border:var(--color-zinc-950)]/90 [--radio-checked-indicator:var(--color-white)]',
+ 'dark:[--radio-checked-bg:var(--color-white)] dark:[--radio-checked-border:var(--color-zinc-950)]/15 dark:[--radio-checked-indicator:var(--color-zinc-900)]',
+ ],
+ white:
+ '[--radio-checked-bg:var(--color-white)] [--radio-checked-border:var(--color-zinc-950)]/15 [--radio-checked-indicator:var(--color-zinc-900)]',
+ dark: '[--radio-checked-bg:var(--color-zinc-900)] [--radio-checked-border:var(--color-zinc-950)]/90 [--radio-checked-indicator:var(--color-white)]',
+ zinc: '[--radio-checked-indicator:var(--color-white)] [--radio-checked-bg:var(--color-zinc-600)] [--radio-checked-border:var(--color-zinc-700)]/90',
+ red: '[--radio-checked-indicator:var(--color-white)] [--radio-checked-bg:var(--color-red-600)] [--radio-checked-border:var(--color-red-700)]/90',
+ orange:
+ '[--radio-checked-indicator:var(--color-white)] [--radio-checked-bg:var(--color-orange-500)] [--radio-checked-border:var(--color-orange-600)]/90',
+ amber:
+ '[--radio-checked-bg:var(--color-amber-400)] [--radio-checked-border:var(--color-amber-500)]/80 [--radio-checked-indicator:var(--color-amber-950)]',
+ yellow:
+ '[--radio-checked-bg:var(--color-yellow-300)] [--radio-checked-border:var(--color-yellow-400)]/80 [--radio-checked-indicator:var(--color-yellow-950)]',
+ lime: '[--radio-checked-bg:var(--color-lime-300)] [--radio-checked-border:var(--color-lime-400)]/80 [--radio-checked-indicator:var(--color-lime-950)]',
+ green:
+ '[--radio-checked-indicator:var(--color-white)] [--radio-checked-bg:var(--color-green-600)] [--radio-checked-border:var(--color-green-700)]/90',
+ emerald:
+ '[--radio-checked-indicator:var(--color-white)] [--radio-checked-bg:var(--color-emerald-600)] [--radio-checked-border:var(--color-emerald-700)]/90',
+ teal: '[--radio-checked-indicator:var(--color-white)] [--radio-checked-bg:var(--color-teal-600)] [--radio-checked-border:var(--color-teal-700)]/90',
+ cyan: '[--radio-checked-bg:var(--color-cyan-300)] [--radio-checked-border:var(--color-cyan-400)]/80 [--radio-checked-indicator:var(--color-cyan-950)]',
+ sky: '[--radio-checked-indicator:var(--color-white)] [--radio-checked-bg:var(--color-sky-500)] [--radio-checked-border:var(--color-sky-600)]/80',
+ blue: '[--radio-checked-indicator:var(--color-white)] [--radio-checked-bg:var(--color-blue-600)] [--radio-checked-border:var(--color-blue-700)]/90',
+ indigo:
+ '[--radio-checked-indicator:var(--color-white)] [--radio-checked-bg:var(--color-indigo-500)] [--radio-checked-border:var(--color-indigo-600)]/90',
+ violet:
+ '[--radio-checked-indicator:var(--color-white)] [--radio-checked-bg:var(--color-violet-500)] [--radio-checked-border:var(--color-violet-600)]/90',
+ purple:
+ '[--radio-checked-indicator:var(--color-white)] [--radio-checked-bg:var(--color-purple-500)] [--radio-checked-border:var(--color-purple-600)]/90',
+ fuchsia:
+ '[--radio-checked-indicator:var(--color-white)] [--radio-checked-bg:var(--color-fuchsia-500)] [--radio-checked-border:var(--color-fuchsia-600)]/90',
+ pink: '[--radio-checked-indicator:var(--color-white)] [--radio-checked-bg:var(--color-pink-500)] [--radio-checked-border:var(--color-pink-600)]/90',
+ rose: '[--radio-checked-indicator:var(--color-white)] [--radio-checked-bg:var(--color-rose-500)] [--radio-checked-border:var(--color-rose-600)]/90',
+}
+
+type Color = keyof typeof colors
+
+export function Radio({
+ color = 'dark/zinc',
+ className,
+ ...props
+}: { color?: Color; className?: string } & Omit) {
+ return (
+
+
+
+
+
+ )
+}
diff --git a/src/app/components/select.tsx b/src/app/components/select.tsx
new file mode 100644
index 0000000..ffda600
--- /dev/null
+++ b/src/app/components/select.tsx
@@ -0,0 +1,68 @@
+import * as Headless from '@headlessui/react'
+import clsx from 'clsx'
+import React, { forwardRef } from 'react'
+
+export const Select = forwardRef(function Select(
+ { className, multiple, ...props }: { className?: string } & Omit,
+ ref: React.ForwardedRef
+) {
+ return (
+
+
+ {!multiple && (
+
+
+
+ )}
+
+ )
+})
diff --git a/src/app/components/sidebar-layout.tsx b/src/app/components/sidebar-layout.tsx
new file mode 100644
index 0000000..ecc07b1
--- /dev/null
+++ b/src/app/components/sidebar-layout.tsx
@@ -0,0 +1,82 @@
+'use client'
+
+import * as Headless from '@headlessui/react'
+import React, { useState } from 'react'
+import { NavbarItem } from './navbar'
+
+function OpenMenuIcon() {
+ return (
+
+ )
+}
+
+function CloseMenuIcon() {
+ return (
+
+ )
+}
+
+function MobileSidebar({ open, close, children }: React.PropsWithChildren<{ open: boolean; close: () => void }>) {
+ return (
+
+
+
+
+
+
+ )
+}
+
+export function SidebarLayout({
+ navbar,
+ sidebar,
+ children,
+}: React.PropsWithChildren<{ navbar: React.ReactNode; sidebar: React.ReactNode }>) {
+ let [showSidebar, setShowSidebar] = useState(false)
+
+ return (
+
+ {/* Sidebar on desktop */}
+
{sidebar}
+
+ {/* Sidebar on mobile */}
+
setShowSidebar(false)}>
+ {sidebar}
+
+
+ {/* Navbar on mobile */}
+
+
+ {/* Content */}
+
+
+
+
+ )
+}
diff --git a/src/app/components/sidebar.tsx b/src/app/components/sidebar.tsx
new file mode 100644
index 0000000..9b4086c
--- /dev/null
+++ b/src/app/components/sidebar.tsx
@@ -0,0 +1,142 @@
+'use client'
+
+import * as Headless from '@headlessui/react'
+import clsx from 'clsx'
+import { LayoutGroup, motion } from 'motion/react'
+import React, { forwardRef, useId } from 'react'
+import { TouchTarget } from './button'
+import { Link } from './link'
+
+export function Sidebar({ className, ...props }: React.ComponentPropsWithoutRef<'nav'>) {
+ return
+}
+
+export function SidebarHeader({ className, ...props }: React.ComponentPropsWithoutRef<'div'>) {
+ return (
+ [data-slot=section]+[data-slot=section]]:mt-2.5'
+ )}
+ />
+ )
+}
+
+export function SidebarBody({ className, ...props }: React.ComponentPropsWithoutRef<'div'>) {
+ return (
+
[data-slot=section]+[data-slot=section]]:mt-8'
+ )}
+ />
+ )
+}
+
+export function SidebarFooter({ className, ...props }: React.ComponentPropsWithoutRef<'div'>) {
+ return (
+
[data-slot=section]+[data-slot=section]]:mt-2.5'
+ )}
+ />
+ )
+}
+
+export function SidebarSection({ className, ...props }: React.ComponentPropsWithoutRef<'div'>) {
+ let id = useId()
+
+ return (
+
+
+
+ )
+}
+
+export function SidebarDivider({ className, ...props }: React.ComponentPropsWithoutRef<'hr'>) {
+ return
+}
+
+export function SidebarSpacer({ className, ...props }: React.ComponentPropsWithoutRef<'div'>) {
+ return
+}
+
+export function SidebarHeading({ className, ...props }: React.ComponentPropsWithoutRef<'h3'>) {
+ return (
+
+ )
+}
+
+export const SidebarItem = forwardRef(function SidebarItem(
+ {
+ current,
+ className,
+ children,
+ ...props
+ }: { current?: boolean; className?: string; children: React.ReactNode } & (
+ | ({ href?: never } & Omit
)
+ | ({ href: string } & Omit, 'as' | 'className'>)
+ ),
+ ref: React.ForwardedRef
+) {
+ let classes = clsx(
+ // Base
+ 'flex w-full items-center gap-3 rounded-lg px-2 py-2.5 text-left text-base/6 font-medium text-zinc-950 sm:py-2 sm:text-sm/5',
+ // Leading icon/icon-only
+ '*:data-[slot=icon]:size-6 *:data-[slot=icon]:shrink-0 *:data-[slot=icon]:fill-zinc-500 sm:*:data-[slot=icon]:size-5',
+ // Trailing icon (down chevron or similar)
+ '*:last:data-[slot=icon]:ml-auto *:last:data-[slot=icon]:size-5 sm:*:last:data-[slot=icon]:size-4',
+ // Avatar
+ '*:data-[slot=avatar]:-m-0.5 *:data-[slot=avatar]:size-7 sm:*:data-[slot=avatar]:size-6',
+ // Hover
+ 'data-hover:bg-zinc-950/5 data-hover:*:data-[slot=icon]:fill-zinc-950',
+ // Active
+ 'data-active:bg-zinc-950/5 data-active:*:data-[slot=icon]:fill-zinc-950',
+ // Current
+ 'data-current:*:data-[slot=icon]:fill-zinc-950',
+ // Dark mode
+ 'dark:text-white dark:*:data-[slot=icon]:fill-zinc-400',
+ 'dark:data-hover:bg-white/5 dark:data-hover:*:data-[slot=icon]:fill-white',
+ 'dark:data-active:bg-white/5 dark:data-active:*:data-[slot=icon]:fill-white',
+ 'dark:data-current:*:data-[slot=icon]:fill-white'
+ )
+
+ return (
+
+ {current && (
+
+ )}
+ {typeof props.href === 'string' ? (
+
+ {children}
+
+ ) : (
+
+ {children}
+
+ )}
+
+ )
+})
+
+export function SidebarLabel({ className, ...props }: React.ComponentPropsWithoutRef<'span'>) {
+ return
+}
diff --git a/src/app/components/stacked-layout.tsx b/src/app/components/stacked-layout.tsx
new file mode 100644
index 0000000..5b1c40e
--- /dev/null
+++ b/src/app/components/stacked-layout.tsx
@@ -0,0 +1,79 @@
+'use client'
+
+import * as Headless from '@headlessui/react'
+import React, { useState } from 'react'
+import { NavbarItem } from './navbar'
+
+function OpenMenuIcon() {
+ return (
+
+ )
+}
+
+function CloseMenuIcon() {
+ return (
+
+ )
+}
+
+function MobileSidebar({ open, close, children }: React.PropsWithChildren<{ open: boolean; close: () => void }>) {
+ return (
+
+
+
+
+
+
+ )
+}
+
+export function StackedLayout({
+ navbar,
+ sidebar,
+ children,
+}: React.PropsWithChildren<{ navbar: React.ReactNode; sidebar: React.ReactNode }>) {
+ let [showSidebar, setShowSidebar] = useState(false)
+
+ return (
+
+ {/* Sidebar on mobile */}
+
setShowSidebar(false)}>
+ {sidebar}
+
+
+ {/* Navbar */}
+
+
+ {/* Content */}
+
+
+
+
+ )
+}
diff --git a/src/app/components/switch.tsx b/src/app/components/switch.tsx
new file mode 100644
index 0000000..db40ea4
--- /dev/null
+++ b/src/app/components/switch.tsx
@@ -0,0 +1,195 @@
+import * as Headless from '@headlessui/react'
+import clsx from 'clsx'
+import type React from 'react'
+
+export function SwitchGroup({ className, ...props }: React.ComponentPropsWithoutRef<'div'>) {
+ return (
+
+ )
+}
+
+export function SwitchField({
+ className,
+ ...props
+}: { className?: string } & Omit) {
+ return (
+
+ )
+}
+
+const colors = {
+ 'dark/zinc': [
+ '[--switch-bg-ring:var(--color-zinc-950)]/90 [--switch-bg:var(--color-zinc-900)] dark:[--switch-bg-ring:transparent] dark:[--switch-bg:var(--color-white)]/25',
+ '[--switch-ring:var(--color-zinc-950)]/90 [--switch-shadow:var(--color-black)]/10 [--switch:white] dark:[--switch-ring:var(--color-zinc-700)]/90',
+ ],
+ 'dark/white': [
+ '[--switch-bg-ring:var(--color-zinc-950)]/90 [--switch-bg:var(--color-zinc-900)] dark:[--switch-bg-ring:transparent] dark:[--switch-bg:var(--color-white)]',
+ '[--switch-ring:var(--color-zinc-950)]/90 [--switch-shadow:var(--color-black)]/10 [--switch:white] dark:[--switch-ring:transparent] dark:[--switch:var(--color-zinc-900)]',
+ ],
+ dark: [
+ '[--switch-bg-ring:var(--color-zinc-950)]/90 [--switch-bg:var(--color-zinc-900)] dark:[--switch-bg-ring:var(--color-white)]/15',
+ '[--switch-ring:var(--color-zinc-950)]/90 [--switch-shadow:var(--color-black)]/10 [--switch:white]',
+ ],
+ zinc: [
+ '[--switch-bg-ring:var(--color-zinc-700)]/90 [--switch-bg:var(--color-zinc-600)] dark:[--switch-bg-ring:transparent]',
+ '[--switch-shadow:var(--color-black)]/10 [--switch:white] [--switch-ring:var(--color-zinc-700)]/90',
+ ],
+ white: [
+ '[--switch-bg-ring:var(--color-black)]/15 [--switch-bg:white] dark:[--switch-bg-ring:transparent]',
+ '[--switch-shadow:var(--color-black)]/10 [--switch-ring:transparent] [--switch:var(--color-zinc-950)]',
+ ],
+ red: [
+ '[--switch-bg-ring:var(--color-red-700)]/90 [--switch-bg:var(--color-red-600)] dark:[--switch-bg-ring:transparent]',
+ '[--switch:white] [--switch-ring:var(--color-red-700)]/90 [--switch-shadow:var(--color-red-900)]/20',
+ ],
+ orange: [
+ '[--switch-bg-ring:var(--color-orange-600)]/90 [--switch-bg:var(--color-orange-500)] dark:[--switch-bg-ring:transparent]',
+ '[--switch:white] [--switch-ring:var(--color-orange-600)]/90 [--switch-shadow:var(--color-orange-900)]/20',
+ ],
+ amber: [
+ '[--switch-bg-ring:var(--color-amber-500)]/80 [--switch-bg:var(--color-amber-400)] dark:[--switch-bg-ring:transparent]',
+ '[--switch-ring:transparent] [--switch-shadow:transparent] [--switch:var(--color-amber-950)]',
+ ],
+ yellow: [
+ '[--switch-bg-ring:var(--color-yellow-400)]/80 [--switch-bg:var(--color-yellow-300)] dark:[--switch-bg-ring:transparent]',
+ '[--switch-ring:transparent] [--switch-shadow:transparent] [--switch:var(--color-yellow-950)]',
+ ],
+ lime: [
+ '[--switch-bg-ring:var(--color-lime-400)]/80 [--switch-bg:var(--color-lime-300)] dark:[--switch-bg-ring:transparent]',
+ '[--switch-ring:transparent] [--switch-shadow:transparent] [--switch:var(--color-lime-950)]',
+ ],
+ green: [
+ '[--switch-bg-ring:var(--color-green-700)]/90 [--switch-bg:var(--color-green-600)] dark:[--switch-bg-ring:transparent]',
+ '[--switch:white] [--switch-ring:var(--color-green-700)]/90 [--switch-shadow:var(--color-green-900)]/20',
+ ],
+ emerald: [
+ '[--switch-bg-ring:var(--color-emerald-600)]/90 [--switch-bg:var(--color-emerald-500)] dark:[--switch-bg-ring:transparent]',
+ '[--switch:white] [--switch-ring:var(--color-emerald-600)]/90 [--switch-shadow:var(--color-emerald-900)]/20',
+ ],
+ teal: [
+ '[--switch-bg-ring:var(--color-teal-700)]/90 [--switch-bg:var(--color-teal-600)] dark:[--switch-bg-ring:transparent]',
+ '[--switch:white] [--switch-ring:var(--color-teal-700)]/90 [--switch-shadow:var(--color-teal-900)]/20',
+ ],
+ cyan: [
+ '[--switch-bg-ring:var(--color-cyan-400)]/80 [--switch-bg:var(--color-cyan-300)] dark:[--switch-bg-ring:transparent]',
+ '[--switch-ring:transparent] [--switch-shadow:transparent] [--switch:var(--color-cyan-950)]',
+ ],
+ sky: [
+ '[--switch-bg-ring:var(--color-sky-600)]/80 [--switch-bg:var(--color-sky-500)] dark:[--switch-bg-ring:transparent]',
+ '[--switch:white] [--switch-ring:var(--color-sky-600)]/80 [--switch-shadow:var(--color-sky-900)]/20',
+ ],
+ blue: [
+ '[--switch-bg-ring:var(--color-blue-700)]/90 [--switch-bg:var(--color-blue-600)] dark:[--switch-bg-ring:transparent]',
+ '[--switch:white] [--switch-ring:var(--color-blue-700)]/90 [--switch-shadow:var(--color-blue-900)]/20',
+ ],
+ indigo: [
+ '[--switch-bg-ring:var(--color-indigo-600)]/90 [--switch-bg:var(--color-indigo-500)] dark:[--switch-bg-ring:transparent]',
+ '[--switch:white] [--switch-ring:var(--color-indigo-600)]/90 [--switch-shadow:var(--color-indigo-900)]/20',
+ ],
+ violet: [
+ '[--switch-bg-ring:var(--color-violet-600)]/90 [--switch-bg:var(--color-violet-500)] dark:[--switch-bg-ring:transparent]',
+ '[--switch:white] [--switch-ring:var(--color-violet-600)]/90 [--switch-shadow:var(--color-violet-900)]/20',
+ ],
+ purple: [
+ '[--switch-bg-ring:var(--color-purple-600)]/90 [--switch-bg:var(--color-purple-500)] dark:[--switch-bg-ring:transparent]',
+ '[--switch:white] [--switch-ring:var(--color-purple-600)]/90 [--switch-shadow:var(--color-purple-900)]/20',
+ ],
+ fuchsia: [
+ '[--switch-bg-ring:var(--color-fuchsia-600)]/90 [--switch-bg:var(--color-fuchsia-500)] dark:[--switch-bg-ring:transparent]',
+ '[--switch:white] [--switch-ring:var(--color-fuchsia-600)]/90 [--switch-shadow:var(--color-fuchsia-900)]/20',
+ ],
+ pink: [
+ '[--switch-bg-ring:var(--color-pink-600)]/90 [--switch-bg:var(--color-pink-500)] dark:[--switch-bg-ring:transparent]',
+ '[--switch:white] [--switch-ring:var(--color-pink-600)]/90 [--switch-shadow:var(--color-pink-900)]/20',
+ ],
+ rose: [
+ '[--switch-bg-ring:var(--color-rose-600)]/90 [--switch-bg:var(--color-rose-500)] dark:[--switch-bg-ring:transparent]',
+ '[--switch:white] [--switch-ring:var(--color-rose-600)]/90 [--switch-shadow:var(--color-rose-900)]/20',
+ ],
+}
+
+type Color = keyof typeof colors
+
+export function Switch({
+ color = 'dark/zinc',
+ className,
+ ...props
+}: {
+ color?: Color
+ className?: string
+} & Omit) {
+ return (
+
+
+
+ )
+}
diff --git a/src/app/components/table.tsx b/src/app/components/table.tsx
new file mode 100644
index 0000000..8642386
--- /dev/null
+++ b/src/app/components/table.tsx
@@ -0,0 +1,124 @@
+'use client'
+
+import clsx from 'clsx'
+import type React from 'react'
+import { createContext, useContext, useState } from 'react'
+import { Link } from './link'
+
+const TableContext = createContext<{ bleed: boolean; dense: boolean; grid: boolean; striped: boolean }>({
+ bleed: false,
+ dense: false,
+ grid: false,
+ striped: false,
+})
+
+export function Table({
+ bleed = false,
+ dense = false,
+ grid = false,
+ striped = false,
+ className,
+ children,
+ ...props
+}: { bleed?: boolean; dense?: boolean; grid?: boolean; striped?: boolean } & React.ComponentPropsWithoutRef<'div'>) {
+ return (
+ }>
+
+
+ )
+}
+
+export function TableHead({ className, ...props }: React.ComponentPropsWithoutRef<'thead'>) {
+ return
+}
+
+export function TableBody(props: React.ComponentPropsWithoutRef<'tbody'>) {
+ return
+}
+
+const TableRowContext = createContext<{ href?: string; target?: string; title?: string }>({
+ href: undefined,
+ target: undefined,
+ title: undefined,
+})
+
+export function TableRow({
+ href,
+ target,
+ title,
+ className,
+ ...props
+}: { href?: string; target?: string; title?: string } & React.ComponentPropsWithoutRef<'tr'>) {
+ let { striped } = useContext(TableContext)
+
+ return (
+ }>
+
+
+ )
+}
+
+export function TableHeader({ className, ...props }: React.ComponentPropsWithoutRef<'th'>) {
+ let { bleed, grid } = useContext(TableContext)
+
+ return (
+ |
+ )
+}
+
+export function TableCell({ className, children, ...props }: React.ComponentPropsWithoutRef<'td'>) {
+ let { bleed, dense, grid, striped } = useContext(TableContext)
+ let { href, target, title } = useContext(TableRowContext)
+ let [cellRef, setCellRef] = useState(null)
+
+ return (
+
+ {href && (
+
+ )}
+ {children}
+ |
+ )
+}
diff --git a/src/app/components/text.tsx b/src/app/components/text.tsx
new file mode 100644
index 0000000..2b2512c
--- /dev/null
+++ b/src/app/components/text.tsx
@@ -0,0 +1,40 @@
+import clsx from 'clsx'
+import { Link } from './link'
+
+export function Text({ className, ...props }: React.ComponentPropsWithoutRef<'p'>) {
+ return (
+
+ )
+}
+
+export function TextLink({ className, ...props }: React.ComponentPropsWithoutRef) {
+ return (
+
+ )
+}
+
+export function Strong({ className, ...props }: React.ComponentPropsWithoutRef<'strong'>) {
+ return
+}
+
+export function Code({ className, ...props }: React.ComponentPropsWithoutRef<'code'>) {
+ return (
+
+ )
+}
diff --git a/src/app/components/textarea.tsx b/src/app/components/textarea.tsx
new file mode 100644
index 0000000..d4ef5ef
--- /dev/null
+++ b/src/app/components/textarea.tsx
@@ -0,0 +1,54 @@
+import * as Headless from '@headlessui/react'
+import clsx from 'clsx'
+import React, { forwardRef } from 'react'
+
+export const Textarea = forwardRef(function Textarea(
+ {
+ className,
+ resizable = true,
+ ...props
+ }: { className?: string; resizable?: boolean } & Omit,
+ ref: React.ForwardedRef
+) {
+ return (
+
+
+
+ )
+})
diff --git a/src/app/components/ui/badge.tsx b/src/app/components/ui/badge.tsx
new file mode 100644
index 0000000..7ff4c4e
--- /dev/null
+++ b/src/app/components/ui/badge.tsx
@@ -0,0 +1,37 @@
+import * as React from "react"
+// Simple className utility
+function cn(...classes: (string | undefined | null | boolean)[]): string {
+ return classes.filter(Boolean).join(' ')
+}
+
+// Badge variants
+const badgeVariants = {
+ variant: {
+ default: "bg-[#8D6B1D] text-white",
+ secondary: "bg-gray-100 text-gray-900",
+ destructive: "bg-red-500 text-white",
+ outline: "border border-gray-300 text-gray-900",
+ },
+}
+
+export interface BadgeProps extends React.HTMLAttributes {
+ variant?: keyof typeof badgeVariants.variant
+}
+
+const Badge = React.forwardRef(
+ ({ className, variant = "default", ...props }, ref) => {
+ const baseClasses = "inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-[#8D6B1D] focus:ring-offset-2"
+ const variantClasses = badgeVariants.variant[variant]
+
+ return (
+
+ )
+ }
+)
+Badge.displayName = "Badge"
+
+export { Badge }
\ No newline at end of file
diff --git a/src/app/components/ui/button.tsx b/src/app/components/ui/button.tsx
new file mode 100644
index 0000000..4a0f5e4
--- /dev/null
+++ b/src/app/components/ui/button.tsx
@@ -0,0 +1,47 @@
+import * as React from "react"
+// Simple className utility
+function cn(...classes: (string | undefined | null | boolean)[]): string {
+ return classes.filter(Boolean).join(' ')
+}
+
+// Button variants
+const buttonVariants = {
+ variant: {
+ default: "bg-[#8D6B1D] text-white hover:bg-[#7A5E1A]",
+ destructive: "bg-red-500 text-white hover:bg-red-600",
+ outline: "border border-gray-300 bg-white hover:bg-gray-50 hover:text-gray-900",
+ secondary: "bg-gray-100 text-gray-900 hover:bg-gray-200",
+ ghost: "hover:bg-gray-100 hover:text-gray-900",
+ link: "text-[#8D6B1D] underline-offset-4 hover:underline",
+ },
+ size: {
+ default: "h-10 px-4 py-2",
+ sm: "h-9 rounded-md px-3",
+ lg: "h-11 rounded-md px-8",
+ icon: "h-10 w-10",
+ },
+}
+
+export interface ButtonProps extends React.ButtonHTMLAttributes {
+ variant?: keyof typeof buttonVariants.variant
+ size?: keyof typeof buttonVariants.size
+}
+
+const Button = React.forwardRef(
+ ({ className, variant = "default", size = "default", ...props }, ref) => {
+ const baseClasses = "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#8D6B1D] focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50"
+ const variantClasses = buttonVariants.variant[variant]
+ const sizeClasses = buttonVariants.size[size]
+
+ return (
+
+ )
+ }
+)
+Button.displayName = "Button"
+
+export { Button }
\ No newline at end of file
diff --git a/src/app/dashboard/page.tsx b/src/app/dashboard/page.tsx
new file mode 100644
index 0000000..8cce8dc
--- /dev/null
+++ b/src/app/dashboard/page.tsx
@@ -0,0 +1,225 @@
+'use client'
+
+import { useEffect } from 'react'
+import { useRouter } from 'next/navigation'
+import useAuthStore from '../store/authStore'
+import Header from '../components/nav/Header'
+import Footer from '../components/Footer'
+import {
+ ShoppingBagIcon,
+ UsersIcon,
+ UserCircleIcon,
+ StarIcon,
+ ChartBarIcon,
+ HeartIcon
+} from '@heroicons/react/24/outline'
+
+export default function DashboardPage() {
+ const router = useRouter()
+ const user = useAuthStore(state => state.user)
+
+ // Redirect if not logged in
+ useEffect(() => {
+ if (!user) {
+ router.push('/login')
+ }
+ }, [user, router])
+
+ // Don't render if no user
+ if (!user) {
+ return (
+
+ )
+ }
+
+ // Get user name
+ const getUserName = () => {
+ if (user.firstName && user.lastName) {
+ return `${user.firstName} ${user.lastName}`
+ }
+ if (user.firstName) return user.firstName
+ if (user.email) return user.email.split('@')[0]
+ return 'User'
+ }
+
+ // Quick actions
+ const quickActions = [
+ {
+ title: 'Browse Shop',
+ description: 'Explore sustainable products',
+ icon: ShoppingBagIcon,
+ href: '/shop',
+ color: 'bg-blue-500'
+ },
+ {
+ title: 'Join Community',
+ description: 'Connect with like-minded people',
+ icon: UsersIcon,
+ href: '/community',
+ color: 'bg-green-500'
+ },
+ {
+ title: 'Edit Profile',
+ description: 'Update your information',
+ icon: UserCircleIcon,
+ href: '/profile',
+ color: 'bg-purple-500'
+ }
+ ]
+
+ // Stats (mock data for now)
+ const stats = [
+ { label: 'Orders', value: '12', icon: ShoppingBagIcon },
+ { label: 'Favorites', value: '8', icon: HeartIcon },
+ { label: 'Gold Points', value: '250', icon: StarIcon },
+ { label: 'Activity', value: '15', icon: ChartBarIcon }
+ ]
+
+ return (
+
+
+
+
+
+ {/* Welcome Section */}
+
+
+ Welcome back, {getUserName()}! 👋
+
+
+ Here's what's happening with your Profit Planet account
+
+
+
+ {/* Stats Grid - Tailwind UI Plus "With brand icon" */}
+
+
+ {stats.map((stat, index) => {
+ // Define icon colors for each stat
+ const iconColors = [
+ 'bg-blue-500', // Orders
+ 'bg-red-500', // Favorites
+ 'bg-yellow-500', // Gold Points
+ 'bg-green-500' // Activity
+ ];
+
+ return (
+
+
+
+
+
+ {stat.label}
+
+
+ {stat.value}
+
+
+
+ );
+ })}
+
+
+
+ {/* Quick Actions */}
+
+
Quick Actions
+
+ {quickActions.map((action, index) => (
+
+ ))}
+
+
+
+ {/* Gold Member Status */}
+
+
+
+
+
Gold Member Status
+
+ Enjoy exclusive benefits and discounts
+
+
+
+
+
+
+
+
+ {/* Recent Activity */}
+
+
Recent Activity
+
+
+
+
+
+
+
Order completed
+
Eco-friendly water bottle
+
+
2 days ago
+
+
+
+
+
+
+
+
Added to favorites
+
Sustainable backpack
+
+
1 week ago
+
+
+
+
+
+
+
+
Joined community
+
Eco Warriors Group
+
+
2 weeks ago
+
+
+
+
+
+
+
+
+ )
+}
\ No newline at end of file
diff --git a/src/app/globals.css b/src/app/globals.css
index a2dc41e..292b445 100644
--- a/src/app/globals.css
+++ b/src/app/globals.css
@@ -3,6 +3,12 @@
:root {
--background: #ffffff;
--foreground: #171717;
+
+ /* Brand Colors */
+ --color-brand-accent: #8D6B1D;
+ --color-brand-header: #0F172A;
+ --color-brand-text: #4A4A4A;
+ --color-brand-background: #FFFFFF;
}
@theme inline {
diff --git a/src/app/i18n/config.ts b/src/app/i18n/config.ts
new file mode 100644
index 0000000..6ee64db
--- /dev/null
+++ b/src/app/i18n/config.ts
@@ -0,0 +1,10 @@
+export type Language = 'en' | 'de';
+
+export const DEFAULT_LANGUAGE: Language = 'en';
+
+export const SUPPORTED_LANGUAGES: Language[] = ['en', 'de'];
+
+export const LANGUAGE_NAMES = {
+ en: 'English',
+ de: 'Deutsch'
+} as const;
\ No newline at end of file
diff --git a/src/app/i18n/translations/de.ts b/src/app/i18n/translations/de.ts
new file mode 100644
index 0000000..8f97c6d
--- /dev/null
+++ b/src/app/i18n/translations/de.ts
@@ -0,0 +1,48 @@
+import { Translations } from '../types';
+
+export const de: Translations = {
+ home: {
+ title: 'Profit Planet',
+ tagline: 'Nachhaltige Produkte entdecken und handeln',
+ description: 'Tritt unserer Community von umweltbewussten Verbrauchern und Unternehmen bei. Handle mit nachhaltigen Produkten, sammle Belohnungen und mache einen positiven Einfluss auf unseren Planeten.',
+ features: {
+ sustainable: {
+ title: 'Nachhaltige Produkte',
+ description: 'Entdecke umweltfreundliche Produkte, die einen Unterschied für unseren Planeten machen.'
+ },
+ community: {
+ title: 'Aktive Community',
+ description: 'Vernetze dich mit Gleichgesinnten, denen Nachhaltigkeit wichtig ist.'
+ },
+ rewards: {
+ title: 'Belohnungen sammeln',
+ description: 'Erhalte Gold-Punkte für jeden nachhaltigen Kauf und jede Aktion.'
+ }
+ },
+ stats: {
+ members: 'Aktive Mitglieder',
+ products: 'Öko-Produkte',
+ communities: 'Communities'
+ },
+ cta: {
+ getStarted: 'Jetzt starten',
+ learnMore: 'Mehr erfahren'
+ }
+ },
+ footer: {
+ company: 'Profit Planet GmbH',
+ rights: 'Alle Rechte vorbehalten.',
+ privacy: 'Datenschutz',
+ terms: 'AGB',
+ contact: 'Kontakt'
+ },
+ nav: {
+ home: 'Home',
+ shop: 'Shop',
+ dashboard: 'Dashboard',
+ community: 'Community',
+ profile: 'Profil',
+ login: 'Anmelden',
+ logout: 'Abmelden'
+ }
+};
\ No newline at end of file
diff --git a/src/app/i18n/translations/en.ts b/src/app/i18n/translations/en.ts
new file mode 100644
index 0000000..d0fd9ea
--- /dev/null
+++ b/src/app/i18n/translations/en.ts
@@ -0,0 +1,48 @@
+import { Translations } from '../types';
+
+export const en: Translations = {
+ home: {
+ title: 'Profit Planet',
+ tagline: 'Discover and trade sustainable products',
+ description: 'Join our community of eco-conscious consumers and businesses. Trade sustainable products, earn rewards, and make a positive impact on our planet.',
+ features: {
+ sustainable: {
+ title: 'Sustainable Products',
+ description: 'Discover eco-friendly products that make a difference for our planet.'
+ },
+ community: {
+ title: 'Active Community',
+ description: 'Connect with like-minded people who care about sustainability.'
+ },
+ rewards: {
+ title: 'Earn Rewards',
+ description: 'Get Gold Points for every sustainable purchase and action.'
+ }
+ },
+ stats: {
+ members: 'Active Members',
+ products: 'Eco Products',
+ communities: 'Communities'
+ },
+ cta: {
+ getStarted: 'Get Started',
+ learnMore: 'Learn More'
+ }
+ },
+ footer: {
+ company: 'Profit Planet GmbH',
+ rights: 'All rights reserved.',
+ privacy: 'Privacy Policy',
+ terms: 'Terms of Service',
+ contact: 'Contact'
+ },
+ nav: {
+ home: 'Home',
+ shop: 'Shop',
+ dashboard: 'Dashboard',
+ community: 'Community',
+ profile: 'Profile',
+ login: 'Login',
+ logout: 'Logout'
+ }
+};
\ No newline at end of file
diff --git a/src/app/i18n/types.ts b/src/app/i18n/types.ts
new file mode 100644
index 0000000..8f884a7
--- /dev/null
+++ b/src/app/i18n/types.ts
@@ -0,0 +1,46 @@
+export interface Translations {
+ home: {
+ title: string;
+ tagline: string;
+ description: string;
+ features: {
+ sustainable: {
+ title: string;
+ description: string;
+ };
+ community: {
+ title: string;
+ description: string;
+ };
+ rewards: {
+ title: string;
+ description: string;
+ };
+ };
+ stats: {
+ members: string;
+ products: string;
+ communities: string;
+ };
+ cta: {
+ getStarted: string;
+ learnMore: string;
+ };
+ };
+ footer: {
+ company: string;
+ rights: string;
+ privacy: string;
+ terms: string;
+ contact: string;
+ };
+ nav: {
+ home: string;
+ shop: string;
+ dashboard: string;
+ community: string;
+ profile: string;
+ login: string;
+ logout: string;
+ };
+}
\ No newline at end of file
diff --git a/src/app/i18n/useTranslation.tsx b/src/app/i18n/useTranslation.tsx
new file mode 100644
index 0000000..f026ee0
--- /dev/null
+++ b/src/app/i18n/useTranslation.tsx
@@ -0,0 +1,52 @@
+'use client';
+
+import { createContext, useContext, useState, ReactNode } from 'react';
+import { Language, DEFAULT_LANGUAGE } from './config';
+import { en } from './translations/en';
+import { de } from './translations/de';
+
+const translations = {
+ en,
+ de
+} as const;
+
+interface I18nContextType {
+ language: Language;
+ setLanguage: (lang: Language) => void;
+ t: (key: string) => string;
+}
+
+const I18nContext = createContext(undefined);
+
+interface I18nProviderProps {
+ children: ReactNode;
+}
+
+export function I18nProvider({ children }: I18nProviderProps) {
+ const [language, setLanguage] = useState(DEFAULT_LANGUAGE);
+
+ const t = (key: string): string => {
+ const keys = key.split('.');
+ let value: any = translations[language];
+
+ for (const k of keys) {
+ value = value?.[k];
+ }
+
+ return typeof value === 'string' ? value : key;
+ };
+
+ return (
+
+ {children}
+
+ );
+}
+
+export function useTranslation() {
+ const context = useContext(I18nContext);
+ if (context === undefined) {
+ throw new Error('useTranslation must be used within an I18nProvider');
+ }
+ return context;
+}
\ No newline at end of file
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index f7fa87e..ecde0f8 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -1,6 +1,8 @@
import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";
+import '@tailwindplus/elements';
+import ClientWrapper from './ClientWrapper';
const geistSans = Geist({
variable: "--font-geist-sans",
@@ -23,11 +25,13 @@ export default function RootLayout({
children: React.ReactNode;
}>) {
return (
-
+
- {children}
+
+ {children}
+
);
diff --git a/src/app/login/components/LoginForm.tsx b/src/app/login/components/LoginForm.tsx
new file mode 100644
index 0000000..6be4c61
--- /dev/null
+++ b/src/app/login/components/LoginForm.tsx
@@ -0,0 +1,369 @@
+'use client'
+
+import { useState } from 'react'
+import { useRouter } from 'next/navigation'
+import { EyeIcon, EyeSlashIcon } from '@heroicons/react/24/outline'
+import { useLogin } from '../hooks/useLogin'
+
+export default function LoginForm() {
+ const [showPassword, setShowPassword] = useState(false)
+ const [showBall, setShowBall] = useState(true)
+ const [formData, setFormData] = useState({
+ email: '',
+ password: '',
+ rememberMe: false
+ })
+ const router = useRouter()
+ const { login, error, setError, loading } = useLogin()
+
+ // Responsive ball visibility
+ useState(() => {
+ const handleResize = () => {
+ setShowBall(window.innerWidth >= 768)
+ }
+ handleResize()
+ window.addEventListener('resize', handleResize)
+ return () => window.removeEventListener('resize', handleResize)
+ })
+
+ const handleInputChange = (e: React.ChangeEvent) => {
+ const { name, value, type, checked } = e.target
+ setFormData(prev => ({
+ ...prev,
+ [name]: type === 'checkbox' ? checked : value
+ }))
+ setError('') // Clear error when user starts typing
+ }
+
+ const validateForm = (): boolean => {
+ if (!formData.email.trim()) {
+ setError('E-Mail-Adresse ist erforderlich')
+ return false
+ }
+
+ if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.email)) {
+ setError('Bitte gib eine gültige E-Mail-Adresse ein')
+ return false
+ }
+
+ if (!formData.password.trim()) {
+ setError('Passwort ist erforderlich')
+ return false
+ }
+
+ if (formData.password.length < 6) {
+ setError('Passwort muss mindestens 6 Zeichen lang sein')
+ return false
+ }
+
+ return true
+ }
+
+ const handleSubmit = async (e: React.FormEvent) => {
+ e.preventDefault()
+
+ if (!validateForm()) return
+
+ await login({
+ email: formData.email,
+ password: formData.password,
+ rememberMe: formData.rememberMe
+ })
+ }
+
+ const isMobile = typeof window !== 'undefined' && window.innerWidth < 768
+
+ return (
+
+
+ {/* Animated Ball - Desktop Only */}
+ {showBall && !isMobile && (
+
+
+
+ {/* Orbiting balls */}
+
+
+
+
+
+
+
+
+
+
+ )}
+
+ {/* Content */}
+
+
+ Profit Planet
+
+
+ Welcome back! Login to continue.
+
+
+
+
+ {/* Registration Section */}
+
+
+
+
+ Profit Planet is available by invitation only.
+
+
+ Contact us for an invitation!
+
+
+
+
+
+ {/* CSS Animations */}
+
+
+
+ )
+}
\ No newline at end of file
diff --git a/src/app/login/hooks/useLogin.ts b/src/app/login/hooks/useLogin.ts
new file mode 100644
index 0000000..badf83c
--- /dev/null
+++ b/src/app/login/hooks/useLogin.ts
@@ -0,0 +1,116 @@
+'use client'
+
+import { useState } from 'react'
+import { useRouter } from 'next/navigation'
+import useAuthStore from '../../store/authStore'
+
+export interface LoginCredentials {
+ email: string
+ password: string
+ rememberMe?: boolean
+}
+
+export function useLogin() {
+ const [error, setError] = useState('')
+ const [loading, setLoading] = useState(false)
+
+ const router = useRouter()
+ const setAccessToken = useAuthStore(state => state.setAccessToken)
+ const setUser = useAuthStore(state => state.setUser)
+
+ const login = async (credentials: LoginCredentials) => {
+ setError('')
+ setLoading(true)
+
+ try {
+ console.log('Login attempt:', {
+ email: credentials.email,
+ rememberMe: credentials.rememberMe
+ })
+
+ // Make actual API call to backend
+ const loginUrl = `${process.env.NEXT_PUBLIC_API_BASE_URL}/api/login`
+ console.log('Calling login API:', loginUrl)
+
+ const response = await fetch(loginUrl, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ credentials: 'include', // Include cookies for refresh token
+ body: JSON.stringify({
+ email: credentials.email,
+ password: credentials.password,
+ }),
+ })
+
+ console.log('Login response status:', response.status)
+
+ if (!response.ok) {
+ // Handle HTTP errors
+ if (response.status === 401) {
+ throw new Error('Invalid credentials')
+ } else if (response.status === 404) {
+ throw new Error('Account not found')
+ } else if (response.status === 423) {
+ throw new Error('Account locked')
+ } else {
+ throw new Error('Login failed. Please try again.')
+ }
+ }
+
+ const data = await response.json()
+ console.log('Login response data:', data)
+
+ if (data.success && data.accessToken && data.user) {
+ // Update auth store
+ setAccessToken(data.accessToken)
+ setUser(data.user)
+
+ // Store session info if remember me is checked
+ if (credentials.rememberMe) {
+ if (typeof window !== 'undefined') {
+ sessionStorage.setItem('userType', data.user.userType)
+ sessionStorage.setItem('role', data.user.role)
+ }
+ }
+
+ console.log('✅ Login successful:', data.user)
+
+ // Redirect to dashboard
+ router.push('/dashboard')
+
+ return { success: true, user: data.user }
+ } else {
+ throw new Error(data.message || 'Login failed')
+ }
+
+ } catch (err: any) {
+ console.error('❌ Login error:', err)
+
+ // Handle specific error cases
+ if (err.message?.includes('Invalid credentials')) {
+ setError('E-Mail oder Passwort falsch')
+ } else if (err.message?.includes('Account not found')) {
+ setError('Kein Account mit dieser E-Mail-Adresse gefunden')
+ } else if (err.message?.includes('Account locked')) {
+ setError('Account wurde gesperrt. Kontaktiere den Support.')
+ } else if (err.message?.includes('Failed to fetch')) {
+ setError('Verbindung zum Server fehlgeschlagen. Bitte versuche es später erneut.')
+ } else {
+ setError(err.message || 'Anmeldung fehlgeschlagen. Bitte versuche es erneut.')
+ }
+
+ return { success: false, error: err.message }
+ } finally {
+ setLoading(false)
+ }
+ }
+
+ return {
+ login,
+ error,
+ setError,
+ loading
+ }
+}
\ No newline at end of file
diff --git a/src/app/login/page.tsx b/src/app/login/page.tsx
new file mode 100644
index 0000000..973f8bc
--- /dev/null
+++ b/src/app/login/page.tsx
@@ -0,0 +1,69 @@
+'use client'
+
+import { useState, useEffect } from 'react'
+import { useRouter } from 'next/navigation'
+import LoginForm from './components/LoginForm'
+import PageLayout from '../components/PageLayout'
+import useAuthStore from '../store/authStore'
+import GlobalAnimatedBackground from '../background/GlobalAnimatedBackground'
+
+export default function LoginPage() {
+ const [showBackground, setShowBackground] = useState(false)
+ const router = useRouter()
+ const user = useAuthStore(state => state.user)
+
+ // Check if user is already logged in
+ useEffect(() => {
+ if (user) {
+ router.push('/dashboard')
+ }
+ }, [user, router])
+
+ // Responsive background detection
+ useEffect(() => {
+ const handleResize = () => setShowBackground(window.innerWidth >= 768)
+ handleResize() // Initial check
+ window.addEventListener('resize', handleResize)
+ return () => window.removeEventListener('resize', handleResize)
+ }, [])
+
+ // Don't render if user is already logged in
+ if (user) {
+ return (
+
+
+
+
+
You are already logged in. Redirecting...
+
+
+
+ )
+ }
+
+ return (
+
+
+ {/* Animated background for desktop */}
+ {showBackground && (
+
+
+
+ )}
+
+
+
+ {/* Footer for mobile */}
+
+
+ © 2024 Profit Planet. Alle Rechte vorbehalten.
+
+
+
+
+ )
+}
\ No newline at end of file
diff --git a/src/app/login/schema/loginSchema.ts b/src/app/login/schema/loginSchema.ts
new file mode 100644
index 0000000..d24ae86
--- /dev/null
+++ b/src/app/login/schema/loginSchema.ts
@@ -0,0 +1,12 @@
+import * as yup from 'yup';
+
+export const loginSchema = yup.object().shape({
+ email: yup
+ .string()
+ .required('validation.email.required')
+ .email('validation.email.invalid'),
+ password: yup
+ .string()
+ .required('validation.password.required')
+ .min(8, 'validation.password.minLength')
+});
\ No newline at end of file
diff --git a/src/app/page.tsx b/src/app/page.tsx
index a932894..4611dbc 100644
--- a/src/app/page.tsx
+++ b/src/app/page.tsx
@@ -1,103 +1,108 @@
+'use client';
+
import Image from "next/image";
+import { useRouter } from 'next/navigation';
+import GlobalAnimatedBackground from './background/GlobalAnimatedBackground';
+import Footer from './components/Footer';
+import Header from './components/nav/Header';
+import { useTranslation } from './i18n/useTranslation';
export default function Home() {
- return (
-
-
-
-
- -
- Get started by editing{" "}
-
- src/app/page.tsx
-
- .
-
- -
- Save and see your changes instantly.
-
-
+ const router = useRouter();
+ const { t } = useTranslation();
-
-
-
- Deploy now
-
-
- Read our docs
-
+ return (
+
+ {/* Header */}
+
+
+ {/* Main Content */}
+
+
+ {/* Hero Section */}
+
+ {/* Logo/Title */}
+
+ {t('home.title')}
+
+
+ {/* Tagline */}
+
+ {t('home.tagline')}
+
+
+ {/* Feature Highlights */}
+
+ {[
+ {
+ title: t('home.features.sustainable.title'),
+ description: t('home.features.sustainable.description')
+ },
+ {
+ title: t('home.features.community.title'),
+ description: t('home.features.community.description')
+ },
+ {
+ title: t('home.features.rewards.title'),
+ description: t('home.features.rewards.description')
+ }
+ ].map((feature, index) => (
+
+
+ {feature.title}
+
+
+ {feature.description}
+
+
+ ))}
+
+
+ {/* Community Stats */}
+
+
+
10k+
+
{t('home.stats.members')}
+
+
+
50k+
+
{t('home.stats.products')}
+
+
+
100
+
{t('home.stats.communities')}
+
+
+
+ {/* CTA Buttons */}
+
+
+
+
+
-
+
+ {/* Footer */}
+
+
+
+
+ {/* Decorative Elements */}
+
);
}
diff --git a/src/app/profile/page.tsx b/src/app/profile/page.tsx
new file mode 100644
index 0000000..3ede887
--- /dev/null
+++ b/src/app/profile/page.tsx
@@ -0,0 +1,244 @@
+'use client'
+
+import { useEffect } from 'react'
+import { useRouter } from 'next/navigation'
+import useAuthStore from '../store/authStore'
+import Header from '../components/nav/Header'
+import Footer from '../components/Footer'
+import {
+ UserCircleIcon,
+ EnvelopeIcon,
+ PhoneIcon,
+ MapPinIcon,
+ PencilIcon,
+ CheckCircleIcon
+} from '@heroicons/react/24/outline'
+
+export default function ProfilePage() {
+ const router = useRouter()
+ const user = useAuthStore(state => state.user)
+
+ // Redirect if not logged in
+ useEffect(() => {
+ if (!user) {
+ router.push('/login')
+ }
+ }, [user, router])
+
+ // Don't render if no user
+ if (!user) {
+ return (
+
+ )
+ }
+
+ // Mock user data for display
+ const profileData = {
+ firstName: 'Admin',
+ lastName: 'User',
+ email: user.email || 'office@profit-planet.com',
+ phone: '+49 123 456 789',
+ address: 'Musterstraße 123, 12345 Berlin',
+ joinDate: 'Oktober 2024',
+ memberStatus: 'Gold Member',
+ profileComplete: 95
+ }
+
+ return (
+
+
+
+
+
+ {/* Page Header */}
+
+
Profile Settings
+
+ Manage your account information and preferences
+
+
+
+ {/* Profile Completion */}
+
+
+
Profile Completion
+ {profileData.profileComplete}%
+
+
+
+ Complete your profile to unlock all features
+
+
+
+
+ {/* Profile Information */}
+
+ {/* Basic Information */}
+
+
+
Basic Information
+
+
+
+
+
+
+
+
+
+ {profileData.firstName}
+
+
+
+
+
+
+ {profileData.lastName}
+
+
+
+
+
+
+
+
+ {profileData.email}
+
+
+
+
+
+
+
+
+
{profileData.phone}
+
+
+
+
+
+
+
+ {profileData.address}
+
+
+
+
+
+ {/* Account Settings */}
+
+
Account Settings
+
+
+
+
+
Email Notifications
+
Receive updates about orders and promotions
+
+
+
+
+
+
+
SMS Notifications
+
Get text messages for important updates
+
+
+
+
+
+
+
Two-Factor Authentication
+
Add extra security to your account
+
+
+
+
+
+
+
+ {/* Sidebar */}
+
+ {/* Account Status */}
+
+
Account Status
+
+
+
+ Member Since
+ {profileData.joinDate}
+
+
+
+ Status
+
+ {profileData.memberStatus}
+
+
+
+
+ Profile
+ Verified
+
+
+
+
+ {/* Quick Actions */}
+
+
Quick Actions
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
\ No newline at end of file
diff --git a/src/app/register/components/RegisterForm.tsx b/src/app/register/components/RegisterForm.tsx
new file mode 100644
index 0000000..758eb22
--- /dev/null
+++ b/src/app/register/components/RegisterForm.tsx
@@ -0,0 +1,633 @@
+'use client'
+
+import { useState, useEffect } from 'react'
+import { EyeIcon, EyeSlashIcon } from '@heroicons/react/24/outline'
+
+interface RegisterFormProps {
+ mode: 'personal' | 'company'
+ setMode: (mode: 'personal' | 'company') => void
+ refToken: string | null
+ onRegistered: () => void
+}
+
+interface PersonalFormData {
+ firstName: string
+ lastName: string
+ email: string
+ confirmEmail: string
+ password: string
+ confirmPassword: string
+ phoneNumber: string
+}
+
+interface CompanyFormData {
+ companyName: string
+ companyEmail: string
+ confirmCompanyEmail: string
+ companyPhone: string
+ contactPersonName: string
+ contactPersonPhone: string
+ password: string
+ confirmPassword: string
+}
+
+export default function RegisterForm({
+ mode,
+ setMode,
+ refToken,
+ onRegistered
+}: RegisterFormProps) {
+ // Personal form state
+ const [personalForm, setPersonalForm] = useState
({
+ firstName: '',
+ lastName: '',
+ email: '',
+ confirmEmail: '',
+ password: '',
+ confirmPassword: '',
+ phoneNumber: ''
+ })
+
+ // Company form state
+ const [companyForm, setCompanyForm] = useState({
+ companyName: '',
+ companyEmail: '',
+ confirmCompanyEmail: '',
+ companyPhone: '',
+ contactPersonName: '',
+ contactPersonPhone: '',
+ password: '',
+ confirmPassword: ''
+ })
+
+ // UI state
+ const [showPersonalPassword, setShowPersonalPassword] = useState(false)
+ const [showCompanyPassword, setShowCompanyPassword] = useState(false)
+ const [loading, setLoading] = useState(false)
+ const [error, setError] = useState('')
+ const [formFade, setFormFade] = useState('fade-in')
+
+ // Animate form when mode changes
+ useEffect(() => {
+ setFormFade('fade-out')
+ const timeout = setTimeout(() => {
+ setFormFade('fade-in')
+ }, 180)
+ return () => clearTimeout(timeout)
+ }, [mode])
+
+ // Add fade CSS
+ useEffect(() => {
+ const style = document.createElement('style')
+ style.innerHTML = `
+ .fade-in {
+ opacity: 1;
+ transform: translateY(0);
+ transition: opacity 180ms, transform 180ms;
+ }
+ .fade-out {
+ opacity: 0;
+ transform: translateY(20px);
+ transition: opacity 180ms, transform 180ms;
+ }
+ `
+ document.head.appendChild(style)
+ return () => {
+ document.head.removeChild(style)
+ }
+ }, [])
+
+ // Reset forms when switching modes
+ useEffect(() => {
+ setError('')
+ }, [mode])
+
+ // Validation helpers
+ const validatePersonalForm = (): boolean => {
+ if (!personalForm.firstName.trim() || !personalForm.lastName.trim() ||
+ !personalForm.email.trim() || !personalForm.confirmEmail.trim() ||
+ !personalForm.password.trim() || !personalForm.confirmPassword.trim()) {
+ setError('Alle Felder sind erforderlich')
+ return false
+ }
+
+ if (personalForm.email !== personalForm.confirmEmail) {
+ setError('E-Mail-Adressen stimmen nicht überein')
+ return false
+ }
+
+ if (personalForm.password !== personalForm.confirmPassword) {
+ setError('Passwörter stimmen nicht überein')
+ return false
+ }
+
+ if (!/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[\W_]).{8,}$/.test(personalForm.password)) {
+ setError('Passwort muss mindestens 8 Zeichen lang sein und Groß-, Kleinbuchstaben, Ziffern und Sonderzeichen enthalten')
+ return false
+ }
+
+ setError('')
+ return true
+ }
+
+ const validateCompanyForm = (): boolean => {
+ if (!companyForm.companyName.trim() || !companyForm.companyEmail.trim() ||
+ !companyForm.confirmCompanyEmail.trim() || !companyForm.contactPersonName.trim() ||
+ !companyForm.password.trim() || !companyForm.confirmPassword.trim()) {
+ setError('Alle Felder sind erforderlich')
+ return false
+ }
+
+ if (companyForm.companyEmail !== companyForm.confirmCompanyEmail) {
+ setError('E-Mail-Adressen stimmen nicht überein')
+ return false
+ }
+
+ if (companyForm.password !== companyForm.confirmPassword) {
+ setError('Passwörter stimmen nicht überein')
+ return false
+ }
+
+ if (!/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[\W_]).{8,}$/.test(companyForm.password)) {
+ setError('Passwort muss mindestens 8 Zeichen lang sein und Groß-, Kleinbuchstaben, Ziffern und Sonderzeichen enthalten')
+ return false
+ }
+
+ setError('')
+ return true
+ }
+
+ // Submit handlers
+ const handlePersonalSubmit = async (e: React.FormEvent) => {
+ e.preventDefault()
+ if (loading) return
+
+ if (!validatePersonalForm()) return
+
+ setLoading(true)
+
+ try {
+ // TODO: Implement API call
+ console.log('Personal registration:', { ...personalForm, refToken })
+
+ // Simulate API delay
+ await new Promise(resolve => setTimeout(resolve, 1000))
+
+ // For now, just call onRegistered
+ onRegistered()
+ } catch (error) {
+ setError('Registrierung fehlgeschlagen. Bitte versuche es erneut.')
+ } finally {
+ setLoading(false)
+ }
+ }
+
+ const handleCompanySubmit = async (e: React.FormEvent) => {
+ e.preventDefault()
+ if (loading) return
+
+ if (!validateCompanyForm()) return
+
+ setLoading(true)
+
+ try {
+ // TODO: Implement API call
+ console.log('Company registration:', { ...companyForm, refToken })
+
+ // Simulate API delay
+ await new Promise(resolve => setTimeout(resolve, 1000))
+
+ // For now, just call onRegistered
+ onRegistered()
+ } catch (error) {
+ setError('Registrierung fehlgeschlagen. Bitte versuche es erneut.')
+ } finally {
+ setLoading(false)
+ }
+ }
+
+ // Input change handlers
+ const handlePersonalChange = (e: React.ChangeEvent) => {
+ const { name, value } = e.target
+ setPersonalForm(prev => ({ ...prev, [name]: value }))
+ }
+
+ const handleCompanyChange = (e: React.ChangeEvent) => {
+ const { name, value } = e.target
+ setCompanyForm(prev => ({ ...prev, [name]: value }))
+ }
+
+ // Password strength indicator
+ const getPasswordStrength = (password: string) => {
+ let strength = 0
+ if (password.length >= 8) strength++
+ if (/[a-z]/.test(password)) strength++
+ if (/[A-Z]/.test(password)) strength++
+ if (/\d/.test(password)) strength++
+ if (/[\W_]/.test(password)) strength++
+ return strength
+ }
+
+ const renderPasswordStrength = (password: string) => {
+ const strength = getPasswordStrength(password)
+ const rules = [
+ { test: password.length >= 8, text: 'Mindestens 8 Zeichen' },
+ { test: /[a-z]/.test(password), text: 'Kleinbuchstaben (a-z)' },
+ { test: /[A-Z]/.test(password), text: 'Großbuchstaben (A-Z)' },
+ { test: /\d/.test(password), text: 'Ziffern (0-9)' },
+ { test: /[\W_]/.test(password), text: 'Sonderzeichen (!@#$...)' }
+ ]
+
+ return (
+
+
Passwort-Anforderungen:
+
+ {rules.map((rule, index) => (
+ -
+ {rule.test ? '✓' : '○'}
+ {rule.text}
+
+ ))}
+
+
+ )
+ }
+
+ return (
+
+ {/* Header */}
+
+
+ Registrierung für Profit Planet
+
+ {refToken && (
+
+ Du wurdest eingeladen!
+
+ )}
+
+
+ {/* Mode Toggle */}
+
+
+
+
+
+
+
+ {/* Error Message */}
+ {error && (
+
+ )}
+
+ {/* Forms */}
+
+ {mode === 'personal' ? (
+
+ ) : (
+
+ )}
+
+
+ {/* Login Link */}
+
+
+ )
+}
\ No newline at end of file
diff --git a/src/app/register/components/SessionDetectedModal.tsx b/src/app/register/components/SessionDetectedModal.tsx
new file mode 100644
index 0000000..95953e1
--- /dev/null
+++ b/src/app/register/components/SessionDetectedModal.tsx
@@ -0,0 +1,90 @@
+'use client'
+
+import { Fragment } from 'react'
+import { Dialog, Transition } from '@headlessui/react'
+import { ExclamationTriangleIcon } from '@heroicons/react/24/outline'
+
+interface SessionDetectedModalProps {
+ open: boolean
+ onLogout: () => void
+ onCancel: () => void
+}
+
+export default function SessionDetectedModal({
+ open,
+ onLogout,
+ onCancel
+}: SessionDetectedModalProps) {
+ return (
+
+
+
+ )
+}
\ No newline at end of file
diff --git a/src/app/register/page.tsx b/src/app/register/page.tsx
new file mode 100644
index 0000000..907d3d6
--- /dev/null
+++ b/src/app/register/page.tsx
@@ -0,0 +1,152 @@
+'use client'
+
+import { useEffect, useState } from 'react'
+import { useSearchParams, useRouter } from 'next/navigation'
+import PageLayout from '../components/PageLayout'
+import useAuthStore from '../store/authStore'
+
+export default function RegisterPage() {
+ const searchParams = useSearchParams()
+ const refToken = searchParams.get('ref')
+ const [registered, setRegistered] = useState(false)
+ const [mode, setMode] = useState<'personal' | 'company'>('personal')
+ const router = useRouter()
+
+ // Auth state
+ const user = useAuthStore(state => state.user)
+ const logout = useAuthStore(state => state.logout)
+
+ // Session management
+ const [showSessionModal, setShowSessionModal] = useState(false)
+ const [sessionCleared, setSessionCleared] = useState(false)
+
+ // Redirect to login on successful registration
+ useEffect(() => {
+ if (registered) {
+ // Show success toast would go here
+ setTimeout(() => router.push('/login'), 1200)
+ }
+ }, [registered, router])
+
+ // Check for existing session
+ useEffect(() => {
+ if (user && !sessionCleared) {
+ setShowSessionModal(true)
+ }
+ }, [user, sessionCleared])
+
+ const handleLogout = async () => {
+ await logout()
+ setSessionCleared(true)
+ setShowSessionModal(false)
+ }
+
+ const handleCancel = () => {
+ setShowSessionModal(false)
+ router.push('/dashboard')
+ }
+
+ // Block registration if session detected and not cleared
+ if (showSessionModal) {
+ return (
+
+
+
+
+ Aktive Sitzung erkannt
+
+
+ Du bist bereits angemeldet. Um dich zu registrieren, musst du dich zuerst abmelden.
+
+
+
+
+
+
+
+
+ )
+ }
+
+ // Prevent form rendering until session is cleared
+ if (!sessionCleared && user) {
+ return null
+ }
+
+ return (
+
+
+
+
+
+
+ Registrierung für Profit Planet
+
+ {refToken && (
+
+ Du wurdest eingeladen! Referral-Token: {refToken}
+
+ )}
+
+ {/* Mode Toggle */}
+
+
+
+
+
+
+
+
+
+ Registrierungsmodus: {mode === 'personal' ? 'Privatperson' : 'Unternehmen'}
+
+
+
+
+
+
+
+
+
+ )
+}
\ No newline at end of file
diff --git a/src/app/shop/page.tsx b/src/app/shop/page.tsx
new file mode 100644
index 0000000..483852c
--- /dev/null
+++ b/src/app/shop/page.tsx
@@ -0,0 +1,313 @@
+'use client'
+
+import { useState, useEffect } from 'react'
+import { StarIcon } from '@heroicons/react/20/solid'
+import { HeartIcon, ShoppingCartIcon } from '@heroicons/react/24/outline'
+import { HeartIcon as HeartIconSolid } from '@heroicons/react/24/solid'
+import PageLayout from '../components/PageLayout'
+
+// Mock-Produktdaten im Tailwind UI Plus Format
+const products = [
+ {
+ id: 1,
+ name: 'Premium Bio-Kaffee Starter Set',
+ price: '€24.99',
+ rating: 5,
+ reviewCount: 142,
+ imageSrc: 'https://images.unsplash.com/photo-1559056199-641a0ac8b55e?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80',
+ imageAlt: 'Premium Bio-Kaffee Set mit Bohnen und Filter',
+ href: '#',
+ category: 'Getränke',
+ inStock: true,
+ },
+ {
+ id: 2,
+ name: 'Nachhaltiger Laptop-Ständer',
+ price: '€89.99',
+ rating: 5,
+ reviewCount: 87,
+ imageSrc: 'https://images.unsplash.com/photo-1527864550417-7fd91fc51a46?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80',
+ imageAlt: 'Ergonomischer Laptop-Ständer aus Bambus',
+ href: '#',
+ category: 'Technik',
+ inStock: true,
+ },
+ {
+ id: 3,
+ name: 'Öko-Sportbekleidung Set',
+ price: '€149.99',
+ rating: 5,
+ reviewCount: 203,
+ imageSrc: 'https://images.unsplash.com/photo-1506629905607-b5f9a71351e8?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80',
+ imageAlt: 'Nachhaltige Sportkleidung aus recycelten Materialien',
+ href: '#',
+ category: 'Kleidung',
+ inStock: false,
+ },
+ {
+ id: 4,
+ name: 'Smart Home Energie-Monitor',
+ price: '€199.99',
+ rating: 4,
+ reviewCount: 156,
+ imageSrc: 'https://images.unsplash.com/photo-1558618666-fcd25c85cd64?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80',
+ imageAlt: 'Smart Home Gerät zur Energieüberwachung',
+ href: '#',
+ category: 'Technik',
+ inStock: true,
+ },
+ {
+ id: 5,
+ name: 'Bio-Hautpflege Starter-Set',
+ price: '€79.99',
+ rating: 4,
+ reviewCount: 92,
+ imageSrc: 'https://images.unsplash.com/photo-1556228578-8c89e6adf883?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80',
+ imageAlt: 'Natürliche Hautpflege Produkte ohne Chemikalien',
+ href: '#',
+ category: 'Beauty',
+ inStock: true,
+ },
+ {
+ id: 6,
+ name: 'Solarbetriebene Powerbank',
+ price: '€129.99',
+ rating: 5,
+ reviewCount: 78,
+ imageSrc: 'https://images.unsplash.com/photo-1609091839311-d5365f9ff1c5?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80',
+ imageAlt: 'Portable Solarenergie Powerbank',
+ href: '#',
+ category: 'Technik',
+ inStock: true,
+ },
+ {
+ id: 7,
+ name: 'Nachhaltige Trinkflasche',
+ price: '€25.99',
+ rating: 4,
+ reviewCount: 64,
+ imageSrc: 'https://images.unsplash.com/photo-1523362628745-0c100150b504?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80',
+ imageAlt: 'Wiederverwendbare Edelstahl Trinkflasche',
+ href: '#',
+ category: 'Lifestyle',
+ inStock: true,
+ },
+ {
+ id: 8,
+ name: 'Öko-Notizbuch Set',
+ price: '€19.99',
+ rating: 5,
+ reviewCount: 41,
+ imageSrc: 'https://images.unsplash.com/photo-1544716278-ca5e3f4abd8c?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80',
+ imageAlt: 'Recyceltes Papier Notizbuch Set',
+ href: '#',
+ category: 'Büro',
+ inStock: true,
+ },
+ {
+ id: 9,
+ name: 'Bambus Handy-Halterung',
+ price: '€32.99',
+ rating: 4,
+ reviewCount: 24,
+ imageSrc: 'https://images.unsplash.com/photo-1572635196237-14b3f281503f?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80',
+ imageAlt: 'Nachhaltige Bambus Handy-Halterung',
+ href: '#',
+ category: 'Technik',
+ inStock: true,
+ },
+]
+
+function classNames(...classes: (string | undefined | null | boolean)[]): string {
+ return classes.filter(Boolean).join(' ')
+}
+
+export default function ShopPage() {
+ const [favorites, setFavorites] = useState([])
+ const [isLoading, setIsLoading] = useState(true)
+
+ // Load favorites from localStorage on component mount
+ useEffect(() => {
+ const savedFavorites = localStorage.getItem('shop-favorites')
+ if (savedFavorites) {
+ try {
+ setFavorites(JSON.parse(savedFavorites))
+ } catch (error) {
+ console.error('Error parsing favorites from localStorage:', error)
+ }
+ }
+ setIsLoading(false)
+ }, [])
+
+ // Save favorites to localStorage whenever favorites change
+ useEffect(() => {
+ if (!isLoading) {
+ localStorage.setItem('shop-favorites', JSON.stringify(favorites))
+ }
+ }, [favorites, isLoading])
+
+ const toggleFavorite = (productId: number) => {
+ setFavorites(prev => {
+ const newFavorites = prev.includes(productId)
+ ? prev.filter(id => id !== productId)
+ : [...prev, productId]
+
+ // Show feedback to user
+ const product = products.find(p => p.id === productId)
+ if (product) {
+ if (newFavorites.includes(productId)) {
+ console.log(`❤️ ${product.name} zu Favoriten hinzugefügt`)
+ } else {
+ console.log(`💔 ${product.name} aus Favoriten entfernt`)
+ }
+ }
+
+ return newFavorites
+ })
+ }
+
+ const addToCart = (productId: number) => {
+ const product = products.find(p => p.id === productId)
+ if (product) {
+ console.log(`🛒 ${product.name} zum Warenkorb hinzugefügt`)
+ // Hier würde die echte Add-to-Cart Logik implementiert werden
+ // z.B. API-Call oder Zustand-Update
+ }
+ }
+
+ if (isLoading) {
+ return (
+
+
+
+
+
Shop wird geladen...
+
+
+
+ )
+ }
+
+ return (
+
+
+ {/* Header Section */}
+
+
+
+
Profit Planet Shop
+
+ Entdecke nachhaltige und innovative Produkte, die sowohl deinem Geldbeutel als auch dem Planeten helfen
+
+
+
+
+
+ {/* Products Section - Tailwind UI Plus "Product Grid" */}
+
+
+
+ )
+}
\ No newline at end of file
diff --git a/src/app/store/authStore.ts b/src/app/store/authStore.ts
new file mode 100644
index 0000000..2d43c9f
--- /dev/null
+++ b/src/app/store/authStore.ts
@@ -0,0 +1,206 @@
+import { create } from "zustand";
+import { log } from "../utils/logger";
+
+// Helper to decode JWT and get expiry
+function getTokenExpiry(token: string | null): Date | null {
+ if (!token) return null;
+ try {
+ const [, payload] = token.split(".");
+ const { exp } = JSON.parse(atob(payload));
+ return exp ? new Date(exp * 1000) : null;
+ } catch {
+ return null;
+ }
+}
+
+// Helper functions for sessionStorage with SSR safety
+const getStoredUser = () => {
+ if (typeof window === 'undefined') return null; // SSR check
+ try {
+ const userData = sessionStorage.getItem('user');
+ const parsed = userData ? JSON.parse(userData) : null;
+ log("👤 Retrieved user from sessionStorage:", parsed ? `${parsed.email || parsed.companyName}` : null);
+ return parsed;
+ } catch (error) {
+ log("❌ Error retrieving user from sessionStorage:", error);
+ return null;
+ }
+};
+
+interface User {
+ email?: string;
+ companyName?: string;
+ [key: string]: any;
+}
+
+interface AuthStore {
+ accessToken: string | null;
+ user: User | null;
+ isAuthReady: boolean;
+ isRefreshing: boolean;
+ refreshPromise: Promise | null;
+ setAuthReady: (ready: boolean) => void;
+ setAccessToken: (token: string | null) => void;
+ setUser: (userData: User | null) => void;
+ clearAuth: () => void;
+ logout: () => Promise;
+ refreshAuthToken: () => Promise;
+ getAuthState: () => AuthStore;
+}
+
+const useAuthStore = create((set, get) => ({
+ // Initialize with SSR-safe defaults
+ accessToken: null,
+ user: typeof window !== 'undefined' ? getStoredUser() : null,
+ isAuthReady: false,
+ isRefreshing: false,
+ refreshPromise: null,
+
+ setAuthReady: (ready) => {
+ log("🔔 Zustand: setAuthReady ->", ready);
+ set({ isAuthReady: !!ready });
+ },
+
+ setAccessToken: (token) => {
+ log("🔑 Zustand: Setting access token in memory:", token ? `${token.substring(0, 20)}...` : null);
+ if (token) {
+ const expiry = getTokenExpiry(token);
+ log("⏳ Zustand: Token expiry:", expiry ? expiry.toLocaleString() : "Unknown");
+ } else {
+ log("🗑️ Zustand: Clearing in-memory access token");
+ }
+ set({ accessToken: token });
+ },
+
+ setUser: (userData) => {
+ log("👤 Zustand: Setting user data:", userData ? `${userData.email || userData.companyName}` : null);
+ if (typeof window !== 'undefined') {
+ try {
+ if (userData) {
+ sessionStorage.setItem('user', JSON.stringify(userData));
+ log("✅ User data stored in sessionStorage successfully");
+ } else {
+ sessionStorage.removeItem('user');
+ log("🗑️ User data removed from sessionStorage");
+ }
+ } catch (error) {
+ log("❌ Error storing user in sessionStorage:", error);
+ }
+ }
+ set({ user: userData });
+ },
+
+ clearAuth: () => {
+ log("🧹 Zustand: Clearing all auth data from memory and removing persisted user");
+ if (typeof window !== 'undefined') {
+ try {
+ sessionStorage.removeItem('user');
+ log("✅ User cleared from sessionStorage");
+ sessionStorage.removeItem('accessToken');
+ log("✅ accessToken cleared from sessionStorage");
+ } catch (error) {
+ log("❌ Error clearing user/accessToken from sessionStorage:", error);
+ }
+ }
+ set({ accessToken: null, user: null });
+ },
+
+ logout: async () => {
+ log("🚪 Zustand: Logging out — revoking refresh token on server");
+ try {
+ const logoutUrl = `${process.env.NEXT_PUBLIC_API_BASE_URL}/api/logout`;
+ log("🌐 Zustand: Calling logout endpoint:", logoutUrl);
+ const res = await fetch(logoutUrl, {
+ method: "POST",
+ credentials: "include",
+ headers: {
+ "Content-Type": "application/json"
+ }
+ });
+ log("📡 Logout response status:", res.status);
+ try {
+ const body = await res.json().catch(() => null);
+ log("📦 Logout response body:", body);
+ } catch {}
+ // Attempt to clear refreshToken cookie client-side (will only work if not httpOnly)
+ if (typeof window !== 'undefined') {
+ document.cookie = "refreshToken=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT; Secure; SameSite=Strict";
+ }
+ } catch (error) {
+ log("❌ Error calling logout endpoint:", error);
+ } finally {
+ get().clearAuth();
+ get().setAuthReady(true);
+ }
+ },
+
+ refreshAuthToken: async () => {
+ // If there's already a refresh in flight, return that promise
+ if (get().refreshPromise) {
+ log("🔁 Zustand: refreshAuthToken - returning existing refresh promise");
+ return get().refreshPromise;
+ }
+
+ // SHORT-CIRCUIT: if we already have a valid accessToken that's not about to expire, skip refresh
+ const currentToken = get().accessToken;
+ if (currentToken) {
+ const expiry = getTokenExpiry(currentToken);
+ if (expiry && expiry.getTime() - Date.now() > 60 * 1000) { // more than 60s left
+ log("⏸️ Zustand: accessToken present and valid, skipping refresh");
+ return Promise.resolve(true);
+ }
+ }
+
+ log("🔄 Zustand: refreshAuthToken - starting new refresh");
+ // create promise so concurrent callers can await it
+ const p = (async () => {
+ set({ isRefreshing: true });
+ try {
+ const refreshUrl = `${process.env.NEXT_PUBLIC_API_BASE_URL}/api/refresh`;
+ log("🌐 Zustand: Calling refresh endpoint:", refreshUrl);
+
+ const res = await fetch(refreshUrl, {
+ method: "POST",
+ credentials: "include"
+ });
+
+ log("📡 Zustand: Refresh response status:", res.status);
+ const body = await res.json().catch(() => null);
+ log("📦 Zustand: Refresh response body:", body);
+
+ if (res.ok && body && body.accessToken) {
+ log("✅ Zustand: Refresh succeeded, setting in-memory token and user");
+ get().setAccessToken(body.accessToken);
+ if (body.user) get().setUser(body.user);
+ return true;
+ } else {
+ log("❌ Zustand: Refresh failed (no accessToken or non-ok). Clearing auth state");
+ get().clearAuth();
+ return false;
+ }
+ } catch (error) {
+ log("❌ Zustand: Refresh error:", error);
+ get().clearAuth();
+ return false;
+ } finally {
+ set({ isRefreshing: false, refreshPromise: null });
+ log("🔔 Zustand: refreshAuthToken - finished (flags cleared)");
+ }
+ })();
+
+ set({ refreshPromise: p });
+ return p;
+ },
+
+ getAuthState: () => {
+ const state = get();
+ log("📊 Current auth state:", {
+ hasToken: !!state.accessToken,
+ tokenPrefix: state.accessToken ? `${state.accessToken.substring(0, 20)}...` : null,
+ user: state.user ? `${state.user.email || state.user.companyName}` : null
+ });
+ return state;
+ }
+}));
+
+export default useAuthStore;
\ No newline at end of file
diff --git a/src/app/utils/authFetch.ts b/src/app/utils/authFetch.ts
new file mode 100644
index 0000000..1d87cb3
--- /dev/null
+++ b/src/app/utils/authFetch.ts
@@ -0,0 +1,115 @@
+import useAuthStore from "../store/authStore";
+import { log } from "./logger";
+
+// Helper to decode JWT and get expiry
+function getTokenExpiry(token: string | null): Date | null {
+ if (!token) return null;
+ try {
+ const [, payload] = token.split(".");
+ const { exp } = JSON.parse(atob(payload));
+ return exp ? new Date(exp * 1000) : null;
+ } catch {
+ return null;
+ }
+}
+
+// Helper to get the current accessToken from Zustand
+function getAccessToken(): string | null {
+ const token = useAuthStore.getState().accessToken;
+ log("🔑 authFetch: Getting token from Zustand:", token ? `${token.substring(0, 20)}...` : null);
+ if (token) {
+ const expiry = getTokenExpiry(token);
+ log("⏳ authFetch: Token expiry:", expiry ? expiry.toLocaleString() : "Unknown");
+ }
+ return token;
+}
+
+// Helper to update the accessToken in Zustand
+function setAccessToken(token: string | null): void {
+ log("🔑 authFetch: Updating token in Zustand (memory only):", token ? `${token.substring(0, 20)}...` : null);
+ if (token) {
+ const expiry = getTokenExpiry(token);
+ log("⏳ authFetch: New token expiry:", expiry ? expiry.toLocaleString() : "Unknown");
+ }
+ useAuthStore.getState().setAccessToken(token);
+}
+
+// Helper to clear auth state
+function clearAuth(): void {
+ log("🧹 authFetch: Clearing auth state client-side");
+ useAuthStore.getState().clearAuth();
+}
+
+interface CustomRequestInit extends RequestInit {
+ headers?: Record;
+}
+
+// Main authFetch function
+export async function authFetch(input: RequestInfo | URL, init: CustomRequestInit = {}): Promise {
+ const accessToken = getAccessToken();
+ const url = typeof input === "string" ? input : input instanceof URL ? input.href : (input as Request).url;
+
+ log("🌐 authFetch: Making API call to:", url);
+ log("🔑 authFetch: Using token:", accessToken ? `${accessToken.substring(0, 20)}...` : "No token");
+
+ // Add Authorization header if accessToken exists
+ const headers: Record = {
+ ...(init.headers || {}),
+ ...(accessToken ? { Authorization: `Bearer ${accessToken}` } : {}),
+ };
+
+ // Always send credentials so refresh cookie is included when server-side refresh is needed
+ const fetchWithAuth = async (hdrs: Record): Promise => {
+ log("📡 authFetch: Sending request", { url, headers: Object.keys(hdrs), credentials: "include" });
+ return fetch(url, { ...init, headers: hdrs, credentials: "include" });
+ };
+
+ let res: Response;
+ try {
+ res = await fetchWithAuth(headers);
+ } catch (err) {
+ log("❌ authFetch: Network/error calling API:", err);
+ throw err;
+ }
+ log("📡 authFetch: Response status:", res.status);
+
+ // If unauthorized, try to refresh token and retry once
+ if (res.status === 401) {
+ log("🔄 authFetch: 401 Unauthorized received. Attempting store.refreshAuthToken()...");
+ try {
+ // call centralized, deduped refresh in store
+ const refreshOk = await useAuthStore.getState().refreshAuthToken();
+ log("🔄 authFetch: store.refreshAuthToken() result:", refreshOk);
+
+ if (refreshOk) {
+ // get new token from memory (store already set it)
+ const newToken = getAccessToken();
+ log("🔁 authFetch: Retrieved new token from store after refresh:", newToken ? `${newToken.substring(0,20)}...` : null);
+
+ const retryHeaders = {
+ ...headers,
+ ...(newToken ? { Authorization: `Bearer ${newToken}` } : {}),
+ };
+
+ log("🔄 authFetch: Retrying original request with refreshed token (if available)");
+ res = await fetch(url, { ...init, headers: retryHeaders, credentials: "include" });
+ log("📡 authFetch: Retry response status:", res.status);
+ } else {
+ log("❌ authFetch: Refresh failed. Calling logout to revoke server cookie and clear client state");
+ await useAuthStore.getState().logout().catch((e) => {
+ log("❌ authFetch: logout error:", e);
+ useAuthStore.getState().clearAuth();
+ });
+ }
+ } catch (err) {
+ log("❌ authFetch: Error while refreshing token:", err);
+ await useAuthStore.getState().logout().catch((e) => {
+ log("❌ authFetch: logout error after refresh exception:", e);
+ useAuthStore.getState().clearAuth();
+ });
+ throw err;
+ }
+ }
+
+ return res;
+}
\ No newline at end of file
diff --git a/src/app/utils/cn.ts b/src/app/utils/cn.ts
new file mode 100644
index 0000000..c685f71
--- /dev/null
+++ b/src/app/utils/cn.ts
@@ -0,0 +1,4 @@
+// Simple className utility without external dependencies
+export function cn(...classes: (string | undefined | null | boolean)[]): string {
+ return classes.filter(Boolean).join(' ')
+}
\ No newline at end of file
diff --git a/src/app/utils/logger.ts b/src/app/utils/logger.ts
new file mode 100644
index 0000000..e17191c
--- /dev/null
+++ b/src/app/utils/logger.ts
@@ -0,0 +1,41 @@
+// Simple logger with different log levels
+const LOG_LEVELS = {
+ DEBUG: 0,
+ INFO: 1,
+ WARN: 2,
+ ERROR: 3
+} as const;
+
+type LogLevel = keyof typeof LOG_LEVELS;
+
+// Set current log level based on environment
+const currentLogLevel = process.env.NODE_ENV === 'production' ? LOG_LEVELS.WARN : LOG_LEVELS.DEBUG;
+
+function formatMessage(level: LogLevel, message: string, ...args: any[]): string {
+ const timestamp = new Date().toISOString();
+ return `[${timestamp}] [${level}] ${message}`;
+}
+
+export function log(message: string, ...args: any[]): void {
+ if (LOG_LEVELS.DEBUG >= currentLogLevel) {
+ console.log(formatMessage('DEBUG', message), ...args);
+ }
+}
+
+export function info(message: string, ...args: any[]): void {
+ if (LOG_LEVELS.INFO >= currentLogLevel) {
+ console.info(formatMessage('INFO', message), ...args);
+ }
+}
+
+export function warn(message: string, ...args: any[]): void {
+ if (LOG_LEVELS.WARN >= currentLogLevel) {
+ console.warn(formatMessage('WARN', message), ...args);
+ }
+}
+
+export function error(message: string, ...args: any[]): void {
+ if (LOG_LEVELS.ERROR >= currentLogLevel) {
+ console.error(formatMessage('ERROR', message), ...args);
+ }
+}
\ No newline at end of file
diff --git a/tailwind.config.js b/tailwind.config.js
new file mode 100644
index 0000000..797b002
--- /dev/null
+++ b/tailwind.config.js
@@ -0,0 +1,43 @@
+/** @type {import('tailwindcss').Config} */
+module.exports = {
+ content: [
+ "./src/**/*.{js,ts,jsx,tsx,mdx}",
+ "./app/**/*.{js,ts,jsx,tsx,mdx}",
+ "./node_modules/@tailwindui/react/**/*.js",
+ ],
+ theme: {
+ extend: {
+ colors: {
+ brand: {
+ accent: 'var(--color-brand-accent)',
+ header: 'var(--color-brand-header)',
+ text: 'var(--color-brand-text)',
+ background: 'var(--color-brand-background)',
+ }
+ },
+ animation: {
+ blob: "blob 7s infinite",
+ },
+ keyframes: {
+ blob: {
+ "0%": {
+ transform: "translate(0px, 0px) scale(1)",
+ },
+ "33%": {
+ transform: "translate(30px, -50px) scale(1.1)",
+ },
+ "66%": {
+ transform: "translate(-20px, 20px) scale(0.9)",
+ },
+ "100%": {
+ transform: "translate(0px, 0px) scale(1)",
+ },
+ },
+ },
+ },
+ },
+ plugins: [
+ require('@tailwindcss/forms'),
+ require('@tailwindcss/typography'),
+ ],
+};
\ No newline at end of file