diff --git a/.biomerc.json b/.biomerc.json
new file mode 100644
index 0000000..a680c1c
--- /dev/null
+++ b/.biomerc.json
@@ -0,0 +1,42 @@
+{
+ "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
+ "vcs": {
+ "enabled": false,
+ "clientKind": "git",
+ "useIgnoreFile": false
+ },
+ "files": {
+ "ignoreUnknown": false,
+ "ignore": ["dist/**", "node_modules/**"]
+ },
+ "formatter": {
+ "enabled": true,
+ "indentStyle": "space",
+ "indentWidth": 2,
+ "lineEnding": "lf",
+ "lineWidth": 120,
+ "attributePosition": "auto"
+ },
+ "organizeImports": {
+ "enabled": true
+ },
+ "linter": {
+ "enabled": true,
+ "rules": {
+ "recommended": true
+ }
+ },
+ "javascript": {
+ "formatter": {
+ "quoteStyle": "single",
+ "jsxQuoteStyle": "double",
+ "trailingCommas": "all",
+ "semicolons": "always",
+ "arrowParentheses": "always",
+ "bracketSpacing": true,
+ "bracketSameLine": false,
+ "quoteProperties": "asNeeded",
+ "attributePosition": "auto"
+ }
+ }
+}
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..bb8c3a2
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,2 @@
+# 루트에 .gitattributes 파일 생성 후 아래처럼 작성
+* text=auto eol=lf
\ No newline at end of file
diff --git a/.github/ISSUE_TEMPLATE/new-issue.yaml b/.github/ISSUE_TEMPLATE/new-issue.yaml
new file mode 100644
index 0000000..2ba595e
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/new-issue.yaml
@@ -0,0 +1,60 @@
+name: 새로운 작업
+description: 새로운 작업을 시작할 때 사용합니다.
+title: "[작업]"
+labels: ["작업", "기능개발"]
+assignees: []
+body:
+ - type: input
+ id: summary
+ attributes:
+ label: 📜 작업 상세히 작성
+ description: 이 작업에 대해 상세하게 설명해주세요.
+ placeholder: ex) 로그인 기능 구현, API 연결 등
+ validations:
+ required: true
+
+ - type: input
+ id: branch-name
+ attributes:
+ label: 🗂️ 브랜치 네이밍
+ description: 해당 작업에 대한 브랜치 이름을 지정해주세요.
+ placeholder: ex) feature/login-api, fix/button-bug
+ validations:
+ required: true
+
+ - type: input
+ id: due-date
+ attributes:
+ label: 📅 Due Date
+ description: 작업 마감일을 입력해주세요.
+ placeholder: ex) 2025/04/20
+ validations:
+ required: true
+
+ - type: input
+ id: references
+ attributes:
+ label: 🔗 References
+ description: 관련 자료나 참고 링크를 입력해주세요.
+ placeholder: ex) Jira, 문서 링크 등
+ validations:
+ required: false
+
+ - type: checkboxes
+ id: checklist
+ attributes:
+ label: ✅ 할 일 리스트
+ options:
+ - label: "기능 구현 계획"
+ - label: "브랜치 네이밍 검토"
+ - label: "테스트 계획"
+ - label: "문서화"
+
+ - type: input
+ id: milestone
+ attributes:
+ label: 🏆 마일스톤
+ description: 이 작업이 포함될 마일스톤을 선택하세요.
+ placeholder: 예시) 공통 컴포넌트 구현, MVP 개발 등
+ validations:
+ required: true
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
new file mode 100644
index 0000000..8d02ff5
--- /dev/null
+++ b/.github/pull_request_template.md
@@ -0,0 +1,43 @@
+## 📝 Summary
+
+
+
+## 🔧 Changes
+
+
+
+-
+-
+-
+
+## ✅ Checklist
+
+- [ ] 컨벤션을 준수하였습니다.
+- [ ] 변경 사항을 테스트하였습니다.
+- [ ] 설명을 충분히 작성하였습니다.
+- [ ] 올바른 브랜치에 PR을 보냈습니다.
+- [ ] 🤞 리뷰어의 마음을 사로잡았습니다.
+
+## 🚀 Test Plan
+
+
+
+-
+
+## 🖼️ Screenshots (UI 변경 시)
+
+
+
+| 변경 전 | 변경 후 |
+| ------- | ------- |
+| 이미지 | 이미지 |
+
+## 📚 Additional
+
+
+
+- 아마도 이 PR은 다크호스일지도... 🎩✨
+
+---
+
+> 🚨 _"모든 PR에는 커피가 필요하다!"_ ☕
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..2765267
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,32 @@
+name: Biome CI for React
+
+on:
+ pull_request:
+ branches: [main, dev]
+ push:
+ branches: [main, dev]
+
+jobs:
+ check-project:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: 📥 Checkout code
+ uses: actions/checkout@v4
+
+ - name: 🟢 Setup Node
+ uses: actions/setup-node@v3
+ with:
+ node-version: 22.x
+
+ - name: 📦 Install dependencies
+ run: npm ci
+
+ - name: 🔎 Run Biome Check
+ run: npm run check
+
+ - name: 🧪 Run Tests (optional)
+ run: npm test
+
+ - name: 🏗️ Build Project
+ run: npm run build
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..1adeaf6
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,26 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+package-lock.json
+dist
+dist-ssr
+*.local
+
+# Editor directories and files
+.vscode/*
+!.vscode/settings.json
+!.vscode/extensions.json
+.idea
+.DS_Store
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
diff --git a/.husky/pre-commit b/.husky/pre-commit
new file mode 100644
index 0000000..210877e
--- /dev/null
+++ b/.husky/pre-commit
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+npx lint-staged
\ No newline at end of file
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..819efcb
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,58 @@
+{
+ "editor.defaultFormatter": "biomejs.biome",
+ "editor.formatOnSave": true,
+
+ "[javascript]": {
+ "editor.defaultFormatter": "biomejs.biome"
+ },
+ "[javascriptreact]": {
+ "editor.defaultFormatter": "biomejs.biome"
+ },
+ "[json]": {
+ "editor.defaultFormatter": "biomejs.biome"
+ },
+ "cSpell.words": [
+ "ADOR",
+ "AESPA",
+ "Batchim",
+ "borderradius",
+ "CHAEWON",
+ "ENFP",
+ "ENTP",
+ "ESTJ",
+ "EUNCHAE",
+ "fancam",
+ "GAEUL",
+ "gsap",
+ "HAERIN",
+ "HAEWON",
+ "HANNI",
+ "HYEIN",
+ "INFJ",
+ "INFP",
+ "ISFJ",
+ "ISFP",
+ "ISTJ",
+ "ISTP",
+ "JANG",
+ "Jingyu",
+ "JIWOO",
+ "KAZUHA",
+ "LEESEO",
+ "mbti",
+ "MINJI",
+ "NINGNING",
+ "NMIXX",
+ "ILLIT",
+ "josa",
+ "Pretendard",
+ "SAKURA",
+ "slicktest",
+ "SSERAFIM",
+ "SULLYOON",
+ "Testpage",
+ "WONYOUNG",
+ "YUJIN",
+ "YUNJIN"
+ ]
+}
diff --git a/README.md b/README.md
index 6afb03b..727b9ac 100644
Binary files a/README.md and b/README.md differ
diff --git a/commitlint.config.js b/commitlint.config.js
new file mode 100644
index 0000000..422b194
--- /dev/null
+++ b/commitlint.config.js
@@ -0,0 +1 @@
+module.exports = { extends: ['@commitlint/config-conventional'] };
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..94d625c
--- /dev/null
+++ b/index.html
@@ -0,0 +1,41 @@
+
+
+
+
+
+ Fandom-K
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 0000000..fe583a9
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,5994 @@
+{
+ "name": "fandom-k",
+ "version": "0.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "fandom-k",
+ "version": "0.0.0",
+ "dependencies": {
+ "@emotion/css": "^11.13.5",
+ "@emotion/react": "^11.14.0",
+ "@emotion/styled": "^11.14.0",
+ "@studio-freight/lenis": "^1.0.42",
+ "axios": "^1.8.4",
+ "framer-motion": "^12.8.0",
+ "gsap": "^3.12.7",
+ "prop-types": "^15.8.1",
+ "react": "^19.0.0",
+ "react-dom": "^19.0.0",
+ "react-router-dom": "^7.5.0",
+ "react-slick": "^0.30.3",
+ "react-toastify": "^11.0.5",
+ "slick-carousel": "^1.8.1",
+ "uuid": "^11.1.0"
+ },
+ "devDependencies": {
+ "@biomejs/biome": "^1.9.4",
+ "@commitlint/cli": "^19.8.0",
+ "@commitlint/config-conventional": "^19.8.0",
+ "@eslint/js": "^9.21.0",
+ "@types/react": "^19.0.10",
+ "@types/react-dom": "^19.0.4",
+ "@vitejs/plugin-react": "^4.3.4",
+ "biome": "^0.2.2",
+ "esbuild": "^0.25.2",
+ "eslint": "^9.24.0",
+ "eslint-plugin-react-hooks": "^5.1.0",
+ "eslint-plugin-react-refresh": "^0.4.19",
+ "globals": "^15.15.0",
+ "husky": "^9.1.7",
+ "lint-staged": "^15.5.1",
+ "prettier": "^3.5.3",
+ "vite": "^6.2.0"
+ }
+ },
+ "node_modules/@ampproject/remapping": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz",
+ "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/code-frame": {
+ "version": "7.26.2",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz",
+ "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-validator-identifier": "^7.25.9",
+ "js-tokens": "^4.0.0",
+ "picocolors": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/compat-data": {
+ "version": "7.26.8",
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.8.tgz",
+ "integrity": "sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/core": {
+ "version": "7.26.10",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.10.tgz",
+ "integrity": "sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@ampproject/remapping": "^2.2.0",
+ "@babel/code-frame": "^7.26.2",
+ "@babel/generator": "^7.26.10",
+ "@babel/helper-compilation-targets": "^7.26.5",
+ "@babel/helper-module-transforms": "^7.26.0",
+ "@babel/helpers": "^7.26.10",
+ "@babel/parser": "^7.26.10",
+ "@babel/template": "^7.26.9",
+ "@babel/traverse": "^7.26.10",
+ "@babel/types": "^7.26.10",
+ "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/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==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "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==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/@babel/generator": {
+ "version": "7.27.0",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.0.tgz",
+ "integrity": "sha512-VybsKvpiN1gU1sdMZIp7FcqphVVKEwcuj02x73uvcHE0PTihx1nlBcowYWhDwjpoAXRv43+gDzyggGnn1XZhVw==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.27.0",
+ "@babel/types": "^7.27.0",
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.25",
+ "jsesc": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-compilation-targets": {
+ "version": "7.27.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.0.tgz",
+ "integrity": "sha512-LVk7fbXml0H2xH34dFzKQ7TDZ2G4/rVTOrq9V+icbbadjbVxxeFeDsNHv2SrZeWoA+6ZiTyWYWtScEIW07EAcA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/compat-data": "^7.26.8",
+ "@babel/helper-validator-option": "^7.25.9",
+ "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==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/@babel/helper-module-imports": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz",
+ "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/traverse": "^7.25.9",
+ "@babel/types": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-transforms": {
+ "version": "7.26.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz",
+ "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-module-imports": "^7.25.9",
+ "@babel/helper-validator-identifier": "^7.25.9",
+ "@babel/traverse": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-plugin-utils": {
+ "version": "7.26.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz",
+ "integrity": "sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-string-parser": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz",
+ "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-identifier": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz",
+ "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-option": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz",
+ "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helpers": {
+ "version": "7.27.0",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.0.tgz",
+ "integrity": "sha512-U5eyP/CTFPuNE3qk+WZMxFkp/4zUzdceQlfzf7DdGdhp+Fezd7HD+i8Y24ZuTMKX3wQBld449jijbGq6OdGNQg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/template": "^7.27.0",
+ "@babel/types": "^7.27.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/parser": {
+ "version": "7.27.0",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.0.tgz",
+ "integrity": "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.27.0"
+ },
+ "bin": {
+ "parser": "bin/babel-parser.js"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-react-jsx-self": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.25.9.tgz",
+ "integrity": "sha512-y8quW6p0WHkEhmErnfe58r7x0A70uKphQm8Sp8cV7tjNQwK56sNVK0M73LK3WuYmsuyrftut4xAkjjgU0twaMg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-react-jsx-source": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.25.9.tgz",
+ "integrity": "sha512-+iqjT8xmXhhYv4/uiYd8FNQsraMFZIfxVSqxxVSZP0WbbSAWvBXAul0m/zu+7Vv4O/3WtApy9pmaTMiumEZgfg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/runtime": {
+ "version": "7.27.0",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.0.tgz",
+ "integrity": "sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==",
+ "license": "MIT",
+ "dependencies": {
+ "regenerator-runtime": "^0.14.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/template": {
+ "version": "7.27.0",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.0.tgz",
+ "integrity": "sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.26.2",
+ "@babel/parser": "^7.27.0",
+ "@babel/types": "^7.27.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/traverse": {
+ "version": "7.27.0",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.0.tgz",
+ "integrity": "sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.26.2",
+ "@babel/generator": "^7.27.0",
+ "@babel/parser": "^7.27.0",
+ "@babel/template": "^7.27.0",
+ "@babel/types": "^7.27.0",
+ "debug": "^4.3.1",
+ "globals": "^11.1.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/traverse/node_modules/globals": {
+ "version": "11.12.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
+ "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/@babel/types": {
+ "version": "7.27.0",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz",
+ "integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-string-parser": "^7.25.9",
+ "@babel/helper-validator-identifier": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@biomejs/biome": {
+ "version": "1.9.4",
+ "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-1.9.4.tgz",
+ "integrity": "sha512-1rkd7G70+o9KkTn5KLmDYXihGoTaIGO9PIIN2ZB7UJxFrWw04CZHPYiMRjYsaDvVV7hP1dYNRLxSANLaBFGpog==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT OR Apache-2.0",
+ "bin": {
+ "biome": "bin/biome"
+ },
+ "engines": {
+ "node": ">=14.21.3"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/biome"
+ },
+ "optionalDependencies": {
+ "@biomejs/cli-darwin-arm64": "1.9.4",
+ "@biomejs/cli-darwin-x64": "1.9.4",
+ "@biomejs/cli-linux-arm64": "1.9.4",
+ "@biomejs/cli-linux-arm64-musl": "1.9.4",
+ "@biomejs/cli-linux-x64": "1.9.4",
+ "@biomejs/cli-linux-x64-musl": "1.9.4",
+ "@biomejs/cli-win32-arm64": "1.9.4",
+ "@biomejs/cli-win32-x64": "1.9.4"
+ }
+ },
+ "node_modules/@biomejs/cli-darwin-arm64": {
+ "version": "1.9.4",
+ "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-1.9.4.tgz",
+ "integrity": "sha512-bFBsPWrNvkdKrNCYeAp+xo2HecOGPAy9WyNyB/jKnnedgzl4W4Hb9ZMzYNbf8dMCGmUdSavlYHiR01QaYR58cw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT OR Apache-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=14.21.3"
+ }
+ },
+ "node_modules/@biomejs/cli-darwin-x64": {
+ "version": "1.9.4",
+ "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-1.9.4.tgz",
+ "integrity": "sha512-ngYBh/+bEedqkSevPVhLP4QfVPCpb+4BBe2p7Xs32dBgs7rh9nY2AIYUL6BgLw1JVXV8GlpKmb/hNiuIxfPfZg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT OR Apache-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=14.21.3"
+ }
+ },
+ "node_modules/@biomejs/cli-linux-arm64": {
+ "version": "1.9.4",
+ "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-1.9.4.tgz",
+ "integrity": "sha512-fJIW0+LYujdjUgJJuwesP4EjIBl/N/TcOX3IvIHJQNsAqvV2CHIogsmA94BPG6jZATS4Hi+xv4SkBBQSt1N4/g==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT OR Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=14.21.3"
+ }
+ },
+ "node_modules/@biomejs/cli-linux-arm64-musl": {
+ "version": "1.9.4",
+ "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-1.9.4.tgz",
+ "integrity": "sha512-v665Ct9WCRjGa8+kTr0CzApU0+XXtRgwmzIf1SeKSGAv+2scAlW6JR5PMFo6FzqqZ64Po79cKODKf3/AAmECqA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT OR Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=14.21.3"
+ }
+ },
+ "node_modules/@biomejs/cli-linux-x64": {
+ "version": "1.9.4",
+ "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-1.9.4.tgz",
+ "integrity": "sha512-lRCJv/Vi3Vlwmbd6K+oQ0KhLHMAysN8lXoCI7XeHlxaajk06u7G+UsFSO01NAs5iYuWKmVZjmiOzJ0OJmGsMwg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT OR Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=14.21.3"
+ }
+ },
+ "node_modules/@biomejs/cli-linux-x64-musl": {
+ "version": "1.9.4",
+ "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-1.9.4.tgz",
+ "integrity": "sha512-gEhi/jSBhZ2m6wjV530Yy8+fNqG8PAinM3oV7CyO+6c3CEh16Eizm21uHVsyVBEB6RIM8JHIl6AGYCv6Q6Q9Tg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT OR Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=14.21.3"
+ }
+ },
+ "node_modules/@biomejs/cli-win32-arm64": {
+ "version": "1.9.4",
+ "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-1.9.4.tgz",
+ "integrity": "sha512-tlbhLk+WXZmgwoIKwHIHEBZUwxml7bRJgk0X2sPyNR3S93cdRq6XulAZRQJ17FYGGzWne0fgrXBKpl7l4M87Hg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT OR Apache-2.0",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=14.21.3"
+ }
+ },
+ "node_modules/@biomejs/cli-win32-x64": {
+ "version": "1.9.4",
+ "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-1.9.4.tgz",
+ "integrity": "sha512-8Y5wMhVIPaWe6jw2H+KlEm4wP/f7EW3810ZLmDlrEEy5KvBsb9ECEfu/kMWD484ijfQ8+nIi0giMgu9g1UAuuA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT OR Apache-2.0",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=14.21.3"
+ }
+ },
+ "node_modules/@commitlint/cli": {
+ "version": "19.8.0",
+ "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-19.8.0.tgz",
+ "integrity": "sha512-t/fCrLVu+Ru01h0DtlgHZXbHV2Y8gKocTR5elDOqIRUzQd0/6hpt2VIWOj9b3NDo7y4/gfxeR2zRtXq/qO6iUg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@commitlint/format": "^19.8.0",
+ "@commitlint/lint": "^19.8.0",
+ "@commitlint/load": "^19.8.0",
+ "@commitlint/read": "^19.8.0",
+ "@commitlint/types": "^19.8.0",
+ "tinyexec": "^0.3.0",
+ "yargs": "^17.0.0"
+ },
+ "bin": {
+ "commitlint": "cli.js"
+ },
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/config-conventional": {
+ "version": "19.8.0",
+ "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-19.8.0.tgz",
+ "integrity": "sha512-9I2kKJwcAPwMoAj38hwqFXG0CzS2Kj+SAByPUQ0SlHTfb7VUhYVmo7G2w2tBrqmOf7PFd6MpZ/a1GQJo8na8kw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@commitlint/types": "^19.8.0",
+ "conventional-changelog-conventionalcommits": "^7.0.2"
+ },
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/config-validator": {
+ "version": "19.8.0",
+ "resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-19.8.0.tgz",
+ "integrity": "sha512-+r5ZvD/0hQC3w5VOHJhGcCooiAVdynFlCe2d6I9dU+PvXdV3O+fU4vipVg+6hyLbQUuCH82mz3HnT/cBQTYYuA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@commitlint/types": "^19.8.0",
+ "ajv": "^8.11.0"
+ },
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/ensure": {
+ "version": "19.8.0",
+ "resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-19.8.0.tgz",
+ "integrity": "sha512-kNiNU4/bhEQ/wutI1tp1pVW1mQ0QbAjfPRo5v8SaxoVV+ARhkB8Wjg3BSseNYECPzWWfg/WDqQGIfV1RaBFQZg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@commitlint/types": "^19.8.0",
+ "lodash.camelcase": "^4.3.0",
+ "lodash.kebabcase": "^4.1.1",
+ "lodash.snakecase": "^4.1.1",
+ "lodash.startcase": "^4.4.0",
+ "lodash.upperfirst": "^4.3.1"
+ },
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/execute-rule": {
+ "version": "19.8.0",
+ "resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-19.8.0.tgz",
+ "integrity": "sha512-fuLeI+EZ9x2v/+TXKAjplBJWI9CNrHnyi5nvUQGQt4WRkww/d95oVRsc9ajpt4xFrFmqMZkd/xBQHZDvALIY7A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/format": {
+ "version": "19.8.0",
+ "resolved": "https://registry.npmjs.org/@commitlint/format/-/format-19.8.0.tgz",
+ "integrity": "sha512-EOpA8IERpQstxwp/WGnDArA7S+wlZDeTeKi98WMOvaDLKbjptuHWdOYYr790iO7kTCif/z971PKPI2PkWMfOxg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@commitlint/types": "^19.8.0",
+ "chalk": "^5.3.0"
+ },
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/is-ignored": {
+ "version": "19.8.0",
+ "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-19.8.0.tgz",
+ "integrity": "sha512-L2Jv9yUg/I+jF3zikOV0rdiHUul9X3a/oU5HIXhAJLE2+TXTnEBfqYP9G5yMw/Yb40SnR764g4fyDK6WR2xtpw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@commitlint/types": "^19.8.0",
+ "semver": "^7.6.0"
+ },
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/lint": {
+ "version": "19.8.0",
+ "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-19.8.0.tgz",
+ "integrity": "sha512-+/NZKyWKSf39FeNpqhfMebmaLa1P90i1Nrb1SrA7oSU5GNN/lksA4z6+ZTnsft01YfhRZSYMbgGsARXvkr/VLQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@commitlint/is-ignored": "^19.8.0",
+ "@commitlint/parse": "^19.8.0",
+ "@commitlint/rules": "^19.8.0",
+ "@commitlint/types": "^19.8.0"
+ },
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/load": {
+ "version": "19.8.0",
+ "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-19.8.0.tgz",
+ "integrity": "sha512-4rvmm3ff81Sfb+mcWT5WKlyOa+Hd33WSbirTVUer0wjS1Hv/Hzr07Uv1ULIV9DkimZKNyOwXn593c+h8lsDQPQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@commitlint/config-validator": "^19.8.0",
+ "@commitlint/execute-rule": "^19.8.0",
+ "@commitlint/resolve-extends": "^19.8.0",
+ "@commitlint/types": "^19.8.0",
+ "chalk": "^5.3.0",
+ "cosmiconfig": "^9.0.0",
+ "cosmiconfig-typescript-loader": "^6.1.0",
+ "lodash.isplainobject": "^4.0.6",
+ "lodash.merge": "^4.6.2",
+ "lodash.uniq": "^4.5.0"
+ },
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/message": {
+ "version": "19.8.0",
+ "resolved": "https://registry.npmjs.org/@commitlint/message/-/message-19.8.0.tgz",
+ "integrity": "sha512-qs/5Vi9bYjf+ZV40bvdCyBn5DvbuelhR6qewLE8Bh476F7KnNyLfdM/ETJ4cp96WgeeHo6tesA2TMXS0sh5X4A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/parse": {
+ "version": "19.8.0",
+ "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-19.8.0.tgz",
+ "integrity": "sha512-YNIKAc4EXvNeAvyeEnzgvm1VyAe0/b3Wax7pjJSwXuhqIQ1/t2hD3OYRXb6D5/GffIvaX82RbjD+nWtMZCLL7Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@commitlint/types": "^19.8.0",
+ "conventional-changelog-angular": "^7.0.0",
+ "conventional-commits-parser": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/read": {
+ "version": "19.8.0",
+ "resolved": "https://registry.npmjs.org/@commitlint/read/-/read-19.8.0.tgz",
+ "integrity": "sha512-6ywxOGYajcxK1y1MfzrOnwsXO6nnErna88gRWEl3qqOOP8MDu/DTeRkGLXBFIZuRZ7mm5yyxU5BmeUvMpNte5w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@commitlint/top-level": "^19.8.0",
+ "@commitlint/types": "^19.8.0",
+ "git-raw-commits": "^4.0.0",
+ "minimist": "^1.2.8",
+ "tinyexec": "^0.3.0"
+ },
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/resolve-extends": {
+ "version": "19.8.0",
+ "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-19.8.0.tgz",
+ "integrity": "sha512-CLanRQwuG2LPfFVvrkTrBR/L/DMy3+ETsgBqW1OvRxmzp/bbVJW0Xw23LnnExgYcsaFtos967lul1CsbsnJlzQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@commitlint/config-validator": "^19.8.0",
+ "@commitlint/types": "^19.8.0",
+ "global-directory": "^4.0.1",
+ "import-meta-resolve": "^4.0.0",
+ "lodash.mergewith": "^4.6.2",
+ "resolve-from": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/rules": {
+ "version": "19.8.0",
+ "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-19.8.0.tgz",
+ "integrity": "sha512-IZ5IE90h6DSWNuNK/cwjABLAKdy8tP8OgGVGbXe1noBEX5hSsu00uRlLu6JuruiXjWJz2dZc+YSw3H0UZyl/mA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@commitlint/ensure": "^19.8.0",
+ "@commitlint/message": "^19.8.0",
+ "@commitlint/to-lines": "^19.8.0",
+ "@commitlint/types": "^19.8.0"
+ },
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/to-lines": {
+ "version": "19.8.0",
+ "resolved": "https://registry.npmjs.org/@commitlint/to-lines/-/to-lines-19.8.0.tgz",
+ "integrity": "sha512-3CKLUw41Cur8VMjh16y8LcsOaKbmQjAKCWlXx6B0vOUREplp6em9uIVhI8Cv934qiwkbi2+uv+mVZPnXJi1o9A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/top-level": {
+ "version": "19.8.0",
+ "resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-19.8.0.tgz",
+ "integrity": "sha512-Rphgoc/omYZisoNkcfaBRPQr4myZEHhLPx2/vTXNLjiCw4RgfPR1wEgUpJ9OOmDCiv5ZyIExhprNLhteqH4FuQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "find-up": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/types": {
+ "version": "19.8.0",
+ "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-19.8.0.tgz",
+ "integrity": "sha512-LRjP623jPyf3Poyfb0ohMj8I3ORyBDOwXAgxxVPbSD0unJuW2mJWeiRfaQinjtccMqC5Wy1HOMfa4btKjbNxbg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/conventional-commits-parser": "^5.0.0",
+ "chalk": "^5.3.0"
+ },
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@emotion/babel-plugin": {
+ "version": "11.13.5",
+ "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz",
+ "integrity": "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-module-imports": "^7.16.7",
+ "@babel/runtime": "^7.18.3",
+ "@emotion/hash": "^0.9.2",
+ "@emotion/memoize": "^0.9.0",
+ "@emotion/serialize": "^1.3.3",
+ "babel-plugin-macros": "^3.1.0",
+ "convert-source-map": "^1.5.0",
+ "escape-string-regexp": "^4.0.0",
+ "find-root": "^1.1.0",
+ "source-map": "^0.5.7",
+ "stylis": "4.2.0"
+ }
+ },
+ "node_modules/@emotion/cache": {
+ "version": "11.14.0",
+ "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.14.0.tgz",
+ "integrity": "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==",
+ "license": "MIT",
+ "dependencies": {
+ "@emotion/memoize": "^0.9.0",
+ "@emotion/sheet": "^1.4.0",
+ "@emotion/utils": "^1.4.2",
+ "@emotion/weak-memoize": "^0.4.0",
+ "stylis": "4.2.0"
+ }
+ },
+ "node_modules/@emotion/css": {
+ "version": "11.13.5",
+ "resolved": "https://registry.npmjs.org/@emotion/css/-/css-11.13.5.tgz",
+ "integrity": "sha512-wQdD0Xhkn3Qy2VNcIzbLP9MR8TafI0MJb7BEAXKp+w4+XqErksWR4OXomuDzPsN4InLdGhVe6EYcn2ZIUCpB8w==",
+ "license": "MIT",
+ "dependencies": {
+ "@emotion/babel-plugin": "^11.13.5",
+ "@emotion/cache": "^11.13.5",
+ "@emotion/serialize": "^1.3.3",
+ "@emotion/sheet": "^1.4.0",
+ "@emotion/utils": "^1.4.2"
+ }
+ },
+ "node_modules/@emotion/hash": {
+ "version": "0.9.2",
+ "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz",
+ "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==",
+ "license": "MIT"
+ },
+ "node_modules/@emotion/is-prop-valid": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.3.1.tgz",
+ "integrity": "sha512-/ACwoqx7XQi9knQs/G0qKvv5teDMhD7bXYns9N/wM8ah8iNb8jZ2uNO0YOgiq2o2poIvVtJS2YALasQuMSQ7Kw==",
+ "license": "MIT",
+ "dependencies": {
+ "@emotion/memoize": "^0.9.0"
+ }
+ },
+ "node_modules/@emotion/memoize": {
+ "version": "0.9.0",
+ "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz",
+ "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==",
+ "license": "MIT"
+ },
+ "node_modules/@emotion/react": {
+ "version": "11.14.0",
+ "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz",
+ "integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.18.3",
+ "@emotion/babel-plugin": "^11.13.5",
+ "@emotion/cache": "^11.14.0",
+ "@emotion/serialize": "^1.3.3",
+ "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0",
+ "@emotion/utils": "^1.4.2",
+ "@emotion/weak-memoize": "^0.4.0",
+ "hoist-non-react-statics": "^3.3.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.8.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@emotion/serialize": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz",
+ "integrity": "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==",
+ "license": "MIT",
+ "dependencies": {
+ "@emotion/hash": "^0.9.2",
+ "@emotion/memoize": "^0.9.0",
+ "@emotion/unitless": "^0.10.0",
+ "@emotion/utils": "^1.4.2",
+ "csstype": "^3.0.2"
+ }
+ },
+ "node_modules/@emotion/sheet": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz",
+ "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==",
+ "license": "MIT"
+ },
+ "node_modules/@emotion/styled": {
+ "version": "11.14.0",
+ "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.14.0.tgz",
+ "integrity": "sha512-XxfOnXFffatap2IyCeJyNov3kiDQWoR08gPUQxvbL7fxKryGBKUZUkG6Hz48DZwVrJSVh9sJboyV1Ds4OW6SgA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.18.3",
+ "@emotion/babel-plugin": "^11.13.5",
+ "@emotion/is-prop-valid": "^1.3.0",
+ "@emotion/serialize": "^1.3.3",
+ "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0",
+ "@emotion/utils": "^1.4.2"
+ },
+ "peerDependencies": {
+ "@emotion/react": "^11.0.0-rc.0",
+ "react": ">=16.8.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@emotion/unitless": {
+ "version": "0.10.0",
+ "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz",
+ "integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==",
+ "license": "MIT"
+ },
+ "node_modules/@emotion/use-insertion-effect-with-fallbacks": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.2.0.tgz",
+ "integrity": "sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": ">=16.8.0"
+ }
+ },
+ "node_modules/@emotion/utils": {
+ "version": "1.4.2",
+ "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz",
+ "integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==",
+ "license": "MIT"
+ },
+ "node_modules/@emotion/weak-memoize": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz",
+ "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==",
+ "license": "MIT"
+ },
+ "node_modules/@esbuild/aix-ppc64": {
+ "version": "0.25.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.3.tgz",
+ "integrity": "sha512-W8bFfPA8DowP8l//sxjJLSLkD8iEjMc7cBVyP+u4cEv9sM7mdUCkgsj+t0n/BWPFtv7WWCN5Yzj0N6FJNUUqBQ==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm": {
+ "version": "0.25.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.3.tgz",
+ "integrity": "sha512-PuwVXbnP87Tcff5I9ngV0lmiSu40xw1At6i3GsU77U7cjDDB4s0X2cyFuBiDa1SBk9DnvWwnGvVaGBqoFWPb7A==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm64": {
+ "version": "0.25.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.3.tgz",
+ "integrity": "sha512-XelR6MzjlZuBM4f5z2IQHK6LkK34Cvv6Rj2EntER3lwCBFdg6h2lKbtRjpTTsdEjD/WSe1q8UyPBXP1x3i/wYQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-x64": {
+ "version": "0.25.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.3.tgz",
+ "integrity": "sha512-ogtTpYHT/g1GWS/zKM0cc/tIebFjm1F9Aw1boQ2Y0eUQ+J89d0jFY//s9ei9jVIlkYi8AfOjiixcLJSGNSOAdQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-arm64": {
+ "version": "0.25.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.3.tgz",
+ "integrity": "sha512-eESK5yfPNTqpAmDfFWNsOhmIOaQA59tAcF/EfYvo5/QWQCzXn5iUSOnqt3ra3UdzBv073ykTtmeLJZGt3HhA+w==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-x64": {
+ "version": "0.25.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.3.tgz",
+ "integrity": "sha512-Kd8glo7sIZtwOLcPbW0yLpKmBNWMANZhrC1r6K++uDR2zyzb6AeOYtI6udbtabmQpFaxJ8uduXMAo1gs5ozz8A==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.25.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.3.tgz",
+ "integrity": "sha512-EJiyS70BYybOBpJth3M0KLOus0n+RRMKTYzhYhFeMwp7e/RaajXvP+BWlmEXNk6uk+KAu46j/kaQzr6au+JcIw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-x64": {
+ "version": "0.25.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.3.tgz",
+ "integrity": "sha512-Q+wSjaLpGxYf7zC0kL0nDlhsfuFkoN+EXrx2KSB33RhinWzejOd6AvgmP5JbkgXKmjhmpfgKZq24pneodYqE8Q==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm": {
+ "version": "0.25.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.3.tgz",
+ "integrity": "sha512-dUOVmAUzuHy2ZOKIHIKHCm58HKzFqd+puLaS424h6I85GlSDRZIA5ycBixb3mFgM0Jdh+ZOSB6KptX30DD8YOQ==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm64": {
+ "version": "0.25.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.3.tgz",
+ "integrity": "sha512-xCUgnNYhRD5bb1C1nqrDV1PfkwgbswTTBRbAd8aH5PhYzikdf/ddtsYyMXFfGSsb/6t6QaPSzxtbfAZr9uox4A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ia32": {
+ "version": "0.25.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.3.tgz",
+ "integrity": "sha512-yplPOpczHOO4jTYKmuYuANI3WhvIPSVANGcNUeMlxH4twz/TeXuzEP41tGKNGWJjuMhotpGabeFYGAOU2ummBw==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-loong64": {
+ "version": "0.25.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.3.tgz",
+ "integrity": "sha512-P4BLP5/fjyihmXCELRGrLd793q/lBtKMQl8ARGpDxgzgIKJDRJ/u4r1A/HgpBpKpKZelGct2PGI4T+axcedf6g==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-mips64el": {
+ "version": "0.25.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.3.tgz",
+ "integrity": "sha512-eRAOV2ODpu6P5divMEMa26RRqb2yUoYsuQQOuFUexUoQndm4MdpXXDBbUoKIc0iPa4aCO7gIhtnYomkn2x+bag==",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ppc64": {
+ "version": "0.25.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.3.tgz",
+ "integrity": "sha512-ZC4jV2p7VbzTlnl8nZKLcBkfzIf4Yad1SJM4ZMKYnJqZFD4rTI+pBG65u8ev4jk3/MPwY9DvGn50wi3uhdaghg==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-riscv64": {
+ "version": "0.25.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.3.tgz",
+ "integrity": "sha512-LDDODcFzNtECTrUUbVCs6j9/bDVqy7DDRsuIXJg6so+mFksgwG7ZVnTruYi5V+z3eE5y+BJZw7VvUadkbfg7QA==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-s390x": {
+ "version": "0.25.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.3.tgz",
+ "integrity": "sha512-s+w/NOY2k0yC2p9SLen+ymflgcpRkvwwa02fqmAwhBRI3SC12uiS10edHHXlVWwfAagYSY5UpmT/zISXPMW3tQ==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-x64": {
+ "version": "0.25.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.3.tgz",
+ "integrity": "sha512-nQHDz4pXjSDC6UfOE1Fw9Q8d6GCAd9KdvMZpfVGWSJztYCarRgSDfOVBY5xwhQXseiyxapkiSJi/5/ja8mRFFA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-arm64": {
+ "version": "0.25.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.3.tgz",
+ "integrity": "sha512-1QaLtOWq0mzK6tzzp0jRN3eccmN3hezey7mhLnzC6oNlJoUJz4nym5ZD7mDnS/LZQgkrhEbEiTn515lPeLpgWA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-x64": {
+ "version": "0.25.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.3.tgz",
+ "integrity": "sha512-i5Hm68HXHdgv8wkrt+10Bc50zM0/eonPb/a/OFVfB6Qvpiirco5gBA5bz7S2SHuU+Y4LWn/zehzNX14Sp4r27g==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-arm64": {
+ "version": "0.25.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.3.tgz",
+ "integrity": "sha512-zGAVApJEYTbOC6H/3QBr2mq3upG/LBEXr85/pTtKiv2IXcgKV0RT0QA/hSXZqSvLEpXeIxah7LczB4lkiYhTAQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-x64": {
+ "version": "0.25.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.3.tgz",
+ "integrity": "sha512-fpqctI45NnCIDKBH5AXQBsD0NDPbEFczK98hk/aa6HJxbl+UtLkJV2+Bvy5hLSLk3LHmqt0NTkKNso1A9y1a4w==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/sunos-x64": {
+ "version": "0.25.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.3.tgz",
+ "integrity": "sha512-ROJhm7d8bk9dMCUZjkS8fgzsPAZEjtRJqCAmVgB0gMrvG7hfmPmz9k1rwO4jSiblFjYmNvbECL9uhaPzONMfgA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-arm64": {
+ "version": "0.25.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.3.tgz",
+ "integrity": "sha512-YWcow8peiHpNBiIXHwaswPnAXLsLVygFwCB3A7Bh5jRkIBFWHGmNQ48AlX4xDvQNoMZlPYzjVOQDYEzWCqufMQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-ia32": {
+ "version": "0.25.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.3.tgz",
+ "integrity": "sha512-qspTZOIGoXVS4DpNqUYUs9UxVb04khS1Degaw/MnfMe7goQ3lTfQ13Vw4qY/Nj0979BGvMRpAYbs/BAxEvU8ew==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-x64": {
+ "version": "0.25.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.3.tgz",
+ "integrity": "sha512-ICgUR+kPimx0vvRzf+N/7L7tVSQeE3BYY+NhHRHXS1kBuPO7z2+7ea2HbhDyZdTephgvNvKrlDDKUexuCVBVvg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@eslint-community/eslint-utils": {
+ "version": "4.6.1",
+ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.6.1.tgz",
+ "integrity": "sha512-KTsJMmobmbrFLe3LDh0PC2FXpcSYJt/MLjlkh/9LEnmKYLSYmT/0EW9JWANjeoemiuZrmogti0tW5Ch+qNUYDw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "eslint-visitor-keys": "^3.4.3"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
+ }
+ },
+ "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint-community/regexpp": {
+ "version": "4.12.1",
+ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz",
+ "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
+ }
+ },
+ "node_modules/@eslint/config-array": {
+ "version": "0.20.0",
+ "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.20.0.tgz",
+ "integrity": "sha512-fxlS1kkIjx8+vy2SjuCB94q3htSNrufYTXubwiBFeaQHbH6Ipi43gFJq2zCMt6PHhImH3Xmr0NksKDvchWlpQQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/object-schema": "^2.1.6",
+ "debug": "^4.3.1",
+ "minimatch": "^3.1.2"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/config-helpers": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.2.1.tgz",
+ "integrity": "sha512-RI17tsD2frtDu/3dmI7QRrD4bedNKPM08ziRYaC5AhkGrzIAJelm9kJU1TznK+apx6V+cqRz8tfpEeG3oIyjxw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/core": {
+ "version": "0.13.0",
+ "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.13.0.tgz",
+ "integrity": "sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@types/json-schema": "^7.0.15"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/eslintrc": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz",
+ "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ajv": "^6.12.4",
+ "debug": "^4.3.2",
+ "espree": "^10.0.1",
+ "globals": "^14.0.0",
+ "ignore": "^5.2.0",
+ "import-fresh": "^3.2.1",
+ "js-yaml": "^4.1.0",
+ "minimatch": "^3.1.2",
+ "strip-json-comments": "^3.1.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint/eslintrc/node_modules/ajv": {
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "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/eslintrc/node_modules/json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@eslint/js": {
+ "version": "9.25.1",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.25.1.tgz",
+ "integrity": "sha512-dEIwmjntEx8u3Uvv+kr3PDeeArL8Hw07H9kyYxCjnM9pBjfEhk6uLXSchxxzgiwtRhhzVzqmUSDFBOi1TuZ7qg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/object-schema": {
+ "version": "2.1.6",
+ "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz",
+ "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/plugin-kit": {
+ "version": "0.2.8",
+ "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.8.tgz",
+ "integrity": "sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/core": "^0.13.0",
+ "levn": "^0.4.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@humanfs/core": {
+ "version": "0.19.1",
+ "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
+ "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18.0"
+ }
+ },
+ "node_modules/@humanfs/node": {
+ "version": "0.16.6",
+ "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz",
+ "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@humanfs/core": "^0.19.1",
+ "@humanwhocodes/retry": "^0.3.0"
+ },
+ "engines": {
+ "node": ">=18.18.0"
+ }
+ },
+ "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz",
+ "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@humanwhocodes/module-importer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+ "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=12.22"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@humanwhocodes/retry": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.2.tgz",
+ "integrity": "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@jridgewell/gen-mapping": {
+ "version": "0.3.8",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz",
+ "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==",
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/set-array": "^1.2.1",
+ "@jridgewell/sourcemap-codec": "^1.4.10",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/set-array": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
+ "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
+ "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==",
+ "license": "MIT"
+ },
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.25",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
+ "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
+ }
+ },
+ "node_modules/@rollup/rollup-android-arm-eabi": {
+ "version": "4.40.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.40.0.tgz",
+ "integrity": "sha512-+Fbls/diZ0RDerhE8kyC6hjADCXA1K4yVNlH0EYfd2XjyH0UGgzaQ8MlT0pCXAThfxv3QUAczHaL+qSv1E4/Cg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-android-arm64": {
+ "version": "4.40.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.40.0.tgz",
+ "integrity": "sha512-PPA6aEEsTPRz+/4xxAmaoWDqh67N7wFbgFUJGMnanCFs0TV99M0M8QhhaSCks+n6EbQoFvLQgYOGXxlMGQe/6w==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-arm64": {
+ "version": "4.40.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.40.0.tgz",
+ "integrity": "sha512-GwYOcOakYHdfnjjKwqpTGgn5a6cUX7+Ra2HeNj/GdXvO2VJOOXCiYYlRFU4CubFM67EhbmzLOmACKEfvp3J1kQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-x64": {
+ "version": "4.40.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.40.0.tgz",
+ "integrity": "sha512-CoLEGJ+2eheqD9KBSxmma6ld01czS52Iw0e2qMZNpPDlf7Z9mj8xmMemxEucinev4LgHalDPczMyxzbq+Q+EtA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-arm64": {
+ "version": "4.40.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.40.0.tgz",
+ "integrity": "sha512-r7yGiS4HN/kibvESzmrOB/PxKMhPTlz+FcGvoUIKYoTyGd5toHp48g1uZy1o1xQvybwwpqpe010JrcGG2s5nkg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-x64": {
+ "version": "4.40.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.40.0.tgz",
+ "integrity": "sha512-mVDxzlf0oLzV3oZOr0SMJ0lSDd3xC4CmnWJ8Val8isp9jRGl5Dq//LLDSPFrasS7pSm6m5xAcKaw3sHXhBjoRw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
+ "version": "4.40.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.40.0.tgz",
+ "integrity": "sha512-y/qUMOpJxBMy8xCXD++jeu8t7kzjlOCkoxxajL58G62PJGBZVl/Gwpm7JK9+YvlB701rcQTzjUZ1JgUoPTnoQA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-musleabihf": {
+ "version": "4.40.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.40.0.tgz",
+ "integrity": "sha512-GoCsPibtVdJFPv/BOIvBKO/XmwZLwaNWdyD8TKlXuqp0veo2sHE+A/vpMQ5iSArRUz/uaoj4h5S6Pn0+PdhRjg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-gnu": {
+ "version": "4.40.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.40.0.tgz",
+ "integrity": "sha512-L5ZLphTjjAD9leJzSLI7rr8fNqJMlGDKlazW2tX4IUF9P7R5TMQPElpH82Q7eNIDQnQlAyiNVfRPfP2vM5Avvg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-musl": {
+ "version": "4.40.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.40.0.tgz",
+ "integrity": "sha512-ATZvCRGCDtv1Y4gpDIXsS+wfFeFuLwVxyUBSLawjgXK2tRE6fnsQEkE4csQQYWlBlsFztRzCnBvWVfcae/1qxQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-loongarch64-gnu": {
+ "version": "4.40.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.40.0.tgz",
+ "integrity": "sha512-wG9e2XtIhd++QugU5MD9i7OnpaVb08ji3P1y/hNbxrQ3sYEelKJOq1UJ5dXczeo6Hj2rfDEL5GdtkMSVLa/AOg==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
+ "version": "4.40.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.40.0.tgz",
+ "integrity": "sha512-vgXfWmj0f3jAUvC7TZSU/m/cOE558ILWDzS7jBhiCAFpY2WEBn5jqgbqvmzlMjtp8KlLcBlXVD2mkTSEQE6Ixw==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-gnu": {
+ "version": "4.40.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.40.0.tgz",
+ "integrity": "sha512-uJkYTugqtPZBS3Z136arevt/FsKTF/J9dEMTX/cwR7lsAW4bShzI2R0pJVw+hcBTWF4dxVckYh72Hk3/hWNKvA==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-musl": {
+ "version": "4.40.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.40.0.tgz",
+ "integrity": "sha512-rKmSj6EXQRnhSkE22+WvrqOqRtk733x3p5sWpZilhmjnkHkpeCgWsFFo0dGnUGeA+OZjRl3+VYq+HyCOEuwcxQ==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-s390x-gnu": {
+ "version": "4.40.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.40.0.tgz",
+ "integrity": "sha512-SpnYlAfKPOoVsQqmTFJ0usx0z84bzGOS9anAC0AZ3rdSo3snecihbhFTlJZ8XMwzqAcodjFU4+/SM311dqE5Sw==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-gnu": {
+ "version": "4.40.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.40.0.tgz",
+ "integrity": "sha512-RcDGMtqF9EFN8i2RYN2W+64CdHruJ5rPqrlYw+cgM3uOVPSsnAQps7cpjXe9be/yDp8UC7VLoCoKC8J3Kn2FkQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-musl": {
+ "version": "4.40.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.40.0.tgz",
+ "integrity": "sha512-HZvjpiUmSNx5zFgwtQAV1GaGazT2RWvqeDi0hV+AtC8unqqDSsaFjPxfsO6qPtKRRg25SisACWnJ37Yio8ttaw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-arm64-msvc": {
+ "version": "4.40.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.40.0.tgz",
+ "integrity": "sha512-UtZQQI5k/b8d7d3i9AZmA/t+Q4tk3hOC0tMOMSq2GlMYOfxbesxG4mJSeDp0EHs30N9bsfwUvs3zF4v/RzOeTQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-ia32-msvc": {
+ "version": "4.40.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.40.0.tgz",
+ "integrity": "sha512-+m03kvI2f5syIqHXCZLPVYplP8pQch9JHyXKZ3AGMKlg8dCyr2PKHjwRLiW53LTrN/Nc3EqHOKxUxzoSPdKddA==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-x64-msvc": {
+ "version": "4.40.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.40.0.tgz",
+ "integrity": "sha512-lpPE1cLfP5oPzVjKMx10pgBmKELQnFJXHgvtHCtuJWOv8MxqdEIMNtgHgBFf7Ea2/7EuVwa9fodWUfXAlXZLZQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@studio-freight/lenis": {
+ "version": "1.0.42",
+ "resolved": "https://registry.npmjs.org/@studio-freight/lenis/-/lenis-1.0.42.tgz",
+ "integrity": "sha512-HJAGf2DeM+BTvKzHv752z6Z7zy6bA643nZM7W88Ft9tnw2GsJSp6iJ+3cekjyMIWH+cloL2U9X82dKXgdU8kPg==",
+ "deprecated": "The '@studio-freight/lenis' package has been renamed to 'lenis'. Please update your dependencies: npm install lenis and visit the documentation: https://www.npmjs.com/package/lenis",
+ "license": "MIT"
+ },
+ "node_modules/@types/babel__core": {
+ "version": "7.20.5",
+ "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
+ "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.20.7",
+ "@babel/types": "^7.20.7",
+ "@types/babel__generator": "*",
+ "@types/babel__template": "*",
+ "@types/babel__traverse": "*"
+ }
+ },
+ "node_modules/@types/babel__generator": {
+ "version": "7.27.0",
+ "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz",
+ "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "node_modules/@types/babel__template": {
+ "version": "7.4.4",
+ "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz",
+ "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.1.0",
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "node_modules/@types/babel__traverse": {
+ "version": "7.20.7",
+ "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.7.tgz",
+ "integrity": "sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.20.7"
+ }
+ },
+ "node_modules/@types/conventional-commits-parser": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/@types/conventional-commits-parser/-/conventional-commits-parser-5.0.1.tgz",
+ "integrity": "sha512-7uz5EHdzz2TqoMfV7ee61Egf5y6NkcO4FB/1iCCQnbeiI1F3xzv3vK5dBCXUCLQgGYS+mUeigK1iKQzvED+QnQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/estree": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz",
+ "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/json-schema": {
+ "version": "7.0.15",
+ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
+ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/node": {
+ "version": "22.14.1",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.14.1.tgz",
+ "integrity": "sha512-u0HuPQwe/dHrItgHHpmw3N2fYCR6x4ivMNbPHRkBVP4CvN+kiRrKHWk3i8tXiO/joPwXLMYvF9TTF0eqgHIuOw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~6.21.0"
+ }
+ },
+ "node_modules/@types/parse-json": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz",
+ "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==",
+ "license": "MIT"
+ },
+ "node_modules/@types/react": {
+ "version": "19.1.2",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.2.tgz",
+ "integrity": "sha512-oxLPMytKchWGbnQM9O7D67uPa9paTNxO7jVoNMXgkkErULBPhPARCfkKL9ytcIJJRGjbsVwW4ugJzyFFvm/Tiw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "csstype": "^3.0.2"
+ }
+ },
+ "node_modules/@types/react-dom": {
+ "version": "19.1.2",
+ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.2.tgz",
+ "integrity": "sha512-XGJkWF41Qq305SKWEILa1O8vzhb3aOo3ogBlSmiqNko/WmRb6QIaweuZCXjKygVDXpzXb5wyxKTSOsmkuqj+Qw==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "^19.0.0"
+ }
+ },
+ "node_modules/@vitejs/plugin-react": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.4.1.tgz",
+ "integrity": "sha512-IpEm5ZmeXAP/osiBXVVP5KjFMzbWOonMs0NaQQl+xYnUAcq4oHUBsF2+p4MgKWG4YMmFYJU8A6sxRPuowllm6w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/core": "^7.26.10",
+ "@babel/plugin-transform-react-jsx-self": "^7.25.9",
+ "@babel/plugin-transform-react-jsx-source": "^7.25.9",
+ "@types/babel__core": "^7.20.5",
+ "react-refresh": "^0.17.0"
+ },
+ "engines": {
+ "node": "^14.18.0 || >=16.0.0"
+ },
+ "peerDependencies": {
+ "vite": "^4.2.0 || ^5.0.0 || ^6.0.0"
+ }
+ },
+ "node_modules/acorn": {
+ "version": "8.14.1",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz",
+ "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/acorn-jsx": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+ "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
+ "node_modules/ajv": {
+ "version": "8.17.1",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
+ "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.3",
+ "fast-uri": "^3.0.1",
+ "json-schema-traverse": "^1.0.0",
+ "require-from-string": "^2.0.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/ansi-escapes": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.0.0.tgz",
+ "integrity": "sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "environment": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/ansi-regex": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
+ "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
+ "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/any-promise": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
+ "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "dev": true,
+ "license": "Python-2.0"
+ },
+ "node_modules/array-ify": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz",
+ "integrity": "sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "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/axios": {
+ "version": "1.8.4",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.8.4.tgz",
+ "integrity": "sha512-eBSYY4Y68NNlHbHBMdeDmKNtDgXWhQsJcGqzO3iLUM0GraQFSS9cVgPX5I9b3lbdFKyYoAEGAZF1DwhTaljNAw==",
+ "license": "MIT",
+ "dependencies": {
+ "follow-redirects": "^1.15.6",
+ "form-data": "^4.0.0",
+ "proxy-from-env": "^1.1.0"
+ }
+ },
+ "node_modules/babel-plugin-macros": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz",
+ "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.12.5",
+ "cosmiconfig": "^7.0.0",
+ "resolve": "^1.19.0"
+ },
+ "engines": {
+ "node": ">=10",
+ "npm": ">=6"
+ }
+ },
+ "node_modules/babel-plugin-macros/node_modules/cosmiconfig": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz",
+ "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/parse-json": "^4.0.0",
+ "import-fresh": "^3.2.1",
+ "parse-json": "^5.0.0",
+ "path-type": "^4.0.0",
+ "yaml": "^1.10.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/babel-plugin-macros/node_modules/yaml": {
+ "version": "1.10.2",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
+ "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
+ "license": "ISC",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/biome": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/biome/-/biome-0.2.2.tgz",
+ "integrity": "sha512-yCqdzofMYZZW9v0sx1FY5d1vWkNo+TbKWnJw9JPaX3FXAIFsHG1XlQX/ahyWkHC/MYIjhZIaPNr7GwTosjfrJA==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "bluebird": "^3.4.1",
+ "chalk": "^1.1.3",
+ "commander": "^2.9.0",
+ "editor": "^1.0.0",
+ "fs-promise": "^0.5.0",
+ "untildify": "^3.0.2",
+ "user-home": "^2.0.0"
+ },
+ "bin": {
+ "biome": "dist/index.js"
+ }
+ },
+ "node_modules/biome/node_modules/ansi-regex": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+ "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/biome/node_modules/ansi-styles": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
+ "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/biome/node_modules/chalk": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^2.2.1",
+ "escape-string-regexp": "^1.0.2",
+ "has-ansi": "^2.0.0",
+ "strip-ansi": "^3.0.0",
+ "supports-color": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/biome/node_modules/escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/biome/node_modules/strip-ansi": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/bluebird": {
+ "version": "3.7.2",
+ "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
+ "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/braces": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
+ "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fill-range": "^7.1.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/browserslist": {
+ "version": "4.24.4",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz",
+ "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "caniuse-lite": "^1.0.30001688",
+ "electron-to-chromium": "^1.5.73",
+ "node-releases": "^2.0.19",
+ "update-browserslist-db": "^1.1.1"
+ },
+ "bin": {
+ "browserslist": "cli.js"
+ },
+ "engines": {
+ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+ }
+ },
+ "node_modules/call-bind-apply-helpers": {
+ "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==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/caniuse-lite": {
+ "version": "1.0.30001715",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001715.tgz",
+ "integrity": "sha512-7ptkFGMm2OAOgvZpwgA4yjQ5SQbrNVGdRjzH0pBdy1Fasvcr+KAeECmbCAECzTuDuoX0FCY8KzUxjf9+9kfZEw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "CC-BY-4.0"
+ },
+ "node_modules/chalk": {
+ "version": "5.4.1",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz",
+ "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.17.0 || ^14.13 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "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/cli-cursor": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz",
+ "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "restore-cursor": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/cli-truncate": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz",
+ "integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "slice-ansi": "^5.0.0",
+ "string-width": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/cliui": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
+ "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.1",
+ "wrap-ansi": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/cliui/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cliui/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/cliui/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/cliui/node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cliui/node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cliui/node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cliui/node_modules/wrap-ansi": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/clsx": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
+ "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color-name": {
+ "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/colorette": {
+ "version": "2.0.20",
+ "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz",
+ "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "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/commander": {
+ "version": "2.20.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/compare-func": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz",
+ "integrity": "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "array-ify": "^1.0.0",
+ "dot-prop": "^5.1.0"
+ }
+ },
+ "node_modules/concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/conventional-changelog-angular": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-7.0.0.tgz",
+ "integrity": "sha512-ROjNchA9LgfNMTTFSIWPzebCwOGFdgkEq45EnvvrmSLvCtAw0HSmrCs7/ty+wAeYUZyNay0YMUNYFTRL72PkBQ==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "compare-func": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/conventional-changelog-conventionalcommits": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-7.0.2.tgz",
+ "integrity": "sha512-NKXYmMR/Hr1DevQegFB4MwfM5Vv0m4UIxKZTTYuD98lpTknaZlSRrDOG4X7wIXpGkfsYxZTghUN+Qq+T0YQI7w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "compare-func": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/conventional-commits-parser": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-5.0.0.tgz",
+ "integrity": "sha512-ZPMl0ZJbw74iS9LuX9YIAiW8pfM5p3yh2o/NbXHbkFuZzY5jvdi5jFycEOkmBW5H5I7nA+D6f3UcsCLP2vvSEA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-text-path": "^2.0.0",
+ "JSONStream": "^1.3.5",
+ "meow": "^12.0.1",
+ "split2": "^4.0.0"
+ },
+ "bin": {
+ "conventional-commits-parser": "cli.mjs"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/convert-source-map": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
+ "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==",
+ "license": "MIT"
+ },
+ "node_modules/cookie": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz",
+ "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/cosmiconfig": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz",
+ "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "env-paths": "^2.2.1",
+ "import-fresh": "^3.3.0",
+ "js-yaml": "^4.1.0",
+ "parse-json": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/d-fischer"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.9.5"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/cosmiconfig-typescript-loader": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-6.1.0.tgz",
+ "integrity": "sha512-tJ1w35ZRUiM5FeTzT7DtYWAFFv37ZLqSRkGi2oeCK1gPhvaWjkAtfXvLmvE1pRfxxp9aQo6ba/Pvg1dKj05D4g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "jiti": "^2.4.1"
+ },
+ "engines": {
+ "node": ">=v18"
+ },
+ "peerDependencies": {
+ "@types/node": "*",
+ "cosmiconfig": ">=9",
+ "typescript": ">=5"
+ }
+ },
+ "node_modules/cross-spawn": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
+ "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/csstype": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
+ "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
+ "license": "MIT"
+ },
+ "node_modules/dargs": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/dargs/-/dargs-8.1.0.tgz",
+ "integrity": "sha512-wAV9QHOsNbwnWdNW2FYvE1P56wtgSbM+3SZcdGiWQILwVjACCXDCI3Ai8QlCjMDB8YK5zySiXZYBiwGmNY3lnw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/debug": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
+ "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/deep-is": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
+ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "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/dot-prop": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz",
+ "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-obj": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/dunder-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
+ "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.2.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/editor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/editor/-/editor-1.0.0.tgz",
+ "integrity": "sha512-SoRmbGStwNYHgKfjOrX2L0mUvp9bUVv0uPppZSOMAntEbcFtoC3MKF5b3T6HQPXKIV+QGY3xPO3JK5it5lVkuw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/electron-to-chromium": {
+ "version": "1.5.141",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.141.tgz",
+ "integrity": "sha512-qS+qH9oqVYc1ooubTiB9l904WVyM6qNYxtOEEGReoZXw3xlqeYdFr5GclNzbkAufWgwWLEPoDi3d9MoRwwIjGw==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/emoji-regex": {
+ "version": "10.4.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz",
+ "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/enquire.js": {
+ "version": "2.1.6",
+ "resolved": "https://registry.npmjs.org/enquire.js/-/enquire.js-2.1.6.tgz",
+ "integrity": "sha512-/KujNpO+PT63F7Hlpu4h3pE3TokKRHN26JYmQpPyjkRD/N57R7bPDNojMXdi7uveAKjYB7yQnartCxZnFWr0Xw==",
+ "license": "MIT"
+ },
+ "node_modules/env-paths": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz",
+ "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/environment": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz",
+ "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/error-ex": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
+ "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
+ "license": "MIT",
+ "dependencies": {
+ "is-arrayish": "^0.2.1"
+ }
+ },
+ "node_modules/es-define-property": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
+ "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-errors": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-object-atoms": {
+ "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==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-set-tostringtag": {
+ "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==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.6",
+ "has-tostringtag": "^1.0.2",
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/esbuild": {
+ "version": "0.25.3",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.3.tgz",
+ "integrity": "sha512-qKA6Pvai73+M2FtftpNKRxJ78GIjmFXFxd/1DVBqGo/qNhLSfv+G12n9pNoWdytJC8U00TrViOwpjT0zgqQS8Q==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.25.3",
+ "@esbuild/android-arm": "0.25.3",
+ "@esbuild/android-arm64": "0.25.3",
+ "@esbuild/android-x64": "0.25.3",
+ "@esbuild/darwin-arm64": "0.25.3",
+ "@esbuild/darwin-x64": "0.25.3",
+ "@esbuild/freebsd-arm64": "0.25.3",
+ "@esbuild/freebsd-x64": "0.25.3",
+ "@esbuild/linux-arm": "0.25.3",
+ "@esbuild/linux-arm64": "0.25.3",
+ "@esbuild/linux-ia32": "0.25.3",
+ "@esbuild/linux-loong64": "0.25.3",
+ "@esbuild/linux-mips64el": "0.25.3",
+ "@esbuild/linux-ppc64": "0.25.3",
+ "@esbuild/linux-riscv64": "0.25.3",
+ "@esbuild/linux-s390x": "0.25.3",
+ "@esbuild/linux-x64": "0.25.3",
+ "@esbuild/netbsd-arm64": "0.25.3",
+ "@esbuild/netbsd-x64": "0.25.3",
+ "@esbuild/openbsd-arm64": "0.25.3",
+ "@esbuild/openbsd-x64": "0.25.3",
+ "@esbuild/sunos-x64": "0.25.3",
+ "@esbuild/win32-arm64": "0.25.3",
+ "@esbuild/win32-ia32": "0.25.3",
+ "@esbuild/win32-x64": "0.25.3"
+ }
+ },
+ "node_modules/escalade": {
+ "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"
+ }
+ },
+ "node_modules/escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint": {
+ "version": "9.25.1",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.25.1.tgz",
+ "integrity": "sha512-E6Mtz9oGQWDCpV12319d59n4tx9zOTXSTmc8BLVxBx+G/0RdM5MvEEJLU9c0+aleoePYYgVTOsRblx433qmhWQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.2.0",
+ "@eslint-community/regexpp": "^4.12.1",
+ "@eslint/config-array": "^0.20.0",
+ "@eslint/config-helpers": "^0.2.1",
+ "@eslint/core": "^0.13.0",
+ "@eslint/eslintrc": "^3.3.1",
+ "@eslint/js": "9.25.1",
+ "@eslint/plugin-kit": "^0.2.8",
+ "@humanfs/node": "^0.16.6",
+ "@humanwhocodes/module-importer": "^1.0.1",
+ "@humanwhocodes/retry": "^0.4.2",
+ "@types/estree": "^1.0.6",
+ "@types/json-schema": "^7.0.15",
+ "ajv": "^6.12.4",
+ "chalk": "^4.0.0",
+ "cross-spawn": "^7.0.6",
+ "debug": "^4.3.2",
+ "escape-string-regexp": "^4.0.0",
+ "eslint-scope": "^8.3.0",
+ "eslint-visitor-keys": "^4.2.0",
+ "espree": "^10.3.0",
+ "esquery": "^1.5.0",
+ "esutils": "^2.0.2",
+ "fast-deep-equal": "^3.1.3",
+ "file-entry-cache": "^8.0.0",
+ "find-up": "^5.0.0",
+ "glob-parent": "^6.0.2",
+ "ignore": "^5.2.0",
+ "imurmurhash": "^0.1.4",
+ "is-glob": "^4.0.0",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "lodash.merge": "^4.6.2",
+ "minimatch": "^3.1.2",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.9.3"
+ },
+ "bin": {
+ "eslint": "bin/eslint.js"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://eslint.org/donate"
+ },
+ "peerDependencies": {
+ "jiti": "*"
+ },
+ "peerDependenciesMeta": {
+ "jiti": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-plugin-react-hooks": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz",
+ "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0"
+ }
+ },
+ "node_modules/eslint-plugin-react-refresh": {
+ "version": "0.4.20",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.20.tgz",
+ "integrity": "sha512-XpbHQ2q5gUF8BGOX4dHe+71qoirYMhApEPZ7sfhF/dNnOF1UXnCMGZf79SFTBO7Bz5YEIT4TMieSlJBWhP9WBA==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "eslint": ">=8.40"
+ }
+ },
+ "node_modules/eslint-scope": {
+ "version": "8.3.0",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.3.0.tgz",
+ "integrity": "sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint-visitor-keys": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz",
+ "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint/node_modules/ajv": {
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/eslint/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/eslint/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/eslint/node_modules/find-up": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+ "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "locate-path": "^6.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint/node_modules/json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/eslint/node_modules/locate-path": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+ "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-locate": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint/node_modules/p-limit": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+ "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "yocto-queue": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint/node_modules/p-locate": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+ "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-limit": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint/node_modules/path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/eslint/node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/eslint/node_modules/yocto-queue": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/espree": {
+ "version": "10.3.0",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz",
+ "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "acorn": "^8.14.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^4.2.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/esquery": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz",
+ "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "estraverse": "^5.1.0"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/esrecurse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+ "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/eventemitter3": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz",
+ "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/execa": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz",
+ "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cross-spawn": "^7.0.3",
+ "get-stream": "^8.0.1",
+ "human-signals": "^5.0.0",
+ "is-stream": "^3.0.0",
+ "merge-stream": "^2.0.0",
+ "npm-run-path": "^5.1.0",
+ "onetime": "^6.0.0",
+ "signal-exit": "^4.1.0",
+ "strip-final-newline": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=16.17"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/execa?sponsor=1"
+ }
+ },
+ "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-json-stable-stringify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-uri": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz",
+ "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fastify"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fastify"
+ }
+ ],
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/file-entry-cache": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
+ "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "flat-cache": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/fill-range": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
+ "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "to-regex-range": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/find-root": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz",
+ "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==",
+ "license": "MIT"
+ },
+ "node_modules/find-up": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-7.0.0.tgz",
+ "integrity": "sha512-YyZM99iHrqLKjmt4LJDj58KI+fYyufRLBSYcqycxf//KpBk9FoewoGX0450m9nB44qrZnovzC2oeP5hUibxc/g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "locate-path": "^7.2.0",
+ "path-exists": "^5.0.0",
+ "unicorn-magic": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/flat-cache": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
+ "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "flatted": "^3.2.9",
+ "keyv": "^4.5.4"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/flatted": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz",
+ "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/follow-redirects": {
+ "version": "1.15.9",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
+ "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/RubenVerborgh"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=4.0"
+ },
+ "peerDependenciesMeta": {
+ "debug": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/form-data": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz",
+ "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==",
+ "license": "MIT",
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "es-set-tostringtag": "^2.1.0",
+ "mime-types": "^2.1.12"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/framer-motion": {
+ "version": "12.8.0",
+ "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.8.0.tgz",
+ "integrity": "sha512-EarL75miCDcKLEAQLJ+6Zfwdj+KQsVlbHGGlygZ/TigKBj7NLPkyDKk4WLFUScjAs2xNpfMRLBM6VsCJq9Roxg==",
+ "license": "MIT",
+ "dependencies": {
+ "motion-dom": "^12.8.0",
+ "motion-utils": "^12.7.5",
+ "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/fs-extra": {
+ "version": "0.26.7",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.26.7.tgz",
+ "integrity": "sha512-waKu+1KumRhYv8D8gMRCKJGAMI9pRnPuEb1mvgYD0f7wBscg+h6bW4FDTmEZhB9VKxvoTtxW+Y7bnIlB7zja6Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "graceful-fs": "^4.1.2",
+ "jsonfile": "^2.1.0",
+ "klaw": "^1.0.0",
+ "path-is-absolute": "^1.0.0",
+ "rimraf": "^2.2.8"
+ }
+ },
+ "node_modules/fs-promise": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/fs-promise/-/fs-promise-0.5.0.tgz",
+ "integrity": "sha512-Y+4F4ujhEcayCJt6JmzcOun9MYGQwz+bVUiuBmTkJImhBHKpBvmVPZR9wtfiF7k3ffwAOAuurygQe+cPLSFQhw==",
+ "deprecated": "Use mz or fs-extra^3.0 with Promise Support",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "any-promise": "^1.0.0",
+ "fs-extra": "^0.26.5",
+ "mz": "^2.3.1",
+ "thenify-all": "^1.6.0"
+ }
+ },
+ "node_modules/fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "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==",
+ "license": "MIT",
+ "funding": {
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/get-caller-file": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": "6.* || 8.* || >= 10.*"
+ }
+ },
+ "node_modules/get-east-asian-width": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz",
+ "integrity": "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "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==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "es-define-property": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "function-bind": "^1.1.2",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "hasown": "^2.0.2",
+ "math-intrinsics": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
+ "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+ "license": "MIT",
+ "dependencies": {
+ "dunder-proto": "^1.0.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/get-stream": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz",
+ "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/git-raw-commits": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-4.0.0.tgz",
+ "integrity": "sha512-ICsMM1Wk8xSGMowkOmPrzo2Fgmfo4bMHLNX6ytHjajRJUqvHOw/TFapQ+QG75c3X/tTDDhOSRPGC52dDbNM8FQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "dargs": "^8.0.0",
+ "meow": "^12.0.1",
+ "split2": "^4.0.0"
+ },
+ "bin": {
+ "git-raw-commits": "cli.mjs"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/glob": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "deprecated": "Glob versions prior to v9 are no longer supported",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.1.1",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/glob-parent": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+ "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/global-directory": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/global-directory/-/global-directory-4.0.1.tgz",
+ "integrity": "sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ini": "4.1.1"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/globals": {
+ "version": "15.15.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz",
+ "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/gopd": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
+ "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/graceful-fs": {
+ "version": "4.2.11",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
+ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/gsap": {
+ "version": "3.12.7",
+ "resolved": "https://registry.npmjs.org/gsap/-/gsap-3.12.7.tgz",
+ "integrity": "sha512-V4GsyVamhmKefvcAKaoy0h6si0xX7ogwBoBSs2CTJwt7luW0oZzC0LhdkyuKV8PJAXr7Yaj8pMjCKD4GJ+eEMg==",
+ "license": "Standard 'no charge' license: https://gsap.com/standard-license. Club GSAP members get more: https://gsap.com/licensing/. Why GreenSock doesn't employ an MIT license: https://gsap.com/why-license/"
+ },
+ "node_modules/has-ansi": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
+ "integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/has-ansi/node_modules/ansi-regex": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+ "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/has-symbols": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
+ "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-tostringtag": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
+ "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
+ "license": "MIT",
+ "dependencies": {
+ "has-symbols": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/hasown": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
+ "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+ "license": "MIT",
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/hoist-non-react-statics": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
+ "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "react-is": "^16.7.0"
+ }
+ },
+ "node_modules/human-signals": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz",
+ "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=16.17.0"
+ }
+ },
+ "node_modules/husky": {
+ "version": "9.1.7",
+ "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz",
+ "integrity": "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "husky": "bin.js"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/typicode"
+ }
+ },
+ "node_modules/ignore": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
+ "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/import-fresh": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
+ "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
+ "license": "MIT",
+ "dependencies": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/import-fresh/node_modules/resolve-from": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/import-meta-resolve": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.1.0.tgz",
+ "integrity": "sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8.19"
+ }
+ },
+ "node_modules/inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+ "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/ini": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.1.tgz",
+ "integrity": "sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+ }
+ },
+ "node_modules/is-arrayish": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+ "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
+ "license": "MIT"
+ },
+ "node_modules/is-core-module": {
+ "version": "2.16.1",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
+ "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
+ "license": "MIT",
+ "dependencies": {
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-fullwidth-code-point": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz",
+ "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
+ "node_modules/is-obj": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz",
+ "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-stream": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz",
+ "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-text-path": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-2.0.0.tgz",
+ "integrity": "sha512-+oDTluR6WEjdXEJMnC2z6A4FRwFoYuvShVVEGsS7ewc0UTi2QtAKMDJuL4BDEVt+5T7MjFo12RP8ghOM75oKJw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "text-extensions": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/jiti": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz",
+ "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "jiti": "lib/jiti-cli.mjs"
+ }
+ },
+ "node_modules/jquery": {
+ "version": "3.7.1",
+ "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz",
+ "integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+ "license": "MIT"
+ },
+ "node_modules/js-yaml": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
+ "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "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",
+ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-parse-even-better-errors": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
+ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
+ "license": "MIT"
+ },
+ "node_modules/json-schema-traverse": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json2mq": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/json2mq/-/json2mq-0.2.0.tgz",
+ "integrity": "sha512-SzoRg7ux5DWTII9J2qkrZrqV1gt+rTaoufMxEzXbS26Uid0NwaJd123HcoB80TgubEppxxIGdNxCx50fEoEWQA==",
+ "license": "MIT",
+ "dependencies": {
+ "string-convert": "^0.2.0"
+ }
+ },
+ "node_modules/json5": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
+ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "json5": "lib/cli.js"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/jsonfile": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz",
+ "integrity": "sha512-PKllAqbgLgxHaj8TElYymKCAgrASebJrWpTnEkOaTowt23VKXXN0sUeriJ+eh7y6ufb/CC5ap11pz71/cM0hUw==",
+ "dev": true,
+ "license": "MIT",
+ "optionalDependencies": {
+ "graceful-fs": "^4.1.6"
+ }
+ },
+ "node_modules/jsonparse": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz",
+ "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==",
+ "dev": true,
+ "engines": [
+ "node >= 0.2.0"
+ ],
+ "license": "MIT"
+ },
+ "node_modules/JSONStream": {
+ "version": "1.3.5",
+ "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz",
+ "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==",
+ "dev": true,
+ "license": "(MIT OR Apache-2.0)",
+ "dependencies": {
+ "jsonparse": "^1.2.0",
+ "through": ">=2.2.7 <3"
+ },
+ "bin": {
+ "JSONStream": "bin.js"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/keyv": {
+ "version": "4.5.4",
+ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
+ "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "json-buffer": "3.0.1"
+ }
+ },
+ "node_modules/klaw": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz",
+ "integrity": "sha512-TED5xi9gGQjGpNnvRWknrwAB1eL5GciPfVFOt3Vk1OJCVDQbzuSfrF3hkUQKlsgKrG1F+0t5W0m+Fje1jIt8rw==",
+ "dev": true,
+ "license": "MIT",
+ "optionalDependencies": {
+ "graceful-fs": "^4.1.9"
+ }
+ },
+ "node_modules/levn": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+ "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "^1.2.1",
+ "type-check": "~0.4.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/lilconfig": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz",
+ "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antonk52"
+ }
+ },
+ "node_modules/lines-and-columns": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
+ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
+ "license": "MIT"
+ },
+ "node_modules/lint-staged": {
+ "version": "15.5.1",
+ "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.5.1.tgz",
+ "integrity": "sha512-6m7u8mue4Xn6wK6gZvSCQwBvMBR36xfY24nF5bMTf2MHDYG6S3yhJuOgdYVw99hsjyDt2d4z168b3naI8+NWtQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "chalk": "^5.4.1",
+ "commander": "^13.1.0",
+ "debug": "^4.4.0",
+ "execa": "^8.0.1",
+ "lilconfig": "^3.1.3",
+ "listr2": "^8.2.5",
+ "micromatch": "^4.0.8",
+ "pidtree": "^0.6.0",
+ "string-argv": "^0.3.2",
+ "yaml": "^2.7.0"
+ },
+ "bin": {
+ "lint-staged": "bin/lint-staged.js"
+ },
+ "engines": {
+ "node": ">=18.12.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/lint-staged"
+ }
+ },
+ "node_modules/lint-staged/node_modules/commander": {
+ "version": "13.1.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz",
+ "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/listr2": {
+ "version": "8.3.2",
+ "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.3.2.tgz",
+ "integrity": "sha512-vsBzcU4oE+v0lj4FhVLzr9dBTv4/fHIa57l+GCwovP8MoFNZJTOhGU8PXd4v2VJCbECAaijBiHntiekFMLvo0g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cli-truncate": "^4.0.0",
+ "colorette": "^2.0.20",
+ "eventemitter3": "^5.0.1",
+ "log-update": "^6.1.0",
+ "rfdc": "^1.4.1",
+ "wrap-ansi": "^9.0.0"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/locate-path": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz",
+ "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-locate": "^6.0.0"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/lodash.camelcase": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
+ "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/lodash.debounce": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
+ "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==",
+ "license": "MIT"
+ },
+ "node_modules/lodash.isplainobject": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
+ "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/lodash.kebabcase": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz",
+ "integrity": "sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/lodash.merge": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/lodash.mergewith": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz",
+ "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/lodash.snakecase": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz",
+ "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/lodash.startcase": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/lodash.startcase/-/lodash.startcase-4.4.0.tgz",
+ "integrity": "sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/lodash.uniq": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
+ "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/lodash.upperfirst": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz",
+ "integrity": "sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/log-update": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz",
+ "integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-escapes": "^7.0.0",
+ "cli-cursor": "^5.0.0",
+ "slice-ansi": "^7.1.0",
+ "strip-ansi": "^7.1.0",
+ "wrap-ansi": "^9.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/log-update/node_modules/is-fullwidth-code-point": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.0.0.tgz",
+ "integrity": "sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "get-east-asian-width": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/log-update/node_modules/slice-ansi": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.0.tgz",
+ "integrity": "sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^6.2.1",
+ "is-fullwidth-code-point": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/slice-ansi?sponsor=1"
+ }
+ },
+ "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==",
+ "license": "MIT",
+ "dependencies": {
+ "js-tokens": "^3.0.0 || ^4.0.0"
+ },
+ "bin": {
+ "loose-envify": "cli.js"
+ }
+ },
+ "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==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "yallist": "^3.0.2"
+ }
+ },
+ "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==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/meow": {
+ "version": "12.1.1",
+ "resolved": "https://registry.npmjs.org/meow/-/meow-12.1.1.tgz",
+ "integrity": "sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=16.10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/merge-stream": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
+ "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/micromatch": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
+ "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "braces": "^3.0.3",
+ "picomatch": "^2.3.1"
+ },
+ "engines": {
+ "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/mimic-fn": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz",
+ "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/mimic-function": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz",
+ "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/minimist": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
+ "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/motion-dom": {
+ "version": "12.8.0",
+ "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.8.0.tgz",
+ "integrity": "sha512-YsfUE1F8Ycv9th1V0YJ6LOx9U2EMe/8P3RXK1o6NZhRbdFiWvzBLvxqp2X6Fn3rbJbwWkSEfnpe14ZU9Oz1d1Q==",
+ "license": "MIT",
+ "dependencies": {
+ "motion-utils": "^12.7.5"
+ }
+ },
+ "node_modules/motion-utils": {
+ "version": "12.7.5",
+ "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.7.5.tgz",
+ "integrity": "sha512-JIgrmEq7Vw1x0AUrjvkRp7oMMQkGqSUMT50O/Ag6RRCQWG3gRRTkOI+BirBAJT6m+GIPoiyxkJ1u98GgF/a6TQ==",
+ "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==",
+ "license": "MIT"
+ },
+ "node_modules/mz": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
+ "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "any-promise": "^1.0.0",
+ "object-assign": "^4.0.1",
+ "thenify-all": "^1.0.0"
+ }
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.11",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
+ "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/natural-compare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/node-releases": {
+ "version": "2.0.19",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz",
+ "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/npm-run-path": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz",
+ "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "path-key": "^4.0.0"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/npm-run-path/node_modules/path-key": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz",
+ "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "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==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "wrappy": "1"
+ }
+ },
+ "node_modules/onetime": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz",
+ "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mimic-fn": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/optionator": {
+ "version": "0.9.4",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
+ "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "deep-is": "^0.1.3",
+ "fast-levenshtein": "^2.0.6",
+ "levn": "^0.4.1",
+ "prelude-ls": "^1.2.1",
+ "type-check": "^0.4.0",
+ "word-wrap": "^1.2.5"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/os-homedir": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
+ "integrity": "sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/p-limit": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz",
+ "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "yocto-queue": "^1.0.0"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-locate": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz",
+ "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-limit": "^4.0.0"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/parent-module": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "license": "MIT",
+ "dependencies": {
+ "callsites": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/parse-json": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
+ "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.0.0",
+ "error-ex": "^1.3.1",
+ "json-parse-even-better-errors": "^2.3.0",
+ "lines-and-columns": "^1.1.6"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/path-exists": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz",
+ "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ }
+ },
+ "node_modules/path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-parse": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+ "license": "MIT"
+ },
+ "node_modules/path-type": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
+ "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/picocolors": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+ "license": "ISC"
+ },
+ "node_modules/picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/pidtree": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz",
+ "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "pidtree": "bin/pidtree.js"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/postcss": {
+ "version": "8.5.3",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz",
+ "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "nanoid": "^3.3.8",
+ "picocolors": "^1.1.1",
+ "source-map-js": "^1.2.1"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/prelude-ls": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/prettier": {
+ "version": "3.5.3",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz",
+ "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "prettier": "bin/prettier.cjs"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/prettier/prettier?sponsor=1"
+ }
+ },
+ "node_modules/prop-types": {
+ "version": "15.8.1",
+ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
+ "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
+ "license": "MIT",
+ "dependencies": {
+ "loose-envify": "^1.4.0",
+ "object-assign": "^4.1.1",
+ "react-is": "^16.13.1"
+ }
+ },
+ "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",
+ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/react": {
+ "version": "19.1.0",
+ "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz",
+ "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react-dom": {
+ "version": "19.1.0",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz",
+ "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==",
+ "license": "MIT",
+ "dependencies": {
+ "scheduler": "^0.26.0"
+ },
+ "peerDependencies": {
+ "react": "^19.1.0"
+ }
+ },
+ "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==",
+ "license": "MIT"
+ },
+ "node_modules/react-refresh": {
+ "version": "0.17.0",
+ "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz",
+ "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react-router": {
+ "version": "7.5.2",
+ "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.5.2.tgz",
+ "integrity": "sha512-9Rw8r199klMnlGZ8VAsV/I8WrIF6IyJ90JQUdboupx1cdkgYqwnrYjH+I/nY/7cA1X5zia4mDJqH36npP7sxGQ==",
+ "license": "MIT",
+ "dependencies": {
+ "cookie": "^1.0.1",
+ "set-cookie-parser": "^2.6.0",
+ "turbo-stream": "2.4.0"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=18",
+ "react-dom": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/react-router-dom": {
+ "version": "7.5.2",
+ "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.5.2.tgz",
+ "integrity": "sha512-yk1XW8Fj7gK7flpYBXF3yzd2NbX6P7Kxjvs2b5nu1M04rb5pg/Zc4fGdBNTeT4eDYL2bvzWNyKaIMJX/RKHTTg==",
+ "license": "MIT",
+ "dependencies": {
+ "react-router": "7.5.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=18",
+ "react-dom": ">=18"
+ }
+ },
+ "node_modules/react-slick": {
+ "version": "0.30.3",
+ "resolved": "https://registry.npmjs.org/react-slick/-/react-slick-0.30.3.tgz",
+ "integrity": "sha512-B4x0L9GhkEWUMApeHxr/Ezp2NncpGc+5174R02j+zFiWuYboaq98vmxwlpafZfMjZic1bjdIqqmwLDcQY0QaFA==",
+ "license": "MIT",
+ "dependencies": {
+ "classnames": "^2.2.5",
+ "enquire.js": "^2.1.6",
+ "json2mq": "^0.2.0",
+ "lodash.debounce": "^4.0.8",
+ "resize-observer-polyfill": "^1.5.0"
+ },
+ "peerDependencies": {
+ "react": "^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react-dom": "^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
+ "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/regenerator-runtime": {
+ "version": "0.14.1",
+ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
+ "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==",
+ "license": "MIT"
+ },
+ "node_modules/require-directory": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+ "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/resize-observer-polyfill": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz",
+ "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==",
+ "license": "MIT"
+ },
+ "node_modules/resolve": {
+ "version": "1.22.10",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
+ "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==",
+ "license": "MIT",
+ "dependencies": {
+ "is-core-module": "^2.16.0",
+ "path-parse": "^1.0.7",
+ "supports-preserve-symlinks-flag": "^1.0.0"
+ },
+ "bin": {
+ "resolve": "bin/resolve"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/resolve-from": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
+ "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/restore-cursor": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz",
+ "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "onetime": "^7.0.0",
+ "signal-exit": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/restore-cursor/node_modules/onetime": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz",
+ "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mimic-function": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/rfdc": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz",
+ "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/rimraf": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
+ "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+ "deprecated": "Rimraf versions prior to v4 are no longer supported",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "glob": "^7.1.3"
+ },
+ "bin": {
+ "rimraf": "bin.js"
+ }
+ },
+ "node_modules/rollup": {
+ "version": "4.40.0",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.40.0.tgz",
+ "integrity": "sha512-Noe455xmA96nnqH5piFtLobsGbCij7Tu+tb3c1vYjNbTkfzGqXqQXG3wJaYXkRZuQ0vEYN4bhwg7QnIrqB5B+w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "1.0.7"
+ },
+ "bin": {
+ "rollup": "dist/bin/rollup"
+ },
+ "engines": {
+ "node": ">=18.0.0",
+ "npm": ">=8.0.0"
+ },
+ "optionalDependencies": {
+ "@rollup/rollup-android-arm-eabi": "4.40.0",
+ "@rollup/rollup-android-arm64": "4.40.0",
+ "@rollup/rollup-darwin-arm64": "4.40.0",
+ "@rollup/rollup-darwin-x64": "4.40.0",
+ "@rollup/rollup-freebsd-arm64": "4.40.0",
+ "@rollup/rollup-freebsd-x64": "4.40.0",
+ "@rollup/rollup-linux-arm-gnueabihf": "4.40.0",
+ "@rollup/rollup-linux-arm-musleabihf": "4.40.0",
+ "@rollup/rollup-linux-arm64-gnu": "4.40.0",
+ "@rollup/rollup-linux-arm64-musl": "4.40.0",
+ "@rollup/rollup-linux-loongarch64-gnu": "4.40.0",
+ "@rollup/rollup-linux-powerpc64le-gnu": "4.40.0",
+ "@rollup/rollup-linux-riscv64-gnu": "4.40.0",
+ "@rollup/rollup-linux-riscv64-musl": "4.40.0",
+ "@rollup/rollup-linux-s390x-gnu": "4.40.0",
+ "@rollup/rollup-linux-x64-gnu": "4.40.0",
+ "@rollup/rollup-linux-x64-musl": "4.40.0",
+ "@rollup/rollup-win32-arm64-msvc": "4.40.0",
+ "@rollup/rollup-win32-ia32-msvc": "4.40.0",
+ "@rollup/rollup-win32-x64-msvc": "4.40.0",
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/scheduler": {
+ "version": "0.26.0",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz",
+ "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==",
+ "license": "MIT"
+ },
+ "node_modules/semver": {
+ "version": "7.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz",
+ "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/set-cookie-parser": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz",
+ "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==",
+ "license": "MIT"
+ },
+ "node_modules/shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/signal-exit": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
+ "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/slice-ansi": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz",
+ "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^6.0.0",
+ "is-fullwidth-code-point": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/slice-ansi?sponsor=1"
+ }
+ },
+ "node_modules/slick-carousel": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/slick-carousel/-/slick-carousel-1.8.1.tgz",
+ "integrity": "sha512-XB9Ftrf2EEKfzoQXt3Nitrt/IPbT+f1fgqBdoxO3W/+JYvtEOW6EgxnWfr9GH6nmULv7Y2tPmEX3koxThVmebA==",
+ "license": "MIT",
+ "peerDependencies": {
+ "jquery": ">=1.8.0"
+ }
+ },
+ "node_modules/source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/source-map-js": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/split2": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
+ "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">= 10.x"
+ }
+ },
+ "node_modules/string-argv": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz",
+ "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.6.19"
+ }
+ },
+ "node_modules/string-convert": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/string-convert/-/string-convert-0.2.1.tgz",
+ "integrity": "sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A==",
+ "license": "MIT"
+ },
+ "node_modules/string-width": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
+ "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^10.3.0",
+ "get-east-asian-width": "^1.0.0",
+ "strip-ansi": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/strip-ansi": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
+ "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
+ "node_modules/strip-final-newline": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz",
+ "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/strip-json-comments": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/stylis": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz",
+ "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==",
+ "license": "MIT"
+ },
+ "node_modules/supports-color": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
+ "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/supports-preserve-symlinks-flag": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
+ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/text-extensions": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-2.4.0.tgz",
+ "integrity": "sha512-te/NtwBwfiNRLf9Ijqx3T0nlqZiQ2XrrtBvu+cLL8ZRrGkO0NHTug8MYFKyoSrv/sHTaSKfilUkizV6XhxMJ3g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/thenify": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
+ "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "any-promise": "^1.0.0"
+ }
+ },
+ "node_modules/thenify-all": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz",
+ "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "thenify": ">= 3.1.0 < 4"
+ },
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
+ "node_modules/through": {
+ "version": "2.3.8",
+ "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
+ "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/tinyexec": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz",
+ "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/tinyglobby": {
+ "version": "0.2.13",
+ "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz",
+ "integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fdir": "^6.4.4",
+ "picomatch": "^4.0.2"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/SuperchupuDev"
+ }
+ },
+ "node_modules/tinyglobby/node_modules/fdir": {
+ "version": "6.4.4",
+ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz",
+ "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/tinyglobby/node_modules/picomatch": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz",
+ "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-number": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
+ "node_modules/tslib": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+ "license": "0BSD"
+ },
+ "node_modules/turbo-stream": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/turbo-stream/-/turbo-stream-2.4.0.tgz",
+ "integrity": "sha512-FHncC10WpBd2eOmGwpmQsWLDoK4cqsA/UT/GqNoaKOQnT8uzhtCbg3EoUDMvqpOSAI0S26mr0rkjzbOO6S3v1g==",
+ "license": "ISC"
+ },
+ "node_modules/type-check": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+ "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/typescript": {
+ "version": "5.8.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
+ "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "peer": true,
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "6.21.0",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
+ "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/unicorn-magic": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz",
+ "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/untildify": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/untildify/-/untildify-3.0.3.tgz",
+ "integrity": "sha512-iSk/J8efr8uPT/Z4eSUywnqyrQU7DSdMfdqK4iWEaUVVmcP5JcnpRqmVMwcwcnmI1ATFNgC5V90u09tBynNFKA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/update-browserslist-db": {
+ "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",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "escalade": "^3.2.0",
+ "picocolors": "^1.1.1"
+ },
+ "bin": {
+ "update-browserslist-db": "cli.js"
+ },
+ "peerDependencies": {
+ "browserslist": ">= 4.21.0"
+ }
+ },
+ "node_modules/uri-js": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "node_modules/user-home": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/user-home/-/user-home-2.0.0.tgz",
+ "integrity": "sha512-KMWqdlOcjCYdtIJpicDSFBQ8nFwS2i9sslAd6f4+CBGcU4gist2REnr2fxj2YocvJFxSF3ZOHLYLVZnUxv4BZQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "os-homedir": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/uuid": {
+ "version": "11.1.0",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz",
+ "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==",
+ "funding": [
+ "https://github.com/sponsors/broofa",
+ "https://github.com/sponsors/ctavan"
+ ],
+ "license": "MIT",
+ "bin": {
+ "uuid": "dist/esm/bin/uuid"
+ }
+ },
+ "node_modules/vite": {
+ "version": "6.3.3",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.3.tgz",
+ "integrity": "sha512-5nXH+QsELbFKhsEfWLkHrvgRpTdGJzqOZ+utSdmPTvwHmvU6ITTm3xx+mRusihkcI8GeC7lCDyn3kDtiki9scw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "esbuild": "^0.25.0",
+ "fdir": "^6.4.4",
+ "picomatch": "^4.0.2",
+ "postcss": "^8.5.3",
+ "rollup": "^4.34.9",
+ "tinyglobby": "^0.2.13"
+ },
+ "bin": {
+ "vite": "bin/vite.js"
+ },
+ "engines": {
+ "node": "^18.0.0 || ^20.0.0 || >=22.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/vitejs/vite?sponsor=1"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ },
+ "peerDependencies": {
+ "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0",
+ "jiti": ">=1.21.0",
+ "less": "*",
+ "lightningcss": "^1.21.0",
+ "sass": "*",
+ "sass-embedded": "*",
+ "stylus": "*",
+ "sugarss": "*",
+ "terser": "^5.16.0",
+ "tsx": "^4.8.1",
+ "yaml": "^2.4.2"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ },
+ "jiti": {
+ "optional": true
+ },
+ "less": {
+ "optional": true
+ },
+ "lightningcss": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ },
+ "sass-embedded": {
+ "optional": true
+ },
+ "stylus": {
+ "optional": true
+ },
+ "sugarss": {
+ "optional": true
+ },
+ "terser": {
+ "optional": true
+ },
+ "tsx": {
+ "optional": true
+ },
+ "yaml": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vite/node_modules/fdir": {
+ "version": "6.4.4",
+ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz",
+ "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vite/node_modules/picomatch": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz",
+ "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/word-wrap": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
+ "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/wrap-ansi": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz",
+ "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^6.2.1",
+ "string-width": "^7.0.0",
+ "strip-ansi": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/y18n": {
+ "version": "5.0.8",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/yallist": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/yaml": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.1.tgz",
+ "integrity": "sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "yaml": "bin.mjs"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/yargs": {
+ "version": "17.7.2",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
+ "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cliui": "^8.0.1",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "require-directory": "^2.1.1",
+ "string-width": "^4.2.3",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^21.1.1"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/yargs-parser": {
+ "version": "21.1.1",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
+ "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/yargs/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/yargs/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/yargs/node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/yargs/node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/yargs/node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/yocto-queue": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.1.tgz",
+ "integrity": "sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..bef4fc7
--- /dev/null
+++ b/package.json
@@ -0,0 +1,55 @@
+{
+ "name": "fandom-k",
+ "private": true,
+ "version": "0.0.0",
+ "type": "module",
+ "scripts": {
+ "dev": "vite --host",
+ "build": "vite build",
+ "lint": "biome lint --write",
+ "format": "biome format --write",
+ "check": "biome check --write",
+ "reporter": "biome check --reporter=summary",
+ "prepare": "husky",
+ "preview": "vite preview"
+ },
+ "dependencies": {
+ "@emotion/css": "^11.13.5",
+ "@emotion/react": "^11.14.0",
+ "@emotion/styled": "^11.14.0",
+ "@studio-freight/lenis": "^1.0.42",
+ "axios": "^1.8.4",
+ "framer-motion": "^12.8.0",
+ "gsap": "^3.12.7",
+ "prop-types": "^15.8.1",
+ "react": "^19.0.0",
+ "react-dom": "^19.0.0",
+ "react-router-dom": "^7.5.0",
+ "react-slick": "^0.30.3",
+ "react-toastify": "^11.0.5",
+ "slick-carousel": "^1.8.1",
+ "uuid": "^11.1.0"
+ },
+ "devDependencies": {
+ "@biomejs/biome": "^1.9.4",
+ "@commitlint/cli": "^19.8.0",
+ "@commitlint/config-conventional": "^19.8.0",
+ "@eslint/js": "^9.21.0",
+ "@types/react": "^19.0.10",
+ "@types/react-dom": "^19.0.4",
+ "@vitejs/plugin-react": "^4.3.4",
+ "biome": "^0.2.2",
+ "esbuild": "^0.25.2",
+ "eslint": "^9.24.0",
+ "eslint-plugin-react-hooks": "^5.1.0",
+ "eslint-plugin-react-refresh": "^0.4.19",
+ "globals": "^15.15.0",
+ "husky": "^9.1.7",
+ "lint-staged": "^15.5.1",
+ "prettier": "^3.5.3",
+ "vite": "^6.2.0"
+ },
+ "lint-staged": {
+ "*.{js,jsx,ts,tsx,json,css,scss,md}": ["biome check --apply"]
+ }
+}
diff --git a/public/icons/Checkmark.png b/public/icons/Checkmark.png
new file mode 100644
index 0000000..a9bccb2
Binary files /dev/null and b/public/icons/Checkmark.png differ
diff --git a/public/icons/github-mark-white.svg b/public/icons/github-mark-white.svg
new file mode 100644
index 0000000..d5e6491
--- /dev/null
+++ b/public/icons/github-mark-white.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/icons/icon-arrow-left.svg b/public/icons/icon-arrow-left.svg
new file mode 100644
index 0000000..2676666
--- /dev/null
+++ b/public/icons/icon-arrow-left.svg
@@ -0,0 +1,3 @@
+
+
+
diff --git a/public/icons/icon_coin.png b/public/icons/icon_coin.png
new file mode 100644
index 0000000..0e32bc2
Binary files /dev/null and b/public/icons/icon_coin.png differ
diff --git a/public/icons/icon_credit.svg b/public/icons/icon_credit.svg
new file mode 100644
index 0000000..f70b4a9
--- /dev/null
+++ b/public/icons/icon_credit.svg
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/public/icons/logo.svg b/public/icons/logo.svg
new file mode 100644
index 0000000..01ccba8
--- /dev/null
+++ b/public/icons/logo.svg
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/public/images/ADOR.png b/public/images/ADOR.png
new file mode 100644
index 0000000..205ecaf
Binary files /dev/null and b/public/images/ADOR.png differ
diff --git a/public/images/BLACKPINK.jpg b/public/images/BLACKPINK.jpg
new file mode 100644
index 0000000..9da4e14
Binary files /dev/null and b/public/images/BLACKPINK.jpg differ
diff --git a/public/images/BoyNextDoor.jpg b/public/images/BoyNextDoor.jpg
new file mode 100644
index 0000000..56a3c66
Binary files /dev/null and b/public/images/BoyNextDoor.jpg differ
diff --git a/public/images/Chart.png b/public/images/Chart.png
new file mode 100644
index 0000000..1824147
Binary files /dev/null and b/public/images/Chart.png differ
diff --git a/public/images/FANTAGIO.png b/public/images/FANTAGIO.png
new file mode 100644
index 0000000..549dc0e
Binary files /dev/null and b/public/images/FANTAGIO.png differ
diff --git a/public/images/HYBE.png b/public/images/HYBE.png
new file mode 100644
index 0000000..8808379
Binary files /dev/null and b/public/images/HYBE.png differ
diff --git a/public/images/IVE.jpg b/public/images/IVE.jpg
new file mode 100644
index 0000000..d8fc0ed
Binary files /dev/null and b/public/images/IVE.jpg differ
diff --git a/public/images/Instagram.png b/public/images/Instagram.png
new file mode 100644
index 0000000..5fd9b21
Binary files /dev/null and b/public/images/Instagram.png differ
diff --git a/public/images/JYP.png b/public/images/JYP.png
new file mode 100644
index 0000000..063173f
Binary files /dev/null and b/public/images/JYP.png differ
diff --git a/public/images/KOZ.png b/public/images/KOZ.png
new file mode 100644
index 0000000..0963402
Binary files /dev/null and b/public/images/KOZ.png differ
diff --git a/public/images/Karina.jpeg b/public/images/Karina.jpeg
new file mode 100644
index 0000000..04c0fe9
Binary files /dev/null and b/public/images/Karina.jpeg differ
diff --git a/public/images/NMIX.jpg b/public/images/NMIX.jpg
new file mode 100644
index 0000000..06a9ebf
Binary files /dev/null and b/public/images/NMIX.jpg differ
diff --git a/public/images/NewJeans.jpg b/public/images/NewJeans.jpg
new file mode 100644
index 0000000..bca583f
Binary files /dev/null and b/public/images/NewJeans.jpg differ
diff --git a/public/images/PLEDIS.png b/public/images/PLEDIS.png
new file mode 100644
index 0000000..9bc0a44
Binary files /dev/null and b/public/images/PLEDIS.png differ
diff --git a/public/images/SHINEE.jpg b/public/images/SHINEE.jpg
new file mode 100644
index 0000000..4c2500e
Binary files /dev/null and b/public/images/SHINEE.jpg differ
diff --git a/public/images/SM.png b/public/images/SM.png
new file mode 100644
index 0000000..bd462f8
Binary files /dev/null and b/public/images/SM.png differ
diff --git a/public/images/SOURCEmusic.png b/public/images/SOURCEmusic.png
new file mode 100644
index 0000000..53df9ef
Binary files /dev/null and b/public/images/SOURCEmusic.png differ
diff --git a/public/images/STARSHIP.png b/public/images/STARSHIP.png
new file mode 100644
index 0000000..cf8de8f
Binary files /dev/null and b/public/images/STARSHIP.png differ
diff --git a/public/images/Seventeen.jpg b/public/images/Seventeen.jpg
new file mode 100644
index 0000000..84dbec2
Binary files /dev/null and b/public/images/Seventeen.jpg differ
diff --git a/public/images/WAKEONE.png b/public/images/WAKEONE.png
new file mode 100644
index 0000000..7e79328
Binary files /dev/null and b/public/images/WAKEONE.png differ
diff --git a/public/images/YG.png b/public/images/YG.png
new file mode 100644
index 0000000..6d337d4
Binary files /dev/null and b/public/images/YG.png differ
diff --git a/public/images/YG1.png b/public/images/YG1.png
new file mode 100644
index 0000000..b0b85dd
Binary files /dev/null and b/public/images/YG1.png differ
diff --git a/public/images/Youtube.png b/public/images/Youtube.png
new file mode 100644
index 0000000..a601c65
Binary files /dev/null and b/public/images/Youtube.png differ
diff --git a/public/images/aespa.jpg b/public/images/aespa.jpg
new file mode 100644
index 0000000..3c038cc
Binary files /dev/null and b/public/images/aespa.jpg differ
diff --git a/public/images/btn-donation-arrow-left.svg b/public/images/btn-donation-arrow-left.svg
new file mode 100644
index 0000000..9326145
--- /dev/null
+++ b/public/images/btn-donation-arrow-left.svg
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/public/images/btn-donation-arrow-right.svg b/public/images/btn-donation-arrow-right.svg
new file mode 100644
index 0000000..c04f711
--- /dev/null
+++ b/public/images/btn-donation-arrow-right.svg
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/public/images/btn-modal-close.png b/public/images/btn-modal-close.png
new file mode 100644
index 0000000..a62b9bd
Binary files /dev/null and b/public/images/btn-modal-close.png differ
diff --git a/public/images/credit.png b/public/images/credit.png
new file mode 100644
index 0000000..bffcdad
Binary files /dev/null and b/public/images/credit.png differ
diff --git a/public/images/credit.svg b/public/images/credit.svg
new file mode 100644
index 0000000..d0bcb81
--- /dev/null
+++ b/public/images/credit.svg
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/public/images/donation-card-cover.svg b/public/images/donation-card-cover.svg
new file mode 100644
index 0000000..197f0ee
--- /dev/null
+++ b/public/images/donation-card-cover.svg
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/public/images/favicon/android-chrome-192x192.png b/public/images/favicon/android-chrome-192x192.png
new file mode 100644
index 0000000..8c23846
Binary files /dev/null and b/public/images/favicon/android-chrome-192x192.png differ
diff --git a/public/images/favicon/android-chrome-512x512.png b/public/images/favicon/android-chrome-512x512.png
new file mode 100644
index 0000000..61a0f2a
Binary files /dev/null and b/public/images/favicon/android-chrome-512x512.png differ
diff --git a/public/images/favicon/apple-touch-icon.png b/public/images/favicon/apple-touch-icon.png
new file mode 100644
index 0000000..f2ad2f1
Binary files /dev/null and b/public/images/favicon/apple-touch-icon.png differ
diff --git a/public/images/favicon/favicon-16x16.png b/public/images/favicon/favicon-16x16.png
new file mode 100644
index 0000000..44a8fc3
Binary files /dev/null and b/public/images/favicon/favicon-16x16.png differ
diff --git a/public/images/favicon/favicon-32x32.png b/public/images/favicon/favicon-32x32.png
new file mode 100644
index 0000000..c890ef1
Binary files /dev/null and b/public/images/favicon/favicon-32x32.png differ
diff --git a/public/images/favicon/favicon.ico b/public/images/favicon/favicon.ico
new file mode 100644
index 0000000..8ec9fd8
Binary files /dev/null and b/public/images/favicon/favicon.ico differ
diff --git a/public/images/favicon/manifest.json b/public/images/favicon/manifest.json
new file mode 100644
index 0000000..108aaab
--- /dev/null
+++ b/public/images/favicon/manifest.json
@@ -0,0 +1,41 @@
+{
+ "name": "Fandom-K",
+ "short_name": "Fandom-K",
+ "description": "All your K-pop love, in one place",
+ "start_url": "/",
+ "display": "standalone",
+ "background_color": "#ffffff",
+ "theme_color": "#ffffff",
+ "icons": [
+ {
+ "src": "./android-icon-16x16.png",
+ "sizes": "16x16",
+ "type": "image/png",
+ "density": "0.75"
+ },
+ {
+ "src": "./android-icon-32x32.png",
+ "sizes": "32x32",
+ "type": "image/png",
+ "density": "1.0"
+ },
+ {
+ "src": "./android-icon-512x512.png",
+ "sizes": "512x72",
+ "type": "image/png",
+ "density": "1.5"
+ },
+ {
+ "src": "./apple-touch-icon.png",
+ "sizes": "180x180",
+ "type": "apple-touch-icon",
+ "density": "3.0"
+ },
+ {
+ "src": "./android-chrome-192x192.png",
+ "sizes": "192x192",
+ "type": "image/png",
+ "density": "4.0"
+ }
+ ]
+}
diff --git a/public/images/landing/blur01.png b/public/images/landing/blur01.png
new file mode 100644
index 0000000..7124760
Binary files /dev/null and b/public/images/landing/blur01.png differ
diff --git a/public/images/landing/blur02.png b/public/images/landing/blur02.png
new file mode 100644
index 0000000..f86ec02
Binary files /dev/null and b/public/images/landing/blur02.png differ
diff --git a/public/images/landing/landing_card01.png b/public/images/landing/landing_card01.png
new file mode 100644
index 0000000..818412f
Binary files /dev/null and b/public/images/landing/landing_card01.png differ
diff --git a/public/images/landing/landing_card02.png b/public/images/landing/landing_card02.png
new file mode 100644
index 0000000..ba075ca
Binary files /dev/null and b/public/images/landing/landing_card02.png differ
diff --git a/public/images/landing/landing_card03.png b/public/images/landing/landing_card03.png
new file mode 100644
index 0000000..65fec2c
Binary files /dev/null and b/public/images/landing/landing_card03.png differ
diff --git a/public/images/landing/landing_card04.png b/public/images/landing/landing_card04.png
new file mode 100644
index 0000000..25ad80e
Binary files /dev/null and b/public/images/landing/landing_card04.png differ
diff --git a/public/images/landing/landing_card05.png b/public/images/landing/landing_card05.png
new file mode 100644
index 0000000..a2b3bf2
Binary files /dev/null and b/public/images/landing/landing_card05.png differ
diff --git a/public/images/landing/landing_card06.png b/public/images/landing/landing_card06.png
new file mode 100644
index 0000000..98ed2c1
Binary files /dev/null and b/public/images/landing/landing_card06.png differ
diff --git a/public/images/landing/landing_card07.png b/public/images/landing/landing_card07.png
new file mode 100644
index 0000000..6c67f60
Binary files /dev/null and b/public/images/landing/landing_card07.png differ
diff --git a/public/images/landing/landing_card08.png b/public/images/landing/landing_card08.png
new file mode 100644
index 0000000..0df22c9
Binary files /dev/null and b/public/images/landing/landing_card08.png differ
diff --git a/public/images/landing/landing_fade01.png b/public/images/landing/landing_fade01.png
new file mode 100644
index 0000000..a244824
Binary files /dev/null and b/public/images/landing/landing_fade01.png differ
diff --git a/public/images/landing/landing_fade02.png b/public/images/landing/landing_fade02.png
new file mode 100644
index 0000000..fc80523
Binary files /dev/null and b/public/images/landing/landing_fade02.png differ
diff --git a/public/images/landing/landing_fade03.png b/public/images/landing/landing_fade03.png
new file mode 100644
index 0000000..c0525c4
Binary files /dev/null and b/public/images/landing/landing_fade03.png differ
diff --git a/public/images/landing/landing_icon01.png b/public/images/landing/landing_icon01.png
new file mode 100644
index 0000000..58d869e
Binary files /dev/null and b/public/images/landing/landing_icon01.png differ
diff --git a/public/images/landing/landing_icon02.png b/public/images/landing/landing_icon02.png
new file mode 100644
index 0000000..7acf15b
Binary files /dev/null and b/public/images/landing/landing_icon02.png differ
diff --git a/public/images/landing/landing_icon03.png b/public/images/landing/landing_icon03.png
new file mode 100644
index 0000000..e2080ab
Binary files /dev/null and b/public/images/landing/landing_icon03.png differ
diff --git a/public/images/landing/landing_mock01.png b/public/images/landing/landing_mock01.png
new file mode 100644
index 0000000..c75cf5c
Binary files /dev/null and b/public/images/landing/landing_mock01.png differ
diff --git a/public/images/landing/landing_mock02.png b/public/images/landing/landing_mock02.png
new file mode 100644
index 0000000..e9fd57c
Binary files /dev/null and b/public/images/landing/landing_mock02.png differ
diff --git a/public/images/landing/landing_mock03.png b/public/images/landing/landing_mock03.png
new file mode 100644
index 0000000..26307e2
Binary files /dev/null and b/public/images/landing/landing_mock03.png differ
diff --git a/public/images/landing/logo.svg b/public/images/landing/logo.svg
new file mode 100644
index 0000000..fad6301
--- /dev/null
+++ b/public/images/landing/logo.svg
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/public/images/landing/logo_big.png b/public/images/landing/logo_big.png
new file mode 100644
index 0000000..67a5af0
Binary files /dev/null and b/public/images/landing/logo_big.png differ
diff --git a/public/images/landing/logo_f.svg b/public/images/landing/logo_f.svg
new file mode 100644
index 0000000..bc0ff97
--- /dev/null
+++ b/public/images/landing/logo_f.svg
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/public/images/landing/top_btn.png b/public/images/landing/top_btn.png
new file mode 100644
index 0000000..6d22054
Binary files /dev/null and b/public/images/landing/top_btn.png differ
diff --git a/public/images/plus_24px.svg b/public/images/plus_24px.svg
new file mode 100644
index 0000000..5cdd172
--- /dev/null
+++ b/public/images/plus_24px.svg
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/public/images/profile-default.png b/public/images/profile-default.png
new file mode 100644
index 0000000..64b443c
Binary files /dev/null and b/public/images/profile-default.png differ
diff --git a/public/images/profile.jpg b/public/images/profile.jpg
new file mode 100644
index 0000000..a16a256
Binary files /dev/null and b/public/images/profile.jpg differ
diff --git a/public/images/radio-false.svg b/public/images/radio-false.svg
new file mode 100644
index 0000000..1cc0686
--- /dev/null
+++ b/public/images/radio-false.svg
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/public/images/radio-true.svg b/public/images/radio-true.svg
new file mode 100644
index 0000000..52a16dc
--- /dev/null
+++ b/public/images/radio-true.svg
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/public/mocks/mock01.png b/public/mocks/mock01.png
new file mode 100644
index 0000000..40f536e
Binary files /dev/null and b/public/mocks/mock01.png differ
diff --git a/public/mocks/mock02.png b/public/mocks/mock02.png
new file mode 100644
index 0000000..5f45081
Binary files /dev/null and b/public/mocks/mock02.png differ
diff --git a/public/mocks/mock03.png b/public/mocks/mock03.png
new file mode 100644
index 0000000..5b46700
Binary files /dev/null and b/public/mocks/mock03.png differ
diff --git a/public/mocks/mock04.png b/public/mocks/mock04.png
new file mode 100644
index 0000000..9519da6
Binary files /dev/null and b/public/mocks/mock04.png differ
diff --git a/src/App.jsx b/src/App.jsx
new file mode 100644
index 0000000..fca596d
--- /dev/null
+++ b/src/App.jsx
@@ -0,0 +1,30 @@
+import { Route, Routes } from "react-router-dom";
+import Toastify from "./components/Toastify";
+import { CreditProvider } from "./context/CreditContext";
+import DefaultLayout from "./layouts/DefaultLayout";
+import DonationDetail from "./pages/DonationDetail/index.jsx";
+import Landing from "./pages/Landing";
+import List from "./pages/List";
+import Mypage from "./pages/Mypage";
+import NotFound from "./pages/NotFound/index.jsx";
+import Testpage from "./pages/Testpage/index.jsx";
+
+function App() {
+ return (
+
+
+
+ } />
+ }>
+ } />
+ } />
+ } />
+ } />
+
+ } />
+
+
+ );
+}
+
+export default App;
diff --git a/src/apis/axios.js b/src/apis/axios.js
new file mode 100644
index 0000000..9628921
--- /dev/null
+++ b/src/apis/axios.js
@@ -0,0 +1,54 @@
+import axios from "axios";
+
+export const baseAPI = axios.create({
+ baseURL: "https://fandom-k-api.vercel.app",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ timeout: 10000, // 요청 하나당 최대 10초
+});
+
+const MAX_RETRY_TIME = 30000; // 총 30초 동안 재시도
+const RETRY_DELAY = 1000; // 재시도 간격 1초
+
+// 요청 인터셉터
+baseAPI.interceptors.request.use(
+ (config) => {
+ // 요청이 보내지기 전에 작업 수행
+ return config;
+ },
+ (error) => {
+ // 요청 에러 발생 시 처리
+ return Promise.reject(error);
+ },
+);
+
+// 응답 인터셉터
+baseAPI.interceptors.response.use(
+ (response) => {
+ // 응답이 정상일 때
+ return response;
+ },
+ async (error) => {
+ const config = error.config;
+
+ // 설정 정보가 없거나 이미 재시도 중이면 그대로 에러 반환
+ if (!config || config.__isRetryRequest) {
+ return Promise.reject(error);
+ }
+
+ // 최초 시도 시 설정
+ config.__isRetryRequest = true;
+ config.__retryCount = config.__retryCount || 0;
+ config.__startTime = config.__startTime || Date.now();
+
+ // 경과 시간이 30초 미만이면 재시도
+ if (Date.now() - config.__startTime < MAX_RETRY_TIME) {
+ await new Promise((resolve) => setTimeout(resolve, RETRY_DELAY));
+ return baseAPI(config); // 재요청
+ }
+
+ // 30초 경과 시 에러 반환
+ return Promise.reject(error);
+ },
+);
diff --git a/src/apis/donationsAPI.js b/src/apis/donationsAPI.js
new file mode 100644
index 0000000..d0a0acb
--- /dev/null
+++ b/src/apis/donationsAPI.js
@@ -0,0 +1,28 @@
+import { baseAPI } from "./axios";
+
+export const donationsAPI = {
+ // 조공 목록 조회
+ getDonations: async (pageSize = 20) => {
+ try {
+ const response = await baseAPI.get(
+ `/15-3/donations?&priorityIdolIds=5023&pageSize=${pageSize}`,
+ );
+ return response.data;
+ } catch (error) {
+ throw new Error("목록을 불러오는데 실패했습니다.");
+ }
+ },
+
+ // 조공 후원 요청
+ contribute: async (donationId, amount) => {
+ try {
+ const response = await baseAPI.put(
+ `/15-3/donations/${donationId}/contribute`,
+ { amount },
+ );
+ return response.data;
+ } catch (error) {
+ throw new Error("후원에 실패했습니다.");
+ }
+ },
+};
diff --git a/src/apis/idolsAPI.js b/src/apis/idolsAPI.js
new file mode 100644
index 0000000..c6b532b
--- /dev/null
+++ b/src/apis/idolsAPI.js
@@ -0,0 +1,15 @@
+import { baseAPI } from "./axios";
+
+export const idolsAPI = {
+ // 아이돌 목록 조회
+ getIdols: async (pageSize, keyword = "") => {
+ try {
+ const response = await baseAPI.get(
+ `/15-3/idols?pageSize=${pageSize}&keyword=${keyword}`,
+ );
+ return response.data;
+ } catch (error) {
+ throw new Error("목록을 불러오는데 실패했습니다.");
+ }
+ },
+};
diff --git a/src/apis/voteApi.js b/src/apis/voteApi.js
new file mode 100644
index 0000000..1a91a95
--- /dev/null
+++ b/src/apis/voteApi.js
@@ -0,0 +1,14 @@
+import { baseAPI } from "./axios";
+
+// 투표 생성 API
+export const votesAPI = {
+ addVote: async (idolId) => {
+ try {
+ const response = await baseAPI.post("/15-3/votes", { idolId });
+ return response.data;
+ } catch (error) {
+ console.error("투표 생성에 실패했습니다.", error);
+ throw new Error("투표 생성에 실패했습니다.");
+ }
+ },
+};
diff --git a/src/components/Button/Button.jsx b/src/components/Button/Button.jsx
new file mode 100644
index 0000000..13ab78f
--- /dev/null
+++ b/src/components/Button/Button.jsx
@@ -0,0 +1,43 @@
+import {
+ buttonSizeMobile,
+ buttonSizes,
+ buttonVariants,
+} from "./Button.styles.js";
+import { getButtonStyles } from "./Button.styles.js";
+
+/** @jsxImportSource @emotion/react */
+
+const Button = ({
+ type = "button",
+ size = "donate-lg",
+ variant = "primary",
+ disabled = false,
+ fullWidth = false,
+ onClick,
+ children,
+}) => {
+ const currentSize = buttonSizes[size];
+ const currentVariant = buttonVariants[variant];
+ const mobileSize = buttonSizeMobile[size];
+
+ return (
+ <>
+
+ {children}
+
+ >
+ );
+};
+
+export default Button;
diff --git a/src/components/Button/Button.styles.js b/src/components/Button/Button.styles.js
new file mode 100644
index 0000000..5430d9f
--- /dev/null
+++ b/src/components/Button/Button.styles.js
@@ -0,0 +1,85 @@
+import { css } from "@emotion/react";
+
+//emotion 함수 사용으로 동적관리 가능
+
+export const getButtonStyles = (
+ currentSize,
+ currentVariant,
+ disabled,
+ mobileSize,
+ fullWidth = false, // 너비 제어
+) => css`
+ width: ${fullWidth ? "100%" : currentSize.width || "auto"};
+ height: ${currentSize.height};
+ font-size: ${currentSize.fontSize};
+ background: ${disabled ? "var(--gray-828282)" : currentVariant.background};
+ color: ${disabled ? "var( --white-F7F7F8)" : currentVariant.color};
+ border: ${disabled ? "none" : currentVariant?.border || "none"};
+ border-radius: ${currentSize.borderradius || "3px"};
+ cursor: ${disabled ? "not-allowed" : "pointer"};
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ transition: all 0.2s ease; // 트랜지션 효과
+ // 호버
+ &:hover {
+ opacity: ${disabled ? 1 : 0.85};
+ transform: ${disabled ? "none" : "scale(1.02)"};
+ }
+ // 반응형 사이즈
+ @media (max-width: 426px) {
+ width: ${fullWidth ? "100%" : mobileSize?.width || currentSize.width};
+ height: ${mobileSize?.height || currentSize.height};
+ font-size: ${mobileSize?.fontSize || currentSize.fontSize};
+ border-radius: ${mobileSize?.borderradius || currentSize.borderradius || "3px"};
+ }
+`;
+
+const buttonSizes = {
+ // 각 상황에 맞는 사이즈 사용
+ "start-now": { width: "295px", height: "42px", fontSize: "14px" },
+ "donate-lg": { height: "42px", fontSize: "14px" },
+ "donate-md": { width: "234px", height: "40px", fontSize: "13px" },
+ "donate-sm": { width: "142px", height: "31px", fontSize: "13px" },
+
+ "vote-lg": { height: "42px", fontSize: "14px" },
+ "vote-md": { height: "42px", fontSize: "14px" },
+
+ check: { height: "42px", fontSize: "14px" },
+
+ recharge: { height: "42px", fontSize: "14px" },
+
+ "vote-chart": { width: "128px", height: "32px", fontSize: "13px" },
+
+ "load-more": { width: "326px", height: "42px", fontSize: "14px" },
+
+ add: {
+ width: "255px",
+ height: "48px",
+ fontSize: "16px",
+ borderradius: "24px",
+ },
+};
+
+// 줄어드는 버튼만 반응형 구현
+const buttonSizeMobile = {
+ "start-now": { width: "230px", height: "48px", fontSize: "14px" },
+ "donate-md": { width: "142px", height: "31px", fontSize: "10px" },
+ "vote-lg": { height: "42px", fontSize: "14px" },
+};
+
+// 색상
+const buttonVariants = {
+ primary: {
+ background:
+ "linear-gradient(90deg, var(--orange-F96D69), var(--pink-FE5493))",
+ color: "var(--white-F7F7F8)",
+ },
+ dark: {
+ background: "var(--white-F7F7F8-10)",
+ color: "var(--white-F7F7F8)",
+ border: "1px solid #F1EEF9CC",
+ },
+};
+
+export { buttonSizes, buttonVariants, buttonSizeMobile };
diff --git a/src/components/CheckIdol.jsx b/src/components/CheckIdol.jsx
new file mode 100644
index 0000000..9e1f531
--- /dev/null
+++ b/src/components/CheckIdol.jsx
@@ -0,0 +1,54 @@
+/** @jsxImportSource @emotion/react */
+import { css } from "@emotion/react";
+
+// size: 아이돌 원 사이즈
+// checkSize: 체크표시 사이즈
+const CheckIdol = ({ isChecked, size, checkSize }) => {
+ if (!isChecked) return null;
+
+ return (
+
+
+
+
+ );
+};
+
+const checkWrapper = (size) => css`
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ width: ${size}px;
+ height: ${size}px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ z-index: 1; /* 아이콘과 배경이 위로 올라가도록 */
+`;
+
+const checkBackground = (size) => css`
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ width: ${size * 0.9}px;
+ height: ${size * 0.9}px;
+ background: linear-gradient(271deg, #F96E68 -9.84%, #FE578F 107.18%);
+ border-radius: 50%;
+ opacity: 0.5;
+ z-index: 0; /* 배경이 아이콘 아래에 위치하도록 */
+`;
+
+const checkIconStyle = (checkSize) => css`
+ position: relative;
+ width: ${checkSize}px;
+ height: ${checkSize}px;
+ z-index: 1; /* 아이콘이 배경 위에 표시되도록 */
+`;
+
+export default CheckIdol;
diff --git a/src/components/Circle.jsx b/src/components/Circle.jsx
new file mode 100644
index 0000000..8d3cca3
--- /dev/null
+++ b/src/components/Circle.jsx
@@ -0,0 +1,53 @@
+import styled from "@emotion/styled";
+import PropTypes from "prop-types";
+import React from "react";
+
+// 원하는 사이즈가 있으면 없을 시 기본 128px
+export const ImageCircle = styled.div`
+ display: flex;
+ width: ${(props) => props.size || "128px"};
+ height: ${(props) => props.size || "128px"};
+ border-radius: 50%;
+ border: 1.31px solid #f96868;
+ align-items: center;
+ object-position: center;
+ padding: 3px;
+ position: relative;
+ box-sizing: content-box;
+
+`;
+// 원하는 사이즈가 있으면 없을 시 기본 128px
+// 아이돌 이미지가 Circle frame보다 더 작게하기 위해 *0.9
+const IdolImage = styled.img`
+ object-fit: cover;
+ object-position: top;
+ width: calc(${(props) => props.size || "128px"} );
+ height: calc(${(props) => props.size || "128px"} );
+ border-radius: 50%;
+ cursor:pointer;
+ margin:0 auto;
+
+`;
+
+const Circle = ({ imageUrl, alt, size, children, ...props }) => {
+ return (
+
+
+ {children}
+
+ );
+};
+
+Circle.propTypes = {
+ imageUrl: PropTypes.string.isRequired,
+ alt: PropTypes.string.isRequired,
+ size: PropTypes.string,
+};
+
+export default Circle;
diff --git a/src/components/Error/Error.style.js b/src/components/Error/Error.style.js
new file mode 100644
index 0000000..cb3ae67
--- /dev/null
+++ b/src/components/Error/Error.style.js
@@ -0,0 +1,125 @@
+/** @jsxImportSource @emotion/react */
+import { css } from "@emotion/react";
+
+/**
+ * 에러 컴포넌트의 전체 레이아웃을 구성하는 wrapper 스타일
+ * 수직 정렬과 가운데 정렬을 통해 중앙에 요소를 배치
+ */
+export const errorWrapper = css`
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ gap: 12px;
+
+ @media(max-width: 476px) {
+ gap: 6px;
+ }
+`;
+
+/**
+ * 느낌표 아이콘 border 원형 배경 스타일
+ * 선형 그라데이션 배경과 원형 테두리를 적용하여 시각적으로 강조
+ */
+export const iconWrapper = css`
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ position: relative;
+ width: 90px;
+ height: 90px;
+ margin-bottom: 16px; /* 'margin: button' 오타 수정 */
+ background: linear-gradient(90deg, #F86F65 0%, #FE5493 100%);
+ border-radius: 50%;
+ z-index: 0;
+
+ @media(max-width: 476px) {
+ width: 60px;
+ height: 60px;
+ }
+`;
+
+/**
+ * 아이콘의 내부 원형(검은색 배경) 스타일
+ * iconWrapper 안에 정중앙에 위치하며, 내부 아이콘을 감싸는 역할
+ */
+export const black = css`
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ background: #000000; /* = 대신 : */
+ width: 75px;
+ height: 75px;
+ border-radius: 50%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ z-index: 1;
+
+ @media(max-width: 476px) {
+ width: 50px;
+ height: 50px;
+ }
+`;
+
+/**
+ * 흰색 느낌표 아이콘 스타일입니다.
+ */
+export const icon = css`
+ font-size: 50px;
+ font-weight: 800;
+ color: #ffffff;
+ z-index: 2;
+
+ @media(max-width: 476px) {
+ font-size: 30px;
+ font-weight: 800;
+ }
+`;
+
+/**
+ * 에러 제목 텍스트 스타일
+ */
+export const title = css`
+ font-size: 20px;
+ font-weight: 800;
+ color: #ffffff;
+
+ @media(max-width: 476px) {
+ font-size: 16px;
+ font-weight: 800;
+ }
+`;
+
+/**
+ * 에러 상세 설명 텍스트 스타일
+ */
+export const description = css`
+ font-size: 15px;
+ color: #ffffff;
+
+ @media(max-width: 476px) {
+ font-size: 13px;
+ }
+`;
+
+/**
+ * '다시 시도하기' 버튼 스타일
+ */
+export const button = css`
+width: 140px;
+height: 30px;
+
+background: linear-gradient(90deg, #F86F65 0%, #FE5493 100%);
+border-radius: 20px;
+
+font-size: 12px;
+font-weight: 400;
+
+@media(max-width: 476px) {
+width: 100px;
+height: 25px;
+
+font-size: 10px;
+}`;
diff --git a/src/components/Error/index.jsx b/src/components/Error/index.jsx
new file mode 100644
index 0000000..d28f61c
--- /dev/null
+++ b/src/components/Error/index.jsx
@@ -0,0 +1,50 @@
+/** @jsxImportSource @emotion/react */
+
+import {
+ black,
+ button,
+ description,
+ errorWrapper,
+ icon,
+ iconWrapper,
+ title,
+} from "./Error.style";
+
+const ERROR_MESSAGE = {
+ donation: "후원 목록을",
+ chart: "아이돌 목록을",
+ mypage: "아이돌을",
+};
+
+/**
+ * 에러 발생 시 사용자에게 메시지를 보여주는 컴포넌트입니다.
+ *
+ * @param {Object} props - 컴포넌트에 전달되는 props
+ * @param {"donation"|"chart"|"myPage"|string} [props.error="페이지를"] - 에러가 발생한 영역을 식별하는 키 값.
+ * `ERROR_MESSAGE` 객체의 키값 중 하나를 전달하면 해당 메시지가 자동으로 전달됩니다.
+ * 정의되지 않은 값이 들어오면 그대로 출력됩니다.
+ * @returns 오류 메시지와 다시 시도 버튼
+ */
+function LoadingError({ error = "페이지를" }) {
+ const errorMessage = ERROR_MESSAGE[error] || error;
+
+ const handleReload = () => {
+ location.reload();
+ };
+ return (
+
+
+
문제가 발생했습니다.
+
{errorMessage} 불러오는데 실패했습니다.
+
+ 다시 시도하기
+
+
+ );
+}
+
+export default LoadingError;
diff --git a/src/components/Footer/Footer.styles.js b/src/components/Footer/Footer.styles.js
new file mode 100644
index 0000000..f878db5
--- /dev/null
+++ b/src/components/Footer/Footer.styles.js
@@ -0,0 +1,41 @@
+/** @jsxImportSource @emotion/react */
+import { css } from "@emotion/react";
+
+export const footerStyles = css`
+ top: 0;
+ left: 0;
+ .mainGrid {
+ display: flex;
+ height: 160px;
+ justify-content: space-between;
+ align-items: center;
+ align-self: stretch;
+ }
+`;
+
+export const footerFontStyles = css`
+ color: #ffffff; /* 색상 수정 */
+ text-align: center;
+ font-size: 16px;
+ font-style: normal;
+ font-weight: 400;
+ line-height: normal;
+`;
+
+export const githubLogoStyles = css`
+ width: 20px;
+ height: 20px;
+ flex-shrink: 0;
+ cursor: pointer;
+`;
+
+export const buttonStyles = css`
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 8px 16px;
+ background-color: transparent;
+ border: none;
+ cursor: pointer;
+ gap: 8px;
+`;
diff --git a/src/components/Footer/index.jsx b/src/components/Footer/index.jsx
new file mode 100644
index 0000000..0f12423
--- /dev/null
+++ b/src/components/Footer/index.jsx
@@ -0,0 +1,38 @@
+import githubLogo from "../../../public/icons/github-mark-white.svg";
+/** @jsxImportSource @emotion/react */
+import {
+ buttonStyles,
+ footerFontStyles,
+ footerStyles,
+ githubLogoStyles,
+} from "./Footer.styles";
+
+const Footer = () => {
+ const handleGithubClick = () => {
+ window.open("https://github.com/FandomJingyu/Fandom-K", "_blank");
+ };
+
+ return (
+
+
+
©codeit - 2025
+
15기 3조 FandomJingyu
+
+
+ GitHub
+
+
+
+ );
+};
+
+export default Footer;
diff --git a/src/components/Header/Header.styles.js b/src/components/Header/Header.styles.js
new file mode 100644
index 0000000..8da3b41
--- /dev/null
+++ b/src/components/Header/Header.styles.js
@@ -0,0 +1,37 @@
+/** @jsxImportSource @emotion/react */
+import { css } from "@emotion/react";
+
+export const headerStyles = css`
+ position: sticky;
+ z-index: 99;
+ top: 0;
+ left: 0;
+ width: 100%;
+ background: rgba(2, 0, 14, 0.6); // 반투명 배경
+ backdrop-filter: blur(10px); // 블러 효과
+ -webkit-backdrop-filter: blur(10px); // Safari 대응
+
+ .mainGrid {
+ height: 80px;
+ display: flex;
+ align-items: center;
+ justify-content: flex-end;
+ position: relative;
+ }
+`;
+
+export const logoStyles = css`
+ width: 10.5rem;
+ height: 2rem;
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+`;
+
+export const profileStyles = css`
+ width: 3.125rem;
+ height: 3.125rem;
+ border-radius: 50%;
+ object-fit: cover;
+`;
diff --git a/src/components/Header/index.jsx b/src/components/Header/index.jsx
new file mode 100644
index 0000000..afe31ae
--- /dev/null
+++ b/src/components/Header/index.jsx
@@ -0,0 +1,22 @@
+/** @jsxImportSource @emotion/react */
+import { Link } from "react-router-dom";
+import logo from "/icons/logo.svg";
+import profile from "/images/profile.jpg";
+import { headerStyles, logoStyles, profileStyles } from "./Header.styles";
+
+const Header = () => {
+ return (
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default Header;
diff --git a/src/components/Modal/Modal.styles.js b/src/components/Modal/Modal.styles.js
new file mode 100644
index 0000000..6e3b7de
--- /dev/null
+++ b/src/components/Modal/Modal.styles.js
@@ -0,0 +1,85 @@
+import { css } from "@emotion/react";
+
+export const rootStyles = (isFullScreen) => css`
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ z-index: 1000;
+
+ ${
+ isFullScreen
+ ? `
+ display: block;
+ `
+ : `display: flex;
+ justify-content: center;
+ align-items: center; `
+ }
+`;
+
+export const backdropStyles = css`
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background-color: rgba(0, 0, 0, 0.5);
+`;
+
+export const modalStyles = (isFullScreen, type) => css`
+ position: relative;
+ background-color: ${isFullScreen ? "var(--black-02000E)" : "var(--black-181D26)"};
+ gap: 8px;
+ padding: ${
+ type === "credit"
+ ? "24px 16px 32px 16px" // credit 타입일 경우
+ : "24px 16px 12px 16px" // 그 외에는 기본 패딩
+ };
+
+ ${
+ isFullScreen
+ ? `
+ width: 100%;
+ height: 100%;
+ border-radius: 0;
+ `
+ : `
+ min-width: 340px;
+ min-height: 320px;
+ border-radius: 12px;
+ `
+ }
+`;
+
+export const modalHeaderStyles = (isFullScreen) => css`
+ display: flex;
+ align-items: center;
+ justify-content: ${isFullScreen ? "flex-start" : "space-between"};
+ margin-bottom: 24px;
+ position: relative;
+
+ h2 {
+ color: var(--white-F7F7F8);
+ font-size: 18px;
+ font-weight: 600;
+ line-height: normal;
+ ${isFullScreen && "position: absolute; left: 50%; transform: translateX(-50%);"}
+ }
+`;
+
+export const buttonStyles = (isFullScreen) => css`
+ background: none;
+ border: none;
+ padding: 0;
+ cursor: pointer;
+ position: relative;
+
+ img {
+ width: 24px;
+ height: 24px;
+ }
+
+ ${isFullScreen && "margin-right: auto;"}
+`;
diff --git a/src/components/Modal/ModalPortal.jsx b/src/components/Modal/ModalPortal.jsx
new file mode 100644
index 0000000..b80067c
--- /dev/null
+++ b/src/components/Modal/ModalPortal.jsx
@@ -0,0 +1,25 @@
+import React, { useEffect, useState } from "react";
+import Portal from "./Portal";
+import createModalPortalManager from "./modalPortalManager";
+
+const modalPortalManager = createModalPortalManager();
+
+/**
+ * Modal Portal 컴포넌트
+ */
+const ModalPortal = ({ children }) => {
+ const [modalRoot, setModalRoot] = useState(null);
+
+ useEffect(() => {
+ const root = modalPortalManager.mountRoot();
+ setModalRoot(root);
+
+ return () => {
+ modalPortalManager.unmountRoot();
+ };
+ }, []);
+
+ return {children} ;
+};
+
+export default ModalPortal;
diff --git a/src/components/Modal/Portal.jsx b/src/components/Modal/Portal.jsx
new file mode 100644
index 0000000..39dfef0
--- /dev/null
+++ b/src/components/Modal/Portal.jsx
@@ -0,0 +1,21 @@
+import React, { useEffect, useState } from "react";
+import { createPortal } from "react-dom";
+
+/**
+ * Portal 컴포넌트
+ */
+const Portal = ({ children, targetElement }) => {
+ const [isMounted, setIsMounted] = useState(false);
+
+ useEffect(() => {
+ setIsMounted(true);
+ }, []);
+
+ if (!isMounted || !targetElement) {
+ return null;
+ }
+
+ return createPortal(children, targetElement);
+};
+
+export default Portal;
diff --git a/src/components/Modal/index.jsx b/src/components/Modal/index.jsx
new file mode 100644
index 0000000..184f143
--- /dev/null
+++ b/src/components/Modal/index.jsx
@@ -0,0 +1,110 @@
+/** @jsxImportSource @emotion/react */
+import React, { useEffect } from "react";
+import closeArrow from "/public/icons/icon-arrow-left.svg";
+import closeImg from "/public/images/btn-modal-close.png";
+import useIsMobile from "../../hooks/useIsMobile";
+import {
+ backdropStyles,
+ buttonStyles,
+ modalHeaderStyles,
+ modalStyles,
+ rootStyles,
+} from "./Modal.styles";
+import ModalPortal from "./ModalPortal";
+
+/**
+ * @param {Object} props
+ * @param {boolean} props.isOpen
+ * @param {Function} props.onClose
+ * @param {React.ReactNode} props.children
+ */
+
+const modalData = {
+ credit: {
+ title: "크레딧 충전",
+ },
+ voteWoman: {
+ title: "이달의 여자 아이돌",
+ },
+ voteMan: {
+ title: "이달의 남자 아이돌",
+ },
+ default: {
+ title: "기본 모달",
+ },
+};
+
+const Modal = ({
+ isOpen,
+ onClose,
+ children,
+ type = "default",
+ isMobileFullScreen = false,
+}) => {
+ const isMobile = useIsMobile(); // 여기에서 조건을 하지 않고 항상 호출됨
+ const isFullScreen = isMobile && isMobileFullScreen;
+
+ const { title } = modalData[type] || modalData.default; // type에 맞는 title 가져오기
+
+ // 모달이 열릴 때 body에 overflow-y: hidden 적용
+ useEffect(() => {
+ if (isOpen) {
+ document.body.style.overflowY = "hidden"; // 세로 스크롤만 막기
+ } else {
+ document.body.style.overflowY = ""; // 원래 상태로 복원
+ }
+
+ // 클린업: 모달이 닫히면 스크롤 복원
+ return () => {
+ document.body.style.overflowY = "";
+ };
+ }, [isOpen]);
+
+ if (!isOpen) return null;
+
+ const handleKeyDown = (event) => {
+ // Enter 키 또는 Space 키를 눌렀을 때 모달 닫기
+ if (event.key === "Enter" || event.key === " ") {
+ onClose();
+ }
+ };
+
+ return (
+
+
+ {/* Backdrop */}
+
+ {/* 비어 있는 버튼이지만 키보드 포커스와 역할이 명확함 */}
+
+ {/* Modal */}
+
+
+
{title}
+
+
+
+
+ {/* Modal Content */}
+ {children}
+
+
+
+ );
+};
+
+export default Modal;
diff --git a/src/components/Modal/modalPortalManager.js b/src/components/Modal/modalPortalManager.js
new file mode 100644
index 0000000..d8206f2
--- /dev/null
+++ b/src/components/Modal/modalPortalManager.js
@@ -0,0 +1,53 @@
+function createModalPortalManager() {
+ let activeModalCount = 0;
+ /**
+ * @type {HTMLElement | null}
+ */
+ let modalRoot = null;
+
+ const createPortalRootElement = () => {
+ modalRoot = document.createElement("div");
+ modalRoot.setAttribute("id", "modal-root");
+ document.body.appendChild(modalRoot);
+ };
+
+ const removePortalRootElement = () => {
+ if (modalRoot) {
+ document.body.removeChild(modalRoot);
+ }
+
+ modalRoot = null;
+ };
+
+ /**
+ * @returns {HTMLElement}
+ */
+ const mountRoot = () => {
+ if (activeModalCount === 0) {
+ createPortalRootElement();
+ }
+
+ activeModalCount++;
+ return modalRoot;
+ };
+
+ /**
+ * @returns {void}
+ */
+ const unmountRoot = () => {
+ if (activeModalCount > 0) {
+ activeModalCount--;
+ }
+
+ if (activeModalCount === 0) {
+ removePortalRootElement();
+ }
+ };
+
+ return {
+ mountRoot,
+ unmountRoot,
+ };
+}
+
+export default createModalPortalManager;
diff --git a/src/components/ProfileXIcon.jsx b/src/components/ProfileXIcon.jsx
new file mode 100644
index 0000000..7fa67e9
--- /dev/null
+++ b/src/components/ProfileXIcon.jsx
@@ -0,0 +1,37 @@
+import styled from "@emotion/styled";
+
+const StyledSvg = styled.svg`
+ width: ${({ width }) => width || "32px"};
+ height: ${({ height }) => height || "32px"};
+`;
+
+const ProfileXIcon = ({ width = "32px", height = "32px" }) => {
+ return (
+
+
+
+
+ );
+};
+
+export default ProfileXIcon;
diff --git a/src/components/RadioButton/index.jsx b/src/components/RadioButton/index.jsx
new file mode 100644
index 0000000..41eb676
--- /dev/null
+++ b/src/components/RadioButton/index.jsx
@@ -0,0 +1,101 @@
+/** @jsxImportSource @emotion/react */
+import { css } from "@emotion/react";
+
+import radioFalse from "/images/radio-false.svg";
+import radioTrue from "/images/radio-true.svg";
+
+// 투표 모달창 스타일 정의
+const voteRadioButton = css`
+ display: flex;
+ justify-content: space-between;
+ width: 477px;
+ height: 70px;
+ padding-top: 8px;
+
+ @media (max-width: 425px) {
+ width: 100%;
+ }
+`;
+
+// 크레딧 모달창 스타일 정의 & 선택된 경우 border 색상 변경 (input display:none으로 설정해서 className을 지정해 border색상 변경)
+const chargeRadioButton = (checked) => css`
+ display: flex;
+ align-items: center;
+ justify-content: space-around;
+ width: 100%;
+ height: 62px;
+ border-radius: 8px;
+ border: 1px solid ${checked ? "var(--orange-F96D69)" : "var(--white-F7F7F8)"};
+`;
+
+// input 자체는 화면에 표시되지 않도록 숨김 처리
+const inputStyle = css`
+ clip: rect(0 0 0 0);
+ clip-path: inset(50%);
+ width: 1px;
+ height: 1px;
+ margin: -1px;
+ overflow: hidden;
+ position: absolute;
+`;
+
+// 라벨 내부 콘텐츠 영역 스타일
+const contentWrapper = (className) => css`
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ width: 100%;
+ height: 100%;
+ padding: ${className === "vote" ? "0" : "0 23px 0 16px"};
+`;
+
+// 라디오 버튼 스타일 정의
+const radioIcon = css`
+ width: 16px;
+ height: 16px;
+`;
+
+/**
+ * 공통 RadioButton 컴포넌트
+ *
+ * @param {any} value - 이 버튼이 나타내는 고유 값 (예: voteId 또는 크레딧 수)
+ * @param {boolean} checked - 선택 여부 (상위 컴포넌트에서 현재 선택된 값과 비교해 전달)
+ * @param {function} onChange - 클릭 시 호출될 콜백 함수 (value를 인자로 전달)
+ * @param {React.ReactNode} children - 버튼 내부에 렌더링될 콘텐츠 (아이돌 정보 또는 크레딧 금액 등)
+ * @param {"vote" | "charge"} className - 버튼 타입 ("vote"는 아이돌 투표, "charge"는 크레딧 선택에 사용)
+ */
+
+function RadioButton({ value, checked, onChange, children, className }) {
+ const styleObj = {
+ vote: voteRadioButton,
+ charge: chargeRadioButton(checked),
+ };
+
+ const id = `radio-${value}`; // 선택된 값에 따라 고유 ID 설정 (라벤 연결용)
+
+ return (
+
+ {/* 실제 라디오 버튼은 숨겨져 있고, onChange로 선택 이벤트 처리 */}
+ onChange(value)}
+ />
+
+ {/* 라벨 내부 콘텐츠: children + 선택 상태에 따라 아이콘 변경 */}
+
+
{children}
+
+
+
+ );
+}
+
+export default RadioButton;
diff --git a/src/components/TestComponents.jsx b/src/components/TestComponents.jsx
new file mode 100644
index 0000000..21a1b70
--- /dev/null
+++ b/src/components/TestComponents.jsx
@@ -0,0 +1,41 @@
+import styled from '@emotion/styled';
+import { IDOLS } from '../mocks/idols';
+import { DONATIONS } from '../mocks/donations';
+
+const StyledTest = styled.div`
+ margin: auto;
+ color: var(--white-F7F7F8);
+ padding-block: 100px;
+
+ .mainGrid {
+ border: 1px solid var(--orange-F96D69);
+ padding: 30px;
+ border-radius: 10px;
+ }
+`;
+
+const TestComponents = () => {
+ return
+
+
아이돌 데이터
+ {IDOLS.map((idol) => (
+
+
이름: {idol.idol.name}
+
그룹: {idol.idol.group}
+
총 투표수: {idol.idol.totalVotes}
+
+ ))}
+
후원 데이터
+ {DONATIONS.map((donation) => (
+
+
제목: {donation.title}
+
장소: {donation.subtitle}
+
목표 금액: {donation.targetDonation}
+
현재 금액: {donation.receivedDonations}
+
+ ))}
+
+ ;
+};
+
+export default TestComponents;
diff --git a/src/components/Toastify.jsx b/src/components/Toastify.jsx
new file mode 100644
index 0000000..32691d1
--- /dev/null
+++ b/src/components/Toastify.jsx
@@ -0,0 +1,27 @@
+import { ToastContainer } from "react-toastify";
+import "react-toastify/dist/ReactToastify.css";
+
+function Toastify() {
+ const checkDevice = () => {
+ return window.innerWidth < 768 ? "mobile" : "desktop";
+ };
+
+ return (
+ <>
+
+ >
+ );
+}
+
+export default Toastify;
diff --git a/src/context/CreditContext.jsx b/src/context/CreditContext.jsx
new file mode 100644
index 0000000..237a6e3
--- /dev/null
+++ b/src/context/CreditContext.jsx
@@ -0,0 +1,45 @@
+import { createContext, useContext, useEffect, useState } from "react";
+
+// Context 생성
+const CreditContext = createContext();
+
+// Provider 정의
+const CreditProvider = ({ children }) => {
+ const [credit, setCredit] = useState(() => {
+ const stored = localStorage.getItem("credit");
+ return stored !== null ? Number(stored) : 10000; // 크레딧의 초기값은 10000으로 설정
+ });
+
+ // 크레딧값이 변경될 때마다 localStorage에 저장
+ useEffect(() => {
+ localStorage.setItem("credit", String(credit));
+ }, [credit]);
+
+ // Credit 상태 관리를 위한 함수들
+ // add: 기존 크레딧에서 크레딧을 추가적으로 충전할 경우
+ const addCredit = (amount) => setCredit((prev) => prev + amount);
+
+ // deduct: 후원을 위해 크레딧을 사용한 경우
+ const deductCredit = (amount) => setCredit((prev) => prev - amount);
+
+ const value = {
+ credit,
+ addCredit,
+ deductCredit,
+ };
+
+ return (
+ {children}
+ );
+};
+
+// 커스텀 훅
+const useCredit = () => {
+ const context = useContext(CreditContext);
+ if (!context) {
+ throw new Error("useCredit은 CreditProvider 내부에서 사용되어야 합니다.");
+ }
+ return context;
+};
+
+export { CreditProvider, useCredit };
diff --git a/src/hooks/useChart.jsx b/src/hooks/useChart.jsx
new file mode 100644
index 0000000..0c2e924
--- /dev/null
+++ b/src/hooks/useChart.jsx
@@ -0,0 +1,89 @@
+import { useEffect, useMemo, useState } from "react";
+import { idolsAPI } from "../apis/idolsAPI";
+
+const ITEMS_PER_PAGE = 10;
+const TABLET_ITEMS_PER_PAGE = 5;
+const MOBILE_ITEMS_PER_PAGE = 5;
+
+const getIsMobile = () => window.matchMedia("(max-width: 425px)").matches;
+const getIsTablet = () =>
+ window.matchMedia("(min-width: 426px) and (max-width: 768px)").matches;
+
+const useChart = () => {
+ const [idols, setIdols] = useState([]);
+ const [activeTab, setActiveTab] = useState("female");
+ const [visibleCount, setVisibleCount] = useState(ITEMS_PER_PAGE);
+ const [loading, setLoading] = useState(true);
+
+ useEffect(() => {
+ const fetchIdols = async () => {
+ try {
+ setLoading(true);
+ const res = await idolsAPI.getIdols(100);
+ setIdols(res.list);
+ } catch (e) {
+ console.error("아이돌 불러오기 실패", e);
+ } finally {
+ setLoading(false);
+ }
+ };
+ fetchIdols();
+ }, []);
+
+ useEffect(() => {
+ const handleResize = () => {
+ if (getIsMobile()) setVisibleCount(MOBILE_ITEMS_PER_PAGE);
+ else if (getIsTablet()) setVisibleCount(TABLET_ITEMS_PER_PAGE);
+ else setVisibleCount(ITEMS_PER_PAGE);
+ };
+
+ handleResize();
+ window.addEventListener("resize", handleResize);
+ return () => window.removeEventListener("resize", handleResize);
+ }, []);
+
+ const femaleIdols = useMemo(() => {
+ return idols
+ .filter((i) => i.gender === "female")
+ .sort((a, b) => b.totalVotes - a.totalVotes);
+ }, [idols]);
+
+ const maleIdols = useMemo(() => {
+ return idols
+ .filter((i) => i.gender === "male")
+ .sort((a, b) => b.totalVotes - a.totalVotes);
+ }, [idols]);
+
+ const isFemale = activeTab === "female";
+ const visibleList = (isFemale ? femaleIdols : maleIdols).slice(
+ 0,
+ visibleCount,
+ );
+
+ const handleMore = () => {
+ if (getIsMobile()) setVisibleCount((prev) => prev + MOBILE_ITEMS_PER_PAGE);
+ else if (getIsTablet())
+ setVisibleCount((prev) => prev + TABLET_ITEMS_PER_PAGE);
+ else setVisibleCount((prev) => prev + ITEMS_PER_PAGE);
+ };
+
+ const handleTabChange = (tab) => {
+ setActiveTab(tab);
+ if (getIsMobile()) setVisibleCount(MOBILE_ITEMS_PER_PAGE);
+ else if (getIsTablet()) setVisibleCount(TABLET_ITEMS_PER_PAGE);
+ else setVisibleCount(ITEMS_PER_PAGE);
+ };
+
+ return {
+ idols,
+ activeTab,
+ loading,
+ visibleList,
+ handleMore,
+ handleTabChange,
+ femaleIdols,
+ maleIdols,
+ };
+};
+
+export default useChart;
diff --git a/src/hooks/useChartVoteModal.jsx b/src/hooks/useChartVoteModal.jsx
new file mode 100644
index 0000000..8ab36ef
--- /dev/null
+++ b/src/hooks/useChartVoteModal.jsx
@@ -0,0 +1,92 @@
+import { useEffect, useState } from "react";
+import { toast } from "react-toastify";
+import { idolsAPI } from "../apis/idolsAPI";
+import { votesAPI } from "../apis/voteApi";
+import { useCredit } from "../context/CreditContext";
+
+export const useChartVoteModal = (gender, closeModal) => {
+ const [idols, setIdols] = useState([]); // 아이돌 목록을 로컬 상태로 관리
+ const [selectedIdolId, setSelectedIdolId] = useState(null);
+ const [loading, setLoading] = useState(true); // 아이돌 목록 로딩 상태
+ const [error, setError] = useState(false);
+ const { credit, deductCredit } = useCredit();
+
+ // 컴포넌트가 마운트될 때 아이돌 목록을 받아오는 useEffect
+ useEffect(() => {
+ const fetchIdols = async () => {
+ try {
+ const response = await idolsAPI.getIdols(100); // 아이돌 목록 API 호출
+ const filteredIdols = response.list.filter(
+ (idol) => idol.gender === gender,
+ );
+ setIdols(filteredIdols); // 받아온 아이돌 목록을 상태에 저장
+ setLoading(false); // 로딩 상태 종료
+ } catch (error) {
+ console.error(error);
+ setError(true); // 에러 상태 설정
+ toast.error("아이돌 목록을 불러오는 데 실패했습니다.");
+ setLoading(false);
+ }
+ };
+
+ fetchIdols();
+ }, [gender]);
+
+ const handleVote = async (e) => {
+ e.preventDefault();
+
+ if (!selectedIdolId) {
+ toast.error("투표할 아이돌을 선택해주세요.");
+ return;
+ }
+
+ if (credit < 1000) {
+ toast.error("크레딧이 부족합니다.");
+ return;
+ }
+
+ setLoading(true); // 투표 진행 시 로딩 시작
+
+ try {
+ deductCredit(1000);
+
+ const selectedIdol = idols.find((idol) => idol.id === selectedIdolId);
+ if (!selectedIdol) {
+ throw new Error("아이돌을 찾을 수 없습니다.");
+ }
+
+ const voteResponse = await votesAPI.addVote(selectedIdolId);
+
+ setIdols((prevIdols) =>
+ prevIdols.map((idol) =>
+ idol.id === selectedIdolId
+ ? { ...idol, totalVotes: voteResponse.idol.totalVotes }
+ : idol,
+ ),
+ );
+
+ toast.success(`${selectedIdol.name}에게 투표 완료!`);
+ closeModal?.();
+ setSelectedIdolId(null);
+ } catch (error) {
+ console.error(error);
+ setError(true);
+ toast.error("투표에 실패했습니다. 다시 시도해주세요.");
+ } finally {
+ setLoading(false); // 로딩 상태 종료
+ }
+ };
+
+ const handleIdolSelect = (id) => {
+ setSelectedIdolId(id);
+ };
+
+ return {
+ idols,
+ selectedIdolId,
+ handleVote,
+ handleIdolSelect,
+ loading,
+ error,
+ };
+};
diff --git a/src/hooks/useDonation.jsx b/src/hooks/useDonation.jsx
new file mode 100644
index 0000000..8ef32b8
--- /dev/null
+++ b/src/hooks/useDonation.jsx
@@ -0,0 +1,69 @@
+import { useCallback, useEffect, useState } from "react";
+import { donationsAPI } from "../apis/donationsAPI";
+
+const LOCAL_STORAGE_KEY = "donationData"; // 로컬 스토리지 키
+const SKELETON_DISPLAY_MIN_TIME = 600; // 최소 로딩 스켈레톤 표시 시간
+const ERROR_DISPLAY_DELAY = 600; // 에러 표시 지연 시간
+
+// 로컬 스토리지에서 데이터 불러오기
+const loadData = () => {
+ try {
+ const data = localStorage.getItem(LOCAL_STORAGE_KEY);
+ return data ? JSON.parse(data) : null; // 데이터가 존재하면 파싱하여 반환, 없으면 null
+ } catch (e) {
+ console.log("로컬 스토리에서 데이터를 불러오는데 실패했습니다.", e);
+ }
+};
+
+// 로컬 스토리지에 데이터 저장하기
+const saveData = (data) => {
+ try {
+ localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(data)); // 데이터를 문자열로 변환하여 저장
+ } catch (e) {
+ console.error("로컬 스토리지 저장 실패", e);
+ }
+};
+
+// 커스텀 훅: 후원 데이터를 가져오며 상태 관리 담당
+export const useDonations = () => {
+ const [donations, setDonations] = useState([]); // 후원 데이터 목록
+ const [loading, setLoading] = useState(true); // 로딩 상태
+ const [error, setError] = useState(false); // 에러 상태
+
+ // 로컬 스토리지에서 초기 데이터 불러오기
+ const loadInitialData = useCallback(() => {
+ // 함수가 매 렌더링마다 새로 만들어지는 것을 방지하기 위해 useCallback 사용
+ const stored = loadData();
+ if (stored) {
+ setDonations(stored); // 후원 목록 없데이트
+ setTimeout(() => setLoading(false), SKELETON_DISPLAY_MIN_TIME);
+ }
+ }, []);
+
+ useEffect(() => {
+ loadInitialData(); // 로컬 저장소 초기화
+
+ const getDonations = async () => {
+ try {
+ const response = await donationsAPI.getDonations(); // API 호출
+ if (response?.list) {
+ setDonations(response.list); // 받아온 후원 데이터 저장
+ saveData(response.list); // 로컬 스토리지에도 데이터 저장
+ setTimeout(() => setLoading(false), SKELETON_DISPLAY_MIN_TIME); // 최소 로딩 시간 후 로딩 종료
+ } else {
+ throw new Error("데이터가 존재하지 않습니다."); // 데이터가 없을 경우 에러 발생
+ }
+ } catch (e) {
+ console.error("API 호출 실패:", e);
+ setTimeout(() => {
+ setError(true); // 에러 표시
+ setLoading(false); // 로딩 종료
+ }, ERROR_DISPLAY_DELAY);
+ }
+ };
+
+ getDonations(); // API로 후원 데이터 가져오기
+ }, [loadInitialData]);
+
+ return { donations, loading, error };
+};
diff --git a/src/hooks/useIsMobile.jsx b/src/hooks/useIsMobile.jsx
new file mode 100644
index 0000000..64f1007
--- /dev/null
+++ b/src/hooks/useIsMobile.jsx
@@ -0,0 +1,18 @@
+// hooks/useIsMobile.js
+import { useEffect, useState } from "react";
+
+export default function useIsMobile() {
+ const [isMobile, setIsMobile] = useState(false);
+
+ useEffect(() => {
+ const handleResize = () => {
+ setIsMobile(window.innerWidth <= 425);
+ };
+
+ handleResize(); // 초기 실행
+ window.addEventListener("resize", handleResize);
+ return () => window.removeEventListener("resize", handleResize);
+ }, []);
+
+ return isMobile;
+}
diff --git a/src/layouts/DefaultLayout.jsx b/src/layouts/DefaultLayout.jsx
new file mode 100644
index 0000000..e1f9ab0
--- /dev/null
+++ b/src/layouts/DefaultLayout.jsx
@@ -0,0 +1,17 @@
+import { Outlet } from "react-router-dom";
+import Footer from "../components/Footer";
+import Header from "../components/Header";
+
+const Layout = () => {
+ return (
+ <>
+
+
+
+
+
+ >
+ );
+};
+
+export default Layout;
diff --git a/src/main.jsx b/src/main.jsx
new file mode 100644
index 0000000..59016d4
--- /dev/null
+++ b/src/main.jsx
@@ -0,0 +1,18 @@
+import { StrictMode } from 'react'
+import { createRoot } from 'react-dom/client'
+import { BrowserRouter } from 'react-router-dom'
+import App from './App.jsx'
+import ResetStyles from './styles/resetStyles.jsx'
+import CommonStyles from './styles/commonStyles.jsx'
+import LayoutStyles from './styles/layoutStyles.jsx'
+
+createRoot(document.getElementById('root')).render(
+
+
+
+
+
+
+
+ ,
+)
diff --git a/src/mocks/donations.js b/src/mocks/donations.js
new file mode 100644
index 0000000..69c336a
--- /dev/null
+++ b/src/mocks/donations.js
@@ -0,0 +1,78 @@
+export const DONATIONS = [
+ {
+ status: true,
+ deadline: "2025-04-21T00:00:00.000Z",
+ createdAt: "2025-04-15T09:53:35.947Z",
+ receivedDonations: 500000,
+ targetDonation: 1000000,
+ idolId: 0,
+ subtitle: "강남역 10번 출구",
+ title: "민지 생일 축하",
+ id: 0,
+ idol: {
+ totalVotes: 1000,
+ profilePicture: "/mocks/mock01.png",
+ group: "뉴진스",
+ gender: "female",
+ name: "민지",
+ id: 0,
+ },
+ },
+ {
+ status: true,
+ deadline: "2025-12-25T00:00:00.000Z",
+ createdAt: "2025-06-20T14:22:15.123Z",
+ receivedDonations: 2000000,
+ targetDonation: 2000000,
+ idolId: 1,
+ subtitle: "코엑스몰 메가박스 앞",
+ title: "원영이 20번째 생일",
+ id: 1,
+ idol: {
+ totalVotes: 800,
+ profilePicture: "/mocks/mock02.png",
+ group: "아이브",
+ gender: "female",
+ name: "장원영",
+ id: 1,
+ },
+ },
+ {
+ status: false,
+ deadline: "2025-08-31T00:00:00.000Z",
+ createdAt: "2025-05-01T10:15:45.789Z",
+ receivedDonations: 300000,
+ targetDonation: 1500000,
+ idolId: 2,
+ subtitle: "홍대입구역 9번 출구",
+ title: "채원 팬미팅 서포트",
+ id: 2,
+ idol: {
+ totalVotes: 750,
+ profilePicture: "/mocks/mock03.png",
+ group: "르세라핌",
+ gender: "female",
+ name: "채원",
+ id: 2,
+ },
+ },
+ {
+ status: true,
+ deadline: "2026-01-15T00:00:00.000Z",
+ createdAt: "2025-07-10T08:45:30.456Z",
+ receivedDonations: 900000,
+ targetDonation: 3000000,
+ idolId: 3,
+ subtitle: "잠실 롯데월드몰 광장",
+ title: "블랙핑크 제니 지하철 광고",
+ id: 3,
+ idol: {
+ totalVotes: 650,
+ profilePicture: "/mocks/mock04.png",
+ group: "블랙핑크",
+ gender: "female",
+ name: "제니",
+ id: 3,
+ },
+ },
+];
diff --git a/src/mocks/idols.js b/src/mocks/idols.js
new file mode 100644
index 0000000..3d11b56
--- /dev/null
+++ b/src/mocks/idols.js
@@ -0,0 +1,102 @@
+export const IDOLS = [
+ {
+ voteId: 1,
+ idol: {
+ totalVotes: 1000,
+ profilePicture: "/mocks/mock01.png",
+ group: "뉴진스",
+ gender: "female",
+ name: "민지",
+ id: 0,
+ },
+ },
+ {
+ voteId: 2,
+ idol: {
+ totalVotes: 800,
+ profilePicture: "/mocks/mock02.png",
+ group: "아이브",
+ gender: "female",
+ name: "장원영",
+ id: 1,
+ },
+ },
+ {
+ voteId: 3,
+ idol: {
+ totalVotes: 750,
+ profilePicture: "/mocks/mock03.png",
+ group: "르세라핌",
+ gender: "female",
+ name: "채원",
+ id: 2,
+ },
+ },
+ {
+ voteId: 4,
+ idol: {
+ totalVotes: 650,
+ profilePicture: "/mocks/mock04.png",
+ group: "블랙핑크",
+ gender: "female",
+ name: "제니",
+ id: 3,
+ },
+ },
+ // 추가
+ {
+ voteId: 5,
+ idol: {
+ totalVotes: 1000,
+ profilePicture: "/mocks/mock01.png",
+ group: "뉴진스",
+ gender: "female",
+ name: "민지",
+ id: 4,
+ },
+ },
+ {
+ voteId: 6,
+ idol: {
+ totalVotes: 1000,
+ profilePicture: "/mocks/mock01.png",
+ group: "뉴진스",
+ gender: "female",
+ name: "민지",
+ id: 5,
+ },
+ },
+ {
+ voteId: 7,
+ idol: {
+ totalVotes: 1000,
+ profilePicture: "/mocks/mock01.png",
+ group: "뉴진스",
+ gender: "female",
+ name: "민지",
+ id: 6,
+ },
+ },
+ {
+ voteId: 8,
+ idol: {
+ totalVotes: 1000,
+ profilePicture: "/mocks/mock01.png",
+ group: "뉴진스",
+ gender: "female",
+ name: "민지",
+ id: 7,
+ },
+ },
+ {
+ voteId: 9,
+ idol: {
+ totalVotes: 1000,
+ profilePicture: "/mocks/mock01.png",
+ group: "뉴진스",
+ gender: "female",
+ name: "민지",
+ id: 8,
+ },
+ },
+];
diff --git a/src/pages/DonationDetail/components/DonationDetailInfo.jsx b/src/pages/DonationDetail/components/DonationDetailInfo.jsx
new file mode 100644
index 0000000..29eb861
--- /dev/null
+++ b/src/pages/DonationDetail/components/DonationDetailInfo.jsx
@@ -0,0 +1,428 @@
+import { css } from "@emotion/react";
+import { useCallback, useEffect, useRef, useState } from "react";
+import { toast } from "react-toastify";
+import Modal from "../../../../src/components/Modal";
+import { donationsAPI } from "../../../apis/donationsAPI";
+import Button from "../../../components/Button/Button";
+import { useCredit } from "../../../context/CreditContext";
+import CreditRechargeModalContent from "../../List/Charge/components/CreditRechargeModalContent";
+import DonationDetailTimer from "./DonationDetailTimer";
+/** @jsxImportSource @emotion/react */
+
+export default function DonationDetailInfo({ donation, loading }) {
+ const { idol, receivedDonations, targetDonation, deadline, subtitle, title } =
+ donation;
+
+ const [credit, setCredit] = useState(0);
+ const myCredit = useCredit();
+ const [isModalOpen, setIsModalOpen] = useState(false);
+ const [isScrollDown, setIsScrollDown] = useState(false);
+ const [isError, setIsError] = useState(false);
+ const [donatedAmount, setDonatedAmount] = useState(receivedDonations);
+
+ const handleCredit = (label, value) => {
+ let newCredit;
+
+ if (label === "전액") {
+ newCredit = value;
+ } else {
+ newCredit = credit + value;
+ }
+
+ // 보유 크레딧보다 많은지 확인
+ const isOverLimit = newCredit > (myCredit.credit || 0);
+
+ // 에러 상태 업데이트
+ setIsError(isOverLimit);
+
+ // 크레딧 값 업데이트
+ setCredit(newCredit);
+
+ // 에러 메시지 표시 또는 최대값으로 제한
+ if (isOverLimit) {
+ toast.error("보유한 크레딧을 초과할 수 없습니다.");
+ setCredit(myCredit.credit || 0); // 최대값으로 제한
+ }
+ };
+
+ const creditList = [
+ {
+ label: "+100",
+ value: 100,
+ },
+ {
+ label: "+500",
+ value: 500,
+ },
+ {
+ label: "+1,000",
+ value: 1000,
+ },
+ {
+ label: "전액",
+ value: myCredit.credit,
+ },
+ ];
+
+ const openModal = () => {
+ setIsModalOpen(true);
+ };
+
+ const closeModal = () => {
+ setIsModalOpen(false);
+ };
+
+ const handleDonate = async () => {
+ if (credit === 0 || isError) return;
+
+ try {
+ // 후원 요청
+ await donationsAPI.contribute(donation.id, credit);
+ // 후원 금액 반영
+ myCredit.deductCredit(credit);
+ // 후원 금액 반영
+ setDonatedAmount((prev) => prev + credit); // 상태 업데이트
+ toast.success("후원에 성공하셨습니다!");
+ setCredit(0); //크레딧 초기화
+ } catch (error) {
+ alert(error.message);
+ }
+ };
+
+ // * window의 스크롤 위치 구하기,
+ // * 스크롤 위치가 400px 이상이면 isScrollDown을 true로 설정
+ useEffect(() => {
+ if (window.innerWidth <= 768) return;
+ const handleWindowScroll = () => {
+ const scrollTop =
+ window.pageYOffset || document.documentElement.scrollTop;
+ if (scrollTop > 400) {
+ setIsScrollDown(true);
+ } else {
+ setIsScrollDown(false);
+ }
+ };
+
+ window.addEventListener("scroll", handleWindowScroll);
+ return () => window.removeEventListener("scroll", handleWindowScroll);
+ }, []);
+
+ const inputOnChange = (e) => {
+ const value = e.target.value.replace(/[^0-9]/g, "");
+ const newValue = value === "" ? 0 : Number(value);
+
+ // 입력값이 보유 크레딧보다 크면 에러 상태로 설정
+ setIsError(newValue > myCredit.credit);
+ setCredit(newValue);
+ };
+ return (
+ <>
+
+
+
+
+ >
+ );
+}
+const DonationDetailInfoStyle = css`
+ width: 100%;
+ display: flex;
+ flex-direction: column;
+ height: 600px;
+ position: sticky;
+ overflow: hidden;
+ top: 120px;
+ padding: 30px 20px;
+ background-color: var(--black-02000E);
+ border: 1px solid var(--pink-FE5493);
+ border-radius: 10px;
+ .hideTop {
+ font-size: 20px;
+ color: #fff;
+ line-height: 1.4;
+ max-width: 100%;
+ overflow: hidden;
+ margin-bottom: 20px;
+ margin-top: calc((20px + 2.8em) * -1);
+ opacity: 0;
+ transition:
+ margin 0.3s ease,
+ opacity 0.3s ease;
+ p {
+ font-size: 18px;
+ display: block;
+ max-width: 100%;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+ &.isScrollDown {
+ margin-top: 0;
+ opacity: 1;
+ }
+ }
+
+ button {
+ height: 60px;
+ border-radius: 10px;
+ font-size: 18px;
+ font-weight: 500;
+ }
+ .ellipsis-text {
+ display: block;
+ width: 100%;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+ @media all and (max-width: 1300px) {
+ height: auto;
+ top: 80px;
+ }
+ @media all and (max-width: 768px) {
+ position: static;
+ padding: 3.91vw 2.6vw;
+ .hideTop {
+ margin-top: 0;
+ opacity: 1;
+ font-size: 3.13vw;
+ margin-bottom: 2.6vw;
+ p {
+ font-size: 2.6vw;
+ }
+ }
+ button {
+ height: 7.81vw;
+ font-size: 2.86vw;
+ border-radius: 1.3vw;
+ }
+ }
+ @media all and (max-width: 425px) {
+ .hideTop {
+ font-size: 4.71vw;
+ margin-bottom: 2.35vw;
+ p {
+ font-size: 3.76vw;
+ }
+ }
+ button {
+ height: 11.76vw;
+ font-size: 3.76vw;
+ }
+ }
+`;
+const DonationDetailInfoItem = css`
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+ font-size: 14px;
+ line-height: 1.4;
+ color: rgba(255, 255, 255, 0.7);
+ margin-bottom: 20px;
+ strong {
+ line-height: 1;
+ font-size: 30px;
+ color: #fff;
+ }
+ .input {
+ border-radius: 10px;
+ background: rgba(255, 255, 255, 0.1);
+ display: flex;
+ padding-inline: 24px 54px;
+ font-size: 18px;
+ font-weight: 500;
+ background-image: url('/icons/icon_credit.svg');
+ background-size: 24px;
+ background-position: right 24px center;
+ background-repeat: no-repeat;
+ input[type='text'] {
+ height: 60px;
+ flex: 1;
+ outline: none;
+ }
+ span {
+ display: none;
+ font-size: 12px;
+ color: rgba(255, 0, 0, 0.6);
+ font-weight: 500;
+ align-items: center;
+ }
+ &.isError {
+ span {
+ display: flex;
+ }
+ }
+ }
+ ul {
+ display: grid;
+ grid-template-columns: repeat(4, 1fr);
+ gap: 10px;
+ li {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ button {
+ width: 100%;
+ height: 34px;
+ border-radius: 2em;
+ background: rgba(255, 0, 191, 0.1);
+ font-size: 14px;
+ color: rgba(255, 255, 255, 0.5);
+ transition: color 0.3s ease;
+ }
+ &:hover {
+ button {
+ color: #fff;
+ }
+ }
+ }
+ }
+ .myCredit {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ button {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ width: auto;
+ padding-inline: 14px;
+ height: 30px;
+ border-radius: 2em;
+ background: rgba(255, 0, 191, 0.2);
+ font-size: 12px;
+ transition: background 0.3s ease;
+ &:hover {
+ background: rgba(255, 0, 191, 0.3);
+ }
+ }
+ }
+ &.isCredit {
+ margin-bottom: auto;
+ }
+ @media all and (max-width: 768px) {
+ gap: 1.3vw;
+ font-size: 2.08vw;
+ margin-bottom: 2.6vw;
+ strong {
+ font-size: 3.91vw;
+ }
+ .input {
+ border-radius: 1.3vw;
+ padding-inline: 3.13vw 8.03vw;
+ font-size: 2.34vw;
+ background-size: 3.13vw;
+ background-position: right 3.13vw center;
+ input[type='text'] {
+ height: 7.81vw;
+ min-width: 0;
+ width: 100%;
+ }
+ }
+ &.isCredit {
+ margin-bottom: 3.91vw;
+ }
+ }
+ @media all and (max-width: 425px) {
+ font-size: 3.29vw;
+ margin-bottom: 5.65vw;
+ gap: 1.88vw;
+ strong {
+ font-size: 5.18vw;
+ }
+ ul {
+ gap: 2.35vw;
+ li {
+ button {
+ height: 7.53vw;
+ font-size: 3.29vw;
+ }
+ }
+ }
+ .input {
+ padding-inline: 3.13vw 9.41vw;
+ font-size: 3.76vw;
+ background-size: 5.65vw;
+ background-position: right 3.29vw center;
+ input[type='text'] {
+ height: 11.76vw;
+ }
+ }
+ &.isCredit {
+ margin-bottom: 5.65vw;
+ }
+ .myCredit {
+ button {
+ height: 6.59vw;
+ font-size: 2.82vw;
+ }
+ }
+ }
+`;
diff --git a/src/pages/DonationDetail/components/DonationDetailText.jsx b/src/pages/DonationDetail/components/DonationDetailText.jsx
new file mode 100644
index 0000000..d5f6a34
--- /dev/null
+++ b/src/pages/DonationDetail/components/DonationDetailText.jsx
@@ -0,0 +1,162 @@
+import { css } from "@emotion/react";
+import withPostPosition from "../../../utils/postPosition";
+
+/** @jsxImportSource @emotion/react */
+export default function DonationDetail({ donation, loading }) {
+ const { idol, targetDonation, deadline, subtitle, title } = donation;
+
+ const idolWithGa = idol ? withPostPosition(idol.name, "이가") : "";
+ const idolWithEun = idol ? withPostPosition(idol.name, "은는") : "";
+ const donationWithEul = donation
+ ? withPostPosition(donation.subtitle, "을를")
+ : "";
+ return (
+
+ {loading ? (
+
로딩중...
+ ) : (
+ <>
+
+
+ ✨💖 역.대.급. 사건 발생!
+ {idol.name}({idol.group}) 데뷔 1주년, 합정역에 {idol.name} 강림
+ 💖✨
+
+
+ {idolWithGa} 뭐다? 존재 자체가 명절임 😭
+
+ 이건 그냥… 🎉 국가공휴일 지정 가야 돼 🎉
+ 🕊️ {idolWithGa} 강림하던 그 순간 세상의 조도는 조절 당했고 우리
+ 눈은 그녀만을 트래킹하기 시작했다 👁️💫
+
+ 그날 이후 우린 알게 되었죠. 🧠 "아… {idolWithEun} 그냥
+ 아이돌이 아니라 종교다…"
+
+
+ 🪩무대 위에선 카리스마 풀충전
+ 🐣팬들 앞에선 애교 떡칠
+
+ 갭차이 보고 진짜 의자에서 슬라이드했잖아요… 의자야 미안해 😭
+
+
+
+
+ 🧨 이렇게 존예로운 날 그냥 넘길 수 있냐고! 🎉 우린{" "}
+ {idol.name}의 1주년을 제대로, 작정하고, 뽝! 터지게 축하할 거예요
+
+
+ 📍{donationWithEul} {idol.name}존으로 물들인다? → ㄹㅇ 가능 📺
+ 초대형 디지털 광고
+ + 팬들의 한땀한땀 축하 메시지! 👀 지나가는 사람들 전부
+
+ “누구세요...? 저 사람 왜 이렇게 예뻐요...?” 자동 입덕 예약 👑
+
+
+
+ 📢후원정보 대방출🔥
+
+ 🔹 목표 금액 : {targetDonation.toLocaleString()}원
+ 🔹 후원 기간 : ~ {deadline.split("T")[0]}
+ 🔹 후원 메시지 : {idol.name} 존예해요!
+
+
+
+ 💝후원 특전도 FLEX 간다!
+
+ 📸 광고 인증샷—"이 광고 내가 만들었잖아" 가능 🎀 한정판{" "}
+ {idol.name}
+ 굿즈—포토카드, 슬로건 등 쏜다! 📜 후원자 명단 광고에 박제
+ (닉네임 숨기고 싶은 사람 미리 말해줘요!)
+
+
+
+ ⚡{idol.name}의 1년 = 우리가 만든 기적 ⚡
+
+ 단순한 시간이 아니라 🌟{idol.name}라는 이름 아래, 우리가
+ 함께한 서사집🌟
+
+ 이 아름다운 서포트… 함께해주실 거죠?
+ 💖진짜 {idol.name}좋아하면 손모으고 후원하러 가자💖
+
+
+
+ #{idol.name}
+ #{idol.name}_굿즈
+ #{idol.group}
+ #{idol.group}_조공
+
+ #{subtitle}_{idol.name}
+
+ #{subtitle}
+ #{title}
+
+ >
+ )}
+
+ );
+}
+
+const DonationDetailTextStyle = css`
+ margin-top: 100px;
+ dl {
+ font-size: 18px;
+ line-height: 1.8;
+ margin-bottom: 30px;
+ }
+ dt {
+ font-size: 22px;
+ margin-bottom: 10px;
+ }
+ ul {
+ display: flex;
+ gap: 10px;
+ flex-wrap: wrap;
+ margin-bottom: 30px;
+ li {
+ font-size: 16px;
+ padding: 5px 10px;
+ border-radius: 5px;
+ border: 1px solid rgba(255, 255, 255, 0.7);
+ color: #fff;
+ }
+ }
+ @media all and (max-width: 768px) {
+ margin-top: calc(84vw + 50px);
+ dl {
+ font-size: 2.34vw;
+ margin-bottom: 3.91vw;
+ }
+ dt {
+ font-size: 2.86vw;
+ margin-bottom: 1.3vw;
+ }
+ ul {
+ gap: 1.3vw;
+ margin-bottom: 3.91vw;
+ li {
+ font-size: 2.08vw;
+ padding: 0.65vw 1.3vw;
+ border-radius: 0.65vw;
+ }
+ }
+ }
+ @media all and (max-width: 425px) {
+ margin-top: 125.65vw;
+ dl {
+ font-size: 3.53vw;
+ margin-bottom: 9.41vw;
+ }
+ dt {
+ font-size: 4.24vw;
+ margin-bottom: 1.88vw;
+ }
+ ul {
+ gap: 2.35vw;
+ li {
+ font-size: 2.82vw;
+ padding: 1.3vw 2.6vw;
+ border-radius: 1.3vw;
+ }
+ }
+ }
+`;
diff --git a/src/pages/DonationDetail/components/DonationDetailTimer.jsx b/src/pages/DonationDetail/components/DonationDetailTimer.jsx
new file mode 100644
index 0000000..ea36a9f
--- /dev/null
+++ b/src/pages/DonationDetail/components/DonationDetailTimer.jsx
@@ -0,0 +1,68 @@
+import { css } from "@emotion/react";
+import { useCallback, useEffect, useState } from "react";
+
+/** @jsxImportSource @emotion/react */
+
+export default function DonationDetailTimer({ deadline }) {
+ // *** 모집 기간 계산, ***
+ // * 모집 기간이 지나면 후원 버튼 비활성화,
+ // * 남은 시간 카운트다운
+ const calculateTimeLeft = useCallback(() => {
+ // deadline에서 현재시간 빼기
+ const difference = +new Date(deadline) - +new Date();
+ let timeLeft = {};
+
+ if (difference > 0) {
+ timeLeft = {
+ days: Math.floor(difference / (1000 * 60 * 60 * 24)),
+ hours: Math.floor((difference / (1000 * 60 * 60)) % 24),
+ minutes: Math.floor((difference / 1000 / 60) % 60),
+ seconds: Math.floor((difference / 1000) % 60),
+ };
+ }
+ return timeLeft;
+ }, [deadline]);
+
+ const [timeLeft, setTimeLeft] = useState(calculateTimeLeft());
+ useEffect(() => {
+ // 초기 타임 설정
+ setTimeLeft(calculateTimeLeft());
+
+ // 1초마다 업데이트
+ const timer = setInterval(() => {
+ setTimeLeft(calculateTimeLeft());
+ }, 1000);
+
+ // 컴포넌트 언마운트 시 타이머 정리
+ return () => clearInterval(timer);
+ }, [calculateTimeLeft]);
+
+ // 시간 계산 결과가 0보다 크면 배열로 변환
+ const timeLeftArray = Object.entries(timeLeft).map(([key, value]) => ({
+ label: key,
+ value: ["hours", "minutes", "seconds"].includes(key)
+ ? value.toString().padStart(2, "0")
+ : value,
+ }));
+ return (
+ <>
+ 남은 시간
+
+ {timeLeftArray[0].value} Days
+ {timeLeftArray[1].value} :
+ {timeLeftArray[2].value} :
+ {timeLeftArray[3].value}
+
+
+ 모집 기간 : {deadline.split("T")[0]}
+
+ >
+ );
+}
+
+const DonationDetailTimerStyle = css`
+ strong:first-of-type {
+ font-size: 24px;
+ color: rgba(255, 255, 255, 0.8);
+ }
+`;
diff --git a/src/pages/DonationDetail/index.jsx b/src/pages/DonationDetail/index.jsx
new file mode 100644
index 0000000..797d467
--- /dev/null
+++ b/src/pages/DonationDetail/index.jsx
@@ -0,0 +1,212 @@
+import { css } from "@emotion/react";
+import { useCallback, useEffect, useState } from "react";
+import { useParams } from "react-router-dom";
+import { donationsAPI } from "../../apis/donationsAPI";
+import LoadingError from "../../components/Error";
+import DonationDetailInfo from "./components/DonationDetailInfo";
+import DonationDetailText from "./components/DonationDetailText";
+/** @jsxImportSource @emotion/react */
+
+export default function DonationDetail() {
+ const { id } = useParams();
+ const [donation, setDonation] = useState(null);
+ const [loading, setLoading] = useState(true);
+ const [randomEmoji, setRandomEmoji] = useState("");
+ const [error, setError] = useState(false);
+
+ const getDonation = useCallback(async () => {
+ try {
+ const response = await donationsAPI.getDonations();
+ if (response) {
+ const foundDonation = response.list.find(
+ (item) => item.id === Number.parseInt(id) || item.id === id,
+ );
+ setDonation(foundDonation || null);
+ }
+ } catch (error) {
+ // 로딩 UI 잠시 유지
+ setTimeout(() => {
+ setError(true);
+ }, 600); // 600초 정도 기다렸다가 에러 표시
+ } finally {
+ setLoading(false);
+ }
+ }, [id]);
+
+ useEffect(() => {
+ getDonation();
+ }, [getDonation]);
+
+ // 랜덤 이모지 - 초기 렌더링에서만 이모지를 선택
+ useEffect(() => {
+ const emojiKeys = Object.keys(emojis);
+ const selectedEmoji =
+ emojis[emojiKeys[Math.floor(Math.random() * emojiKeys.length)]];
+ setRandomEmoji(selectedEmoji);
+ }, []);
+
+ const emojis = {
+ heart: "💖",
+ star: "⭐",
+ sparkle: "✨",
+ fire: "🔥",
+ sparkles: "🎆",
+ party: "🎉",
+ gift: "💝",
+ ribbon: "🎀",
+ };
+
+ return (
+
+ {loading ? (
+
로딩 중...
+ ) : error ? (
+
+ ) : (
+ <>
+
+
+ {randomEmoji}
+ {donation.idol.name} ({donation.idol.group})
+ {randomEmoji}
+
+
+ {donation.subtitle} - {donation.title}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ >
+ )}
+
+ );
+}
+
+const DonationDetailStyle = css`
+ padding-block: 80px;
+ color: #fff;
+ &::after {
+ content: '';
+ display: block;
+ position: fixed;
+ bottom: 0;
+ left: 0;
+ width: 100%;
+ height: 20%;
+ background: linear-gradient(180deg, rgba(0, 0, 0, 0) 58.9%, #000 100%);
+ }
+ @media all and (max-width: 1300px) {
+ padding-block: 60px;
+ &::after {
+ height: 80px;
+ }
+ }
+ @media all and (max-width: 425px) {
+ padding-block: 9.41vw;
+ }
+
+`;
+
+const DonationDetailTop = css`
+ text-align: center;
+ h2 {
+ font-size: 48px;
+ line-height: 1;
+ font-weight: bold;
+ margin-bottom: 30px;
+ }
+ p {
+ font-weight: bold;
+ font-size: 30px;
+ line-height: 1.2;
+ }
+ @media all and (max-width: 1300px) {
+ h2 {
+ font-size: 38px;
+ }
+ p {
+ font-size: 24px;
+ }
+ }
+ @media all and (max-width: 425px) {
+ h2 {
+ font-size: 7.06vw;
+ margin-bottom: 4.71vw;
+ }
+ p {
+ font-size: 5.18vw;
+ }
+ }
+`;
+
+const DonationDetailContent = css`
+ position: relative;
+ height: 100%;
+ margin-top: 100px;
+ display: flex;
+ justify-content: space-between;
+ @media all and (max-width: 1300px) {
+ justify-content: center;
+ gap: 40px;
+ margin-top: 60px;
+ }
+ @media all and (max-width: 768px) {
+ flex-direction: column;
+ gap: 0;
+ }
+ @media all and (max-width: 425px) {
+ margin-top: 9.41vw;
+ }
+`;
+
+const DonationDetailContentArea = css`
+ width: 600px;
+ flex: none;
+ .profile {
+ width: 100%;
+ height: 600px;
+ border-radius: 10px;
+ overflow: hidden;
+ img {
+ width: 100%;
+ }
+ }
+ @media all and (max-width: 1300px) {
+ width: auto;
+ flex: 1;
+ .profile {
+ height: auto;
+ aspect-ratio: 1/1;
+ }
+ }
+`;
+
+const DonationDetailInfoArea = css`
+ /* flex: 1; */
+ flex: none;
+ width: 500px;
+ @media all and (max-width: 1300px) {
+ width: 40%;
+ }
+ @media all and (max-width: 768px) {
+ width: 100%;
+ position: absolute;
+ top: 100vw;
+ left: 0;
+ }
+ @media all and (max-width: 425px) {
+ top: 94vw;
+ }
+`;
diff --git a/src/pages/Landing/components/LandingBottomSection.jsx b/src/pages/Landing/components/LandingBottomSection.jsx
new file mode 100644
index 0000000..9b9af42
--- /dev/null
+++ b/src/pages/Landing/components/LandingBottomSection.jsx
@@ -0,0 +1,249 @@
+import styled from "@emotion/styled";
+import { useEffect } from "react";
+import { useNavigate } from "react-router-dom";
+import { infiniteCarousel } from "../../../utils/infiniteCarousel";
+
+const CarouselCards = ({ className, startIndex }) => {
+ const images = Array.from({ length: 4 }, (_, i) => ({
+ src: `/images/landing/landing_card${String(startIndex + i).padStart(2, "0")}.png`,
+ alt: "랜딩 페이지 아이돌 이미지",
+ }));
+
+ return (
+
+ {images.map((image) => (
+ // image src를 키값으로 사용
+
+
+
+ ))}
+
+ );
+};
+
+export default function LandingBottomSection() {
+ const navigate = useNavigate();
+ // LandingBottomSection 캐러셀 초기화 및 cleanup
+ useEffect(() => {
+ const carousel1 = infiniteCarousel({
+ trigger: ".carousel01",
+ duration: 8,
+ reverse: false,
+ pauseOnHover: false,
+ direction: "vertical",
+ });
+
+ const carousel2 = infiniteCarousel({
+ trigger: ".carousel02",
+ duration: 8,
+ reverse: true,
+ pauseOnHover: false,
+ direction: "vertical",
+ });
+
+ // cleanup 함수 반환
+ return () => {
+ carousel1();
+ carousel2();
+ };
+ }, []);
+
+ return (
+
+
+
+
+ 진심을 담은 응원,
+
+ 이제는 더 쉬운 방법으로 시작해 보세요.
+
+
+
navigate("/list")}>
+ 크레딧 받고 시작하기
+
+
+
+
+ );
+}
+
+const StyledLandingBottomSection = styled.section`
+ overflow: hidden;
+ &::before {
+ position: absolute;
+ content: '';
+ width: 100%;
+ height: 80px;
+ top: 0;
+ left: 0;
+ z-index: 3;
+ background: linear-gradient(180deg, #03000e 0%, rgba(2, 0, 14, 0) 100%);
+ }
+ &::after {
+ position: absolute;
+ content: '';
+ width: 100%;
+ height: 80px;
+ bottom: 0;
+ left: 0;
+ z-index: 2;
+ background: linear-gradient(0deg, #02000e 0%, rgba(2, 0, 14, 0) 100%);
+ }
+ .landingGrid {
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ position: relative;
+ z-index: 2;
+ p {
+ color: #fff;
+ text-align: center;
+ font-size: 26px;
+ font-weight: 600;
+ line-height: 1.5;
+ }
+ > img {
+ display: block;
+ margin-top: 20px;
+ height: 150px;
+ }
+ button {
+ cursor: pointer;
+ margin-top: 100px;
+ width: 220px;
+ height: 60px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ border-radius: 10px;
+ border: 2px solid rgba(255, 255, 255, 0.5);
+ background-color: transparent;
+ color: #fff;
+ font-size: 16px;
+ font-weight: 700;
+ transition: all 0.3s ease-in-out;
+ position: relative;
+ z-index: 2;
+ line-height: 1;
+ &:after {
+ content: '';
+ width: 100%;
+ height: 100%;
+ position: absolute;
+ top: 0;
+ left: 0;
+ background: linear-gradient(90deg, #f86f65 0%, #fe5493 50%, #f86f65 100%, #fe5493 150%, #f86f65 200%);
+ background-size: 200% 100%;
+ animation: gradientMove 2s infinite;
+ border-radius: 10px;
+ z-index: -1;
+ transition: all 0.3s ease-in-out;
+ opacity: 0;
+ }
+ &:hover {
+ border-color: transparent;
+ &:after {
+ opacity: 1;
+ }
+ }
+ }
+ }
+ .carousel {
+ width: 500px !important;
+ height: 200vh;
+ position: absolute !important;
+ bottom: 0 !important;
+ display: flex;
+ flex-direction: column;
+ z-index: -1;
+ &.carousel01 {
+ left: 0;
+ align-items: flex-start;
+ }
+ &.carousel02 {
+ right: 0;
+ align-items: flex-end;
+ }
+ .item {
+ padding-block: 42px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 400px;
+ height: 400px;
+ }
+ }
+ @media all and (max-width: 1024px) {
+ &::before {
+ height: 60px;
+ }
+ &::after {
+ height: 60px;
+ }
+ .landingGrid {
+ .carousel {
+ .item {
+ padding-block: 20px;
+ width: 200px;
+ height: 250px;
+ img {
+ max-width: 100%;
+ max-height: 100%;
+ }
+ }
+ }
+ > img {
+ height: auto;
+ width: 80vw;
+ }
+ p {
+ font-size: 20px;
+ }
+ }
+ .carousel {
+ width: 40vw !important;
+ }
+ }
+ @media all and (max-width: 425px) {
+ height: 133.33vw !important;
+ .landingGrid {
+ > img {
+ width: 65.07vw;
+ margin-top: 5.33vw;
+ }
+ p {
+ font-size: 4.27vw;
+ }
+ .carousel {
+ top: -20vh !important;
+ width: 34.67vw !important;
+ height: 300vh !important;
+ bottom: auto !important;
+ &.carousel01 {
+ left: -13.33vw;
+ }
+ &.carousel02 {
+ right: -13.33vw;
+ }
+ .item {
+ padding-block: 4.27vw;
+ width: 100%;
+ height: 42.67vw;
+ }
+ }
+ button {
+ margin-top: 16vw;
+ width: 42.67vw;
+ height: 12.8vw;
+ border-radius: 2.67vw;
+ font-size: 3.2vw;
+ border: none;
+ &::after {
+ opacity: 1;
+ }
+ }
+ }
+ }
+`;
diff --git a/src/pages/Landing/components/LandingFooter.jsx b/src/pages/Landing/components/LandingFooter.jsx
new file mode 100644
index 0000000..9bc1e87
--- /dev/null
+++ b/src/pages/Landing/components/LandingFooter.jsx
@@ -0,0 +1,115 @@
+import styled from "@emotion/styled";
+import { useEffect, useRef } from "react";
+
+export default function LandingFooter() {
+ const btnRef = useRef(null);
+
+ useEffect(() => {
+ btnRef.current?.addEventListener("click", () => {
+ window.scrollTo({ top: 0, behavior: "smooth" });
+ });
+
+ return () => {
+ btnRef.current?.removeEventListener("click");
+ };
+ }, []);
+
+ return (
+
+
+
+
+
All your K-pop love, in one place
+
+
+
+
+
+
© FandomJingyu.2025 All rights reserved.
+
+
+
+ );
+}
+
+const StyledLandingFooter = styled.div`
+ padding-block: 52px;
+ .landingGrid {
+ display: flex;
+ justify-content: space-between;
+ > div {
+ display: flex;
+ flex-direction: column;
+ p {
+ color: #666;
+ font-size: 18px;
+ font-weight: 400;
+ line-height: 1.5;
+ }
+ }
+ }
+ .footer-logo {
+ gap: 20px;
+ align-items: flex-start;
+ img {
+ height: 30px;
+ }
+ }
+ .footer-copy {
+ justify-content: space-between;
+ align-items: flex-end;
+ }
+ .top-btn {
+ width: 35px;
+ height: 35px;
+ background-color: transparent;
+ border: none;
+ padding: 0;
+ cursor: pointer;
+ img {
+ width: 100%;
+ }
+ }
+
+ @media all and (max-width: 768px) {
+ padding-block: 30px;
+ .landingGrid {
+ flex-direction: column;
+ justify-content: flex-start;
+ > div {
+ p {
+ font-size: 16px;
+ }
+ }
+ .footer-copy {
+ justify-content: flex-start;
+ align-items: flex-start;
+ }
+ }
+ .top-btn {
+ position: absolute;
+ top: 0;
+ right: 0;
+ }
+ }
+ @media all and (max-width: 425px) {
+ padding-block: 6.13vw;
+ .landingGrid {
+ > div {
+ p {
+ font-size: 3.2vw;
+ }
+ }
+ .footer-logo {
+ gap: 5.33vw;
+ img {
+ width: 31.47vw;
+ }
+ }
+ }
+ .top-btn {
+ width: 8.53vw;
+ height: 8.53vw;
+ }
+ }
+`;
diff --git a/src/pages/Landing/components/LandingHeader.jsx b/src/pages/Landing/components/LandingHeader.jsx
new file mode 100644
index 0000000..930a976
--- /dev/null
+++ b/src/pages/Landing/components/LandingHeader.jsx
@@ -0,0 +1,95 @@
+import styled from "@emotion/styled";
+import { useCallback, useEffect, useRef } from "react";
+import { useNavigate } from "react-router-dom";
+export default function LandingHeader() {
+ const headerRef = useRef(null);
+ const prevScrollPosRef = useRef(0);
+ const navigate = useNavigate();
+ const handleScroll = useCallback(() => {
+ const currentScrollPos = window.scrollY;
+
+ if (headerRef.current) {
+ if (prevScrollPosRef.current > currentScrollPos) {
+ headerRef.current.style.transform = "translateY(0)";
+ } else {
+ headerRef.current.style.transform = "translateY(-100%)";
+ }
+ }
+
+ prevScrollPosRef.current = currentScrollPos;
+ }, []);
+
+ useEffect(() => {
+ window.addEventListener("scroll", handleScroll);
+
+ return () => {
+ window.removeEventListener("scroll", handleScroll);
+ };
+ }, [handleScroll]);
+
+ return (
+
+
+
+
+
+
navigate("/list")}
+ >
+ 지금 시작하기
+
+
+
+ );
+}
+
+const StyledLandingHeader = styled.div`
+ background-color: rgba(0, 0, 0, 0.8);
+ height: 100px;
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ z-index: 100;
+ transition: transform 0.3s ease-in-out;
+ .landingGrid {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ height: 100%;
+ }
+ .landingBtn {
+ height: 48px;
+ width: 160px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background: linear-gradient(90deg, #f86f65 0%, #fe5493 100%);
+ border-radius: 10px;
+ font-size: 16px;
+ font-weight: 700;
+ color: #fff;
+ }
+ @media all and (max-width: 768px) {
+ h1 {
+ width: 120px;
+ img {
+ width: 100%;
+ }
+ }
+ }
+ @media all and (max-width: 425px) {
+ height: 16vw;
+ h1 {
+ width: 26.67vw;
+ }
+ .landingBtn {
+ height: 9.6vw;
+ width: 26.67vw;
+ font-size: 3.2vw;
+ border-radius: 2.67vw;
+ }
+ }
+`;
diff --git a/src/pages/Landing/components/LandingMiddleSection.jsx b/src/pages/Landing/components/LandingMiddleSection.jsx
new file mode 100644
index 0000000..3057d7d
--- /dev/null
+++ b/src/pages/Landing/components/LandingMiddleSection.jsx
@@ -0,0 +1,428 @@
+import styled from "@emotion/styled";
+import { gsap } from "gsap";
+import { ScrollTrigger } from "gsap/ScrollTrigger";
+import { useEffect } from "react";
+
+export default function LandingMiddleSection() {
+ // LandingMiddleSection 애니메이션
+ useEffect(() => {
+ const ctx = gsap.context(() => {
+ const anim = gsap.timeline({
+ scrollTrigger: {
+ trigger: ".landingMiddleSection",
+ start: "top top",
+ end: "+=300%",
+ pin: true,
+ scrub: 1,
+ id: "sec02Trigger",
+ },
+ });
+
+ gsap.set(".landingMiddleSection article:nth-of-type(2)", {
+ y: "100%",
+ });
+ gsap.set(".landingMiddleSection article:nth-of-type(3)", {
+ y: "100%",
+ });
+
+ // 네비게이션 초기 설정
+ const navButtons = document.querySelectorAll(
+ ".landingMiddleSectionNav button",
+ );
+ navButtons[0].classList.add("active");
+
+ // 네비게이션 활성화
+ const st = ScrollTrigger.create({
+ trigger: ".landingMiddleSection",
+ start: "top top",
+ end: "+=300%",
+ onUpdate: (self) => {
+ const progress = self.progress;
+ for (const btn of navButtons) {
+ btn.classList.remove("active");
+ }
+
+ if (progress < 0.33) {
+ navButtons[0].classList.add("active");
+ } else if (progress < 0.66) {
+ navButtons[1].classList.add("active");
+ } else {
+ navButtons[2].classList.add("active");
+ }
+ },
+ });
+
+ // 네비게이션 클릭 이벤트
+ navButtons.forEach((btn, index) => {
+ btn.addEventListener("click", () => {
+ const targetProgress = index * 0.5;
+ const targetScroll = st.start + (st.end - st.start) * targetProgress;
+
+ gsap.to(window, {
+ scrollTo: targetScroll,
+ duration: 1,
+ ease: "power2.inOut",
+ });
+ });
+ });
+
+ anim
+ .to(".landingMiddleSection article:nth-of-type(2)", {
+ y: 0,
+ delay: 0.5,
+ duration: 1,
+ ease: "power1.inOut",
+ })
+ .to(".landingMiddleSection article:nth-of-type(3)", {
+ y: 0,
+ duration: 1,
+ ease: "power1.inOut",
+ })
+ .to(".landingMiddleSection", {
+ delay: 0.2,
+ });
+ });
+
+ return () => ctx.revert();
+ }, []);
+
+ return (
+
+
+
+
+
+
+
+
+
+ 마음 이
+ 닿는 순간,
+ 응원 이
+ 시작돼요
+
+
+
+
+
+
+
+
+ 좋아하는 아티스트에게 직접 후원하고
+
+ 팬심을 행동으로 보여주세요.
+
+ 투명한 후원 내역과 간편한 참여로
+
+ 진짜 응원을 이어갑니다.
+
+
+
+
+
+
+ 이번 달
+ 가장 빛난 별 은
+ 누구?
+
+
+
+
+
+
+
+
+ 팬들의 사랑을 가장 많이 받은
+
+ 이달의 아티스트를 소개합니다!
+
+ 당신의 크레딧 응원으로 아티스트를
+
+ 가장 빛난 별로 만들어주세요.
+
+
+
+
+
+
+ 내 마음 속
+
+
+
+
+ 나만의 아티스트
+ 소식까지
+
+ 빠르게
+
+
+
+
+
+
+
+
+ 내가 좋아하는 아티스트 소식만 골라보고
+ 새로운 콘텐츠와 스케줄도 한눈에!
+
+ 나만의 맞춤 피드로 더 똑똑하게,
+ 더 깊이 있게 팬 활동을 즐기세요.
+
+
+
+
+ );
+}
+
+const StyledLandingMiddleSection = styled.section`
+ position: relative;
+
+ article {
+ background-color: #02000e;
+ height: 100%;
+ width: 100%;
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ &:nth-of-type(2) {
+ background-color: #09061d;
+ }
+ }
+ .landingGrid {
+ padding-inline: 180px;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ height: 100%;
+ h3 {
+ color: #fff;
+ font-size: 65px;
+ line-height: 1.5;
+ letter-spacing: -1.3px;
+ span {
+ display: inline-block;
+ margin-left: 10px;
+ width: 72px;
+ height: 72px;
+ position: relative;
+ img {
+ width: 100%;
+ }
+ &.is-floating {
+ animation: icon-float 1.5s ease-in-out infinite;
+ }
+ &.is-sparkling {
+ animation: icon-sparkle 1.5s ease-in-out infinite;
+ }
+ &.is-pulsing {
+ animation: icon-pulse 1s ease-in-out infinite;
+ }
+ }
+ }
+ p {
+ width: 420px;
+ color: #aaa;
+ font-size: 26px;
+ font-weight: 400;
+ line-height: 1.5;
+ }
+ }
+ .landing-mockup {
+ width: 390px;
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ img {
+ max-width: 100%;
+ max-height: 70vh;
+ }
+ }
+ .landingMiddleSectionNav {
+ display: flex;
+ flex-direction: column;
+ position: absolute;
+ top: 50%;
+ right: calc(50% - 900px);
+ transform: translateY(-50%);
+ z-index: 10;
+ gap: 8px;
+ button {
+ display: block;
+ cursor: pointer;
+ width: 10px;
+ height: 10px;
+ border-radius: 50%;
+ border: 1px solid rgba(249, 109, 105, 0.502);
+ transition: all 0.3s ease-in-out;
+ background-color: transparent;
+ padding: 0;
+ &.active {
+ background-color: #f96d69;
+ border-color: #f96d69;
+ }
+ }
+ }
+ @keyframes icon-float {
+ 0% {
+ transform: translateY(0);
+ }
+ 50% {
+ transform: translateY(-30px);
+ }
+ 100% {
+ transform: translateY(0);
+ }
+ }
+ @keyframes icon-sparkle {
+ 0% {
+ opacity: 0.2;
+ }
+ 20% {
+ opacity: 0.9;
+ }
+ 45% {
+ opacity: 0.5;
+ }
+ 70% {
+ opacity: 1;
+ }
+ 100% {
+ opacity: 0.2;
+ }
+ }
+ @keyframes icon-pulse {
+ 0% {
+ transform: scale(1);
+ }
+ 50% {
+ transform: scale(1.3);
+ }
+ 100% {
+ transform: scale(1);
+ }
+ }
+ @keyframes gradientMove {
+ 0% {
+ background-position: 0% 0%;
+ }
+ 100% {
+ background-position: -200% 0%;
+ }
+ }
+ @media all and (max-width: 1880px) {
+ .landingMiddleSectionNav {
+ display: none;
+ }
+ .landingGrid {
+ padding-inline: 80px;
+ p {
+ font-size: 24px;
+ width: 350px;
+ }
+ h3 {
+ font-size: 50px;
+ }
+ }
+ }
+ @media all and (max-width: 1300px) {
+ .landingGrid {
+ padding-inline: 20px;
+ p {
+ width: 300px;
+ font-size: 20px;
+ }
+ h3 {
+ font-size: 42px;
+ span {
+ width: 50px;
+ height: 50px;
+ }
+ }
+ }
+ .landing-mockup {
+ width: 20vw;
+ }
+ }
+ @media all and (max-width: 1024px) {
+ .landingGrid {
+ padding-inline: 0;
+ justify-content: center;
+ gap: 30vw;
+ p {
+ font-size: 18px;
+ }
+ h3 {
+ width: 300px;
+ font-size: 36px;
+ }
+ }
+ }
+ @media all and (max-width: 768px) {
+ .landingGrid {
+ gap: 20px;
+ flex-direction: column;
+ justify-content: flex-start;
+ align-items: flex-start;
+ padding-top: 100px;
+ h3 {
+ font-size: 32px;
+ width: auto;
+ span {
+ width: 42px;
+ height: 42px;
+ }
+ }
+ p {
+ width: auto;
+ }
+ }
+ .landing-mockup {
+ width: 40vw;
+ top: 70%;
+ img {
+ max-height: calc(65vh - 100px);
+ }
+ }
+ }
+ @media all and (max-width: 425px) {
+ span.show-425 {
+ display: inline-block !important;
+ }
+ .landingGrid {
+ padding-top: 21.33vw;
+ gap: 5.33vw;
+ h3 {
+ font-size: 8.53vw;
+ line-height: 1.5;
+ span {
+ width: 8.53vw;
+ height: 8.53vw;
+ }
+ }
+ p {
+ font-size: 4.27vw;
+ }
+ }
+ .landing-mockup {
+ width: 56vw;
+ top: 88.53vw;
+ transform: translate(-50%, 0);
+ }
+ @keyframes icon-float {
+ 0% {
+ transform: translateY(0);
+ }
+ 50% {
+ transform: translateY(-2.67vw);
+ }
+ 100% {
+ transform: translateY(0);
+ }
+ }
+ }
+`;
diff --git a/src/pages/Landing/components/LandingTopSection.jsx b/src/pages/Landing/components/LandingTopSection.jsx
new file mode 100644
index 0000000..a84985b
--- /dev/null
+++ b/src/pages/Landing/components/LandingTopSection.jsx
@@ -0,0 +1,270 @@
+import styled from "@emotion/styled";
+import { gsap } from "gsap";
+import { useEffect } from "react";
+
+export default function LandingTopSection() {
+ useEffect(() => {
+ // 랜딩 직후 fade 애니메이션
+ const fadeInCtx = gsap.context(() => {
+ gsap.set(".landingTopSection", {
+ autoAlpha: 0,
+ });
+
+ gsap.to(".landingTopSection", {
+ autoAlpha: 1,
+ duration: 1,
+ delay: 0.3,
+ });
+ });
+ // LandingTopSection 카드 애니메이션
+ const cardAnimCtx = gsap.context(() => {
+ const checkResponsive = (pc, pc1300) => {
+ if (window.innerWidth <= 1300) {
+ return pc1300;
+ }
+ return pc;
+ };
+ const anim = gsap.timeline({
+ scrollTrigger: {
+ trigger: ".landingTopSection",
+ start: "top top",
+ end: "+=200%",
+ pin: true,
+ scrub: 1,
+ },
+ });
+
+ gsap.set(".landingTopSectionCards li", {
+ autoAlpha: 0,
+ y: 100,
+ });
+
+ anim
+ .to(
+ ".landingTopSection h2",
+ {
+ gap: () => checkResponsive(400, 0),
+ duration: 1,
+ },
+ 0,
+ )
+ .to(
+ ".landingTopSectionDesc",
+ {
+ autoAlpha: 0,
+ duration: 1,
+ },
+ 0,
+ )
+ .to(".landingTopSectionCards .item01", {
+ autoAlpha: 1,
+ y: 0,
+ duration: 1,
+ })
+ .to(".landingTopSectionCards .item02", {
+ autoAlpha: 1,
+ y: 0,
+ duration: 1,
+ })
+ .to(".landingTopSectionCards .item03", {
+ autoAlpha: 1,
+ y: 0,
+ duration: 1,
+ })
+ .to(".landingTopSection", {
+ delay: 0.5,
+ });
+ });
+ return () => {
+ fadeInCtx.revert();
+ cardAnimCtx.revert();
+ };
+ }, []);
+
+ return (
+
+
+
+
+
+
+
+
+
+ 좋아하는 만큼
+
+
+ 덕질 은 쉽게
+
+
+
+ 아티스트에게 내 마음을 전하고 싶을 때,
+
+ 가장 먼저 떠오르는 방법이 되어드릴게요.
+
+
+
+ );
+}
+
+const StyledLandingTopSection = styled.section`
+ height: 100vh;
+ .blur {
+ position: absolute;
+ width: 800px;
+ height: 800px;
+ z-index: 1;
+ &.blur01 {
+ top: 100px;
+ left: -400px;
+ }
+ &.blur02 {
+ bottom: 100px;
+ right: -400px;
+ }
+ img {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+ }
+ }
+ .landingGrid {
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ position: relative;
+ > p {
+ margin-top: 100px;
+ color: #888;
+ text-align: center;
+ font-size: 26px;
+ font-weight: 400;
+ line-height: 1.5;
+ height: 0;
+ overflow: visible;
+ }
+ }
+ h2 {
+ width: 100%;
+ color: #fff;
+ text-align: center;
+ font-size: 100px;
+ font-weight: 700;
+ line-height: 1.5;
+ display: flex;
+ justify-content: center;
+ gap: 24px;
+ p {
+ span {
+ color: #f96d69;
+ }
+ }
+ }
+ .landingTopSectionCards {
+ position: absolute;
+ width: 380px;
+ height: 460px;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ z-index: 10;
+ margin-left: 46px;
+ li {
+ position: absolute;
+ width: 300px;
+ height: 380px;
+ img {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+ }
+ &.item02 {
+ margin-top: 40px;
+ margin-left: 40px;
+ }
+ &.item03 {
+ margin-top: 80px;
+ margin-left: 80px;
+ }
+ }
+ }
+
+ @media all and (max-width: 1300px) {
+ h2 {
+ font-size: 60px;
+ line-height: 1.2;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 0;
+ }
+ .landingTopSectionCards {
+ margin-left: 0;
+ width: 300px;
+ top: 65vh;
+ li.item02 {
+ margin-left: 0;
+ }
+ li.item03 {
+ margin-left: 0;
+ }
+ }
+ .landingGrid {
+ padding-top: 100px;
+ justify-content: flex-start;
+ > p {
+ margin-top: 40px;
+ font-size: 20px;
+ }
+ }
+ }
+ @media all and (max-width: 768px) {
+ .blur {
+ display: none;
+ }
+ h2 {
+ font-size: 48px;
+ }
+ }
+ @media all and (max-width: 425px) {
+ h2 {
+ font-size: 11.87vw;
+ line-height: 1.4;
+ }
+ .landingGrid {
+ padding-top: 30.33vw;
+ > p {
+ margin-top: 21.33vw;
+ font-size: 4.27vw;
+ }
+ }
+ .landingTopSectionCards {
+ width: 69.33vw;
+ height: 107.3vw;
+ top: 74.27vw;
+ transform: translate(-50%, 0);
+ li {
+ width: 69.33vw;
+ height: 88vw;
+ }
+ li.item02 {
+ margin-top: 9.6vw;
+ }
+ li.item03 {
+ margin-top: 19.2vw;
+ }
+ }
+ }
+`;
diff --git a/src/pages/Landing/index.jsx b/src/pages/Landing/index.jsx
new file mode 100644
index 0000000..52f6f1a
--- /dev/null
+++ b/src/pages/Landing/index.jsx
@@ -0,0 +1,56 @@
+import styled from "@emotion/styled";
+import Lenis from "@studio-freight/lenis";
+import { gsap } from "gsap";
+import { ScrollTrigger } from "gsap/ScrollTrigger";
+import { useEffect, useRef } from "react";
+import LandingBottomSection from "./components/LandingBottomSection";
+import LandingFooter from "./components/LandingFooter";
+import LandingHeader from "./components/LandingHeader";
+import LandingMiddleSection from "./components/LandingMiddleSection";
+import LandingTopSection from "./components/LandingTopSection";
+
+const Landing = () => {
+ const lenisRef = useRef(null);
+
+ // Lenis 스크롤 초기화
+ useEffect(() => {
+ lenisRef.current = new Lenis({
+ smoothWheel: true,
+ duration: 1.2,
+ });
+
+ lenisRef.current.on("scroll", ScrollTrigger.update);
+
+ const animate = (time) => {
+ lenisRef.current?.raf(time * 1000);
+ };
+
+ gsap.ticker.add(animate);
+ gsap.ticker.lagSmoothing(0);
+
+ return () => {
+ gsap.ticker.remove(animate);
+ lenisRef.current?.destroy();
+ };
+ }, []);
+
+ return (
+
+
+
+
+
+
+
+ );
+};
+
+export default Landing;
+
+const LandingContainer = styled.div`
+ overflow: hidden;
+ section {
+ height: 100vh;
+ position: relative;
+ }
+`;
diff --git a/src/pages/List/Charge/components/CreditCharge.jsx b/src/pages/List/Charge/components/CreditCharge.jsx
new file mode 100644
index 0000000..18a5868
--- /dev/null
+++ b/src/pages/List/Charge/components/CreditCharge.jsx
@@ -0,0 +1,210 @@
+import Modal from "@/components/Modal";
+import { useCredit } from "@/context/CreditContext";
+import { css } from "@emotion/react";
+import gsap from "gsap";
+import { useCallback, useEffect, useRef, useState } from "react";
+import CreditRechargeModalContent from "./CreditRechargeModalContent";
+/** @jsxImportSource @emotion/react */
+
+export default function CreditCharge() {
+ const { credit } = useCredit();
+ const creditRef = useRef(null);
+ const animationRef = useRef(null);
+ const previousCreditRef = useRef(0);
+ const isInitialMount = useRef(true);
+ const [isModalOpen, setIsModalOpen] = useState(false);
+
+ const openModal = () => {
+ setIsModalOpen(true);
+ };
+
+ const closeModal = () => {
+ setIsModalOpen(false);
+ };
+
+ // 크레딧 카운트업 애니메이션 함수
+ const countCredit = useCallback((startValue, endValue) => {
+ // 이전 애니메이션이 실행 중이라면 중단
+ if (animationRef.current) {
+ animationRef.current.kill();
+ }
+
+ // GSAP 초기값으로 사용
+ const counter = {
+ credit: startValue,
+ };
+
+ // 애니메이션 중 state를 건드리지 않고 DOM만 업데이트
+ const updateDisplay = () => {
+ if (creditRef.current) {
+ creditRef.current.textContent = Math.floor(
+ counter.credit,
+ ).toLocaleString();
+ }
+ };
+
+ animationRef.current = gsap.to(counter, {
+ credit: endValue,
+ duration: 2,
+ ease: "power3.out",
+ onUpdate: updateDisplay, // 매 프레임마다 실행될 함수
+ });
+ }, []);
+
+ useEffect(() => {
+ // credit이 0이 아닌 정수일 때 동작하게 처리
+ if (typeof credit === "number") {
+ if (isInitialMount.current) {
+ // 새로고침 혹은 처음 마운트 시
+ countCredit(0, credit);
+ previousCreditRef.current = credit;
+ isInitialMount.current = false;
+ } else {
+ // 그 이후 credit 변경 시
+ countCredit(previousCreditRef.current, credit);
+ previousCreditRef.current = credit;
+ }
+ }
+ }, [countCredit, credit]);
+ return (
+ <>
+
+
+
내 크레딧
+
+
+
{credit.toLocaleString()}
+
+
+
+ 충전하기
+
+
+
+ {/* 모달 내부에 선택된 크레딧 값을 업데이트할 함수 전달 */}
+
+
+
+ >
+ );
+}
+
+// 스타일 컴포넌트 정의
+const CreditChargeStyle = css`
+ z-index: 2;
+ background-color: var(--black-02000E);
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding-inline: 78px;
+ height: 130px;
+ border: 1px solid rgba(241, 238, 249, 0.8);
+ border-radius: 8px;
+ > div {
+ display: flex;
+ flex-direction: column;
+ gap: 14px;
+ }
+ p {
+ color: rgba(255, 255, 255, 0.6);
+ font-size: 16px;
+ font-weight: 400;
+ }
+ .credit {
+ display: flex;
+ align-items: center;
+ gap: 4px;
+ span {
+ color: rgba(255, 255, 255, 0.87);
+ font-size: 24px;
+ font-weight: 700;
+ line-height: 1;
+ }
+ }
+ button {
+ display: flex;
+ align-items: center;
+ gap: 4px;
+ color: var(--orange-F96D69);
+ font-size: 20px;
+ font-weight: 700;
+ letter-spacing: 0.8px;
+ margin-right: -24px;
+ transition: margin-right 0.3s ease-in-out;
+ img {
+ width: 24px;
+ opacity: 0;
+ transition: opacity 0.2s ease-in-out;
+ }
+ &:hover {
+ margin-right: 0;
+ img {
+ opacity: 1;
+ animation: rotate 0.6s 0.2s linear 1;
+ @keyframes rotate {
+ from {
+ transform: perspective(100px) rotateY(0deg);
+ }
+ to {
+ transform: perspective(100px) rotateY(360deg);
+ }
+ }
+ }
+ }
+ }
+ @media all and (max-width: 768px) {
+ padding-inline: 3.13vw;
+ height: 17.06vw;
+ button {
+ margin-right: 0;
+ font-size: 2.34vw;
+ img {
+ display: none;
+ }
+ }
+ > div {
+ gap: 1.82vw;
+ }
+ p {
+ font-size: 2.34vw;
+ }
+ .credit {
+ gap: 0.52vw;
+ span {
+ font-size: 3.13vw;
+ }
+ img {
+ width: 2.6vw;
+ }
+ }
+ }
+ @media all and (max-width: 425px) {
+ padding-inline: 4.71vw;
+ height: 20.47vw;
+ button {
+ margin-right: 0;
+ font-size: 3.76vw;
+ img {
+ display: none;
+ }
+ }
+ > div {
+ gap: 1.88vw;
+ }
+ p {
+ font-size: 3.06vw;
+ }
+ .credit {
+ gap: 0.94vw;
+ span {
+ font-size: 4.71vw;
+ }
+ img {
+ width: 4.71vw;
+ }
+ }
+ }
+`;
diff --git a/src/pages/List/Charge/components/CreditRechargeModalContent.jsx b/src/pages/List/Charge/components/CreditRechargeModalContent.jsx
new file mode 100644
index 0000000..d621114
--- /dev/null
+++ b/src/pages/List/Charge/components/CreditRechargeModalContent.jsx
@@ -0,0 +1,69 @@
+import Button from "@/components/Button/Button";
+import RadioButton from "@/components/RadioButton";
+import { useCredit } from "@/context/CreditContext";
+// src/components/Modal/Modal.styles.js
+/** @jsxImportSource @emotion/react */
+import { useState } from "react";
+import { toast } from "react-toastify";
+import creditIcon from "/icons/icon_credit.svg";
+import {
+ RadioStyles,
+ buttonSpacing,
+ radioContentStyles,
+} from "../styles/CreditRechargeModalContent.styles";
+
+export default function CreditRechargeModalContent({ myCredit, closeModal }) {
+ const credits = [100, 500, 1000]; // 라디오 버튼 test를 위한 크레딧 배열
+ const [select, setSelect] = useState(null); // 라디오 버튼 test를 위한 state설정 (현재 선택된 값을 저장하는 state)
+ const { addCredit } = useCredit(); // 크래딧 충전하는 값을 더하는 훅
+
+ // 제출 시 선택된 값 부모 컴포넌트로 전달
+ const handleSubmit = (e) => {
+ e.preventDefault();
+ if (select !== null) {
+ // 선택된 크레딧 값 처리 로직 (로컬스토리지 저장 등)
+ addCredit(select);
+ closeModal(); // 모달 닫기
+ }
+ };
+
+ // 라디오 버튼의 선택 상태를 설정하는 함수
+ const handleSelect = (value) => {
+ setSelect(value);
+ };
+
+ return (
+
+ );
+}
diff --git a/src/pages/List/Charge/styles/CreditRechargeModalContent.styles.js b/src/pages/List/Charge/styles/CreditRechargeModalContent.styles.js
new file mode 100644
index 0000000..1f091ef
--- /dev/null
+++ b/src/pages/List/Charge/styles/CreditRechargeModalContent.styles.js
@@ -0,0 +1,28 @@
+// CreditRechargeModalContent.styles.js
+import { css } from "@emotion/react";
+
+// 각각의 라디오 버튼 컨테이너
+export const RadioStyles = css`
+ margin-bottom: 8px;
+ background: var(--black-black_02000E, #02000E);
+`;
+
+// 라디오 버튼 + 텍스트 정렬
+export const radioContentStyles = css`
+ display: flex;
+ align-items: center;
+ gap: 4px;
+ font-size: 20px;
+ font-weight: 700;
+ color: var(--white-F7F7F8);
+
+ img {
+ width: 16px;
+ height: 16px;
+ }
+`;
+
+// 버튼 위 마진
+export const buttonSpacing = css`
+ margin-top: 24px;
+`;
diff --git a/src/pages/List/Chart/Chart.styles.js b/src/pages/List/Chart/Chart.styles.js
new file mode 100644
index 0000000..bddd562
--- /dev/null
+++ b/src/pages/List/Chart/Chart.styles.js
@@ -0,0 +1,131 @@
+import styled from "@emotion/styled";
+
+export const ChartContainer = styled.div`
+ max-width:1200px;
+ margin: 0 auto;
+ padding: 40px 0;
+ width: clamp(325px, 90vw, 1200px);
+`;
+
+export const ChartHeaderWrap = styled.div`
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 24px;
+`;
+
+export const ChartTitle = styled.h2`
+ color: white;
+ font-size: 24px;
+ font-weight: 700;
+ font-family: Pretendard;
+`;
+
+export const ChartButtonWrap = styled.div``;
+
+export const ChartIdol = styled.div`
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ height: 42px;
+ font-size: 14px;
+ color: white;
+`;
+
+const ChartIdolBase = styled.div`
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-family: Pretendard;
+ font-weight: 400;
+ line-height: 18px;
+ letter-spacing: -0.17px;
+ height: 42px;
+`;
+
+export const ChartIdolLeft = styled(ChartIdolBase)`
+
+
+`;
+
+export const ChartIdolRight = styled(ChartIdolBase)`
+
+`;
+
+export const ChartList = styled.div`
+ display: flex;
+ flex-wrap: wrap;
+ gap: 16px;
+ margin-top: 24px;
+
+ @media (min-width: 769px) {
+ flex-direction: row;
+ & > li {
+ width: calc(50% - 8px);
+ }
+ }
+
+ @media (max-width: 768px) {
+ flex-direction: column;
+ & > li {
+ width: 100%;
+ }
+ }
+`;
+
+export const ListItem = styled.li`
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding-bottom: 8px;
+ border-bottom: 1px solid #ffffff1a;
+`;
+
+export const ProfileInfo = styled.div`
+ display: flex;
+ align-items: center;
+`;
+
+export const RankAndName = styled.div`
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ margin-left: 16px;
+ color: white;
+
+ .rank {
+ color: #f96d69;
+ font-weight: 400;
+ font-size: 16px;
+ opacity: 0.57;
+ }
+
+ .group {
+ font-weight: 400;
+ font-size: 16px;
+ font-family: Pretendard;
+ }
+
+ .artist-name {
+ font-weight: 500;
+ font-size: 16px;
+ font-family: Pretendard;
+ }
+`;
+
+export const Votes = styled.span`
+ color: rgba(255, 255, 255, 0.6);
+ font-family: Pretendard;
+`;
+
+export const MoreButton = styled.div`
+ display: flex;
+ justify-content: center;
+ margin-top: 40px;
+`;
+
+export const VoteChart = styled.div`
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ gap: 4px;
+`;
diff --git a/src/pages/List/Chart/components/ChartVoteModal/Skeleton/VoteIdolSkeleton.styles.js b/src/pages/List/Chart/components/ChartVoteModal/Skeleton/VoteIdolSkeleton.styles.js
new file mode 100644
index 0000000..ca46e35
--- /dev/null
+++ b/src/pages/List/Chart/components/ChartVoteModal/Skeleton/VoteIdolSkeleton.styles.js
@@ -0,0 +1,40 @@
+// VoteIdolSkeleton.styles.js.
+/** @jsxImportSource @emotion/react */
+import { css } from "@emotion/react";
+import { shimmerStyle } from "../../../../../../styles/skeletonAnimation";
+
+export const SkeletonCircle = css`
+ width: 70px;
+ height: 70px;
+ border-radius: 50%;
+ ${shimmerStyle}
+`;
+
+export const SkeletonInfo = css`
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+`;
+
+export const SkeletonRankAndName = css`
+ display: flex;
+ justify-content: space-between;
+`;
+
+export const SkeletonRank = css`
+ width: 40px;
+ height: 10px;
+ ${shimmerStyle}
+`;
+
+export const SkeletonName = css`
+ width: 100px;
+ height: 10px;
+ ${shimmerStyle}
+`;
+
+export const SkeletonVotes = css`
+ width: 80px;
+ height: 10px;
+ ${shimmerStyle}
+`;
diff --git a/src/pages/List/Chart/components/ChartVoteModal/Skeleton/index.jsx b/src/pages/List/Chart/components/ChartVoteModal/Skeleton/index.jsx
new file mode 100644
index 0000000..cb1c22f
--- /dev/null
+++ b/src/pages/List/Chart/components/ChartVoteModal/Skeleton/index.jsx
@@ -0,0 +1,38 @@
+/** @jsxImportSource @emotion/react */
+import { v4 as uuidv4 } from "uuid";
+import useIsMobile from "../../../../../../hooks/useIsMobile";
+import { IdolItem, IdolList, RadioContent } from "../VoteIdolList.styles";
+import {
+ SkeletonCircle,
+ SkeletonInfo,
+ SkeletonName,
+ SkeletonRank,
+ SkeletonRankAndName,
+ SkeletonVotes,
+} from "./VoteIdolSkeleton.styles"; // 아이돌 리스트 스타일을 import
+
+export default function VoteIdolSkeleton({ count = 6 }) {
+ const isMobile = useIsMobile();
+ const skeletonCount = isMobile ? 9 : count; // 모바일이면 9개, 아니면 기본값 6개
+
+ return (
+
+ {Array.from({ length: skeletonCount }).map(() => (
+
+ {" "}
+ {/* uuid로 고유한 key 부여 */}
+
+
+ ))}
+
+ );
+}
diff --git a/src/pages/List/Chart/components/ChartVoteModal/VoteButton.jsx b/src/pages/List/Chart/components/ChartVoteModal/VoteButton.jsx
new file mode 100644
index 0000000..24696a9
--- /dev/null
+++ b/src/pages/List/Chart/components/ChartVoteModal/VoteButton.jsx
@@ -0,0 +1,25 @@
+/** @jsxImportSource @emotion/react */
+import Button from "../../../../../components/Button/Button";
+import useIsMobile from "../../../../../hooks/useIsMobile";
+import { CreditInfo, centerAlignStyle } from "./VoteButton.styles";
+
+export default function VoteButton({ onSubmit }) {
+ const isMobile = useIsMobile();
+ const buttonSize = isMobile ? "vote-md" : "vote-lg";
+
+ return (
+
+
+ 투표하기
+
+
+ 투표하는 데 1000 크레딧 이 소모됩니다.
+
+
+ );
+}
diff --git a/src/pages/List/Chart/components/ChartVoteModal/VoteButton.styles.js b/src/pages/List/Chart/components/ChartVoteModal/VoteButton.styles.js
new file mode 100644
index 0000000..60dc4c4
--- /dev/null
+++ b/src/pages/List/Chart/components/ChartVoteModal/VoteButton.styles.js
@@ -0,0 +1,33 @@
+import { css } from "@emotion/react";
+
+export const centerAlignStyle = css`
+ overflow-x: hidden;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ text-align: center; /* 텍스트 중앙 정렬 */
+
+
+ @media (max-width: 425px) {
+ position: fixed;
+ bottom: 0;
+ left: 0;
+ width: 100%;
+ padding: 3.76vw 5.65vw 3.29vw 5.65vw;
+ background: rgba(2, 0, 14, 0.8);
+ z-index: 10;
+ }
+`;
+
+export const CreditInfo = css`
+ color: #FFFFFF;
+ font-size: 12px;
+ font-weight: 500;
+ line-height: 26px;
+ margin-top: 8px;
+
+ span {
+ color: var(--orange-F96D69);
+ }
+`;
diff --git a/src/pages/List/Chart/components/ChartVoteModal/VoteIdolList.jsx b/src/pages/List/Chart/components/ChartVoteModal/VoteIdolList.jsx
new file mode 100644
index 0000000..4e3bfc4
--- /dev/null
+++ b/src/pages/List/Chart/components/ChartVoteModal/VoteIdolList.jsx
@@ -0,0 +1,64 @@
+import CheckIdol from "../../../../../components/CheckIdol";
+/** @jsxImportSource @emotion/react */
+import Circle from "../../../../../components/Circle";
+import RadioButton from "../../../../../components/RadioButton";
+import {
+ IdolDetails,
+ IdolInfo,
+ IdolItem,
+ IdolList,
+ IdolName,
+ IdolVote,
+ RadioContent,
+ Rank,
+} from "./VoteIdolList.styles";
+
+// 천 단위 구분 기호 추가 함수
+const formatVotes = (votes) => {
+ return new Intl.NumberFormat().format(votes); // 천 단위 구분 기호 추가
+};
+
+export default function VoteIdolList({ idols, selectedIdolId, onSelectIdol }) {
+ return (
+ <>
+
+ {idols.map((idol, index) => (
+
+ onSelectIdol(idol.id)}
+ className="vote"
+ >
+
+
+
+
+
+
{index + 1}
+
+
+ {idol.group}
+ {idol.name}
+
+
{formatVotes(idol.totalVotes)}표
+
+
+
+
+
+ ))}
+
+ >
+ );
+}
diff --git a/src/pages/List/Chart/components/ChartVoteModal/VoteIdolList.styles.js b/src/pages/List/Chart/components/ChartVoteModal/VoteIdolList.styles.js
new file mode 100644
index 0000000..d947cbf
--- /dev/null
+++ b/src/pages/List/Chart/components/ChartVoteModal/VoteIdolList.styles.js
@@ -0,0 +1,98 @@
+import { css } from "@emotion/react";
+
+export const IdolList = css`
+ max-height: 522px; /* 6개가 보일 수 있게 높이 고정 (예시) */
+ overflow-y: auto;
+ overflow-x: hidden;
+ margin-bottom: 20px;
+
+ /* 웹킷 기반 브라우저용 스크롤바 스타일 */
+ &::-webkit-scrollbar {
+ width: 3px;
+ }
+
+ &::-webkit-scrollbar-thumb {
+ background-color: rgba(249, 109, 105, 0.5); /* 반투명한 오렌지 색 (50% 투명도) */
+ border-radius: 3px;
+ }
+
+ &::-webkit-scrollbar-track {
+ background-color: transparent;
+ }
+
+
+ @media (max-width: 425px) {
+ max-height: calc(100vh - 17.88vw); /* 버튼 영역 + 여유 */
+ padding-bottom: 24vw; /* 리스트 아이템 마지막 여백 */
+ }
+
+`;
+
+export const IdolItem = css`
+ align-items: center;
+ height: 70px;
+ margin-bottom: 17px;
+
+ /* 밑줄 스타일 */
+ &::after {
+ content: "";
+ display: block;
+ width: 100%;
+ height: 1px;
+ background: rgba(255, 255, 255, 0.1);
+ align-self: stretch;
+ margin: 11px 0;
+ }
+`;
+
+export const RadioContent = css`
+ position: relative; /* ✅ CheckIdol 기준점 */
+ display: flex;
+ align-items: center;
+ gap: 12px;
+
+`;
+
+export const IdolInfo = css`
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ gap: 12px;
+
+
+`;
+
+export const Rank = css`
+ color: var(--orange-F96D69);
+ font-size: 14px;
+ font-weight: 400;
+ font-style: normal;
+ line-height: normal;
+`;
+
+export const IdolName = css`
+ color: #FFFFFF;
+ font-size: 14px;
+ font-weight: 500;
+ font-style: normal;
+ line-height: normal;
+
+ display: flex;
+ gap: 4px;
+`;
+
+export const IdolDetails = css`
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+
+`;
+
+export const IdolVote = css`
+ color: rgba(255, 255, 255, 0.60);
+ font-size: 14px;
+ font-weight: 400;
+ font-style: normal;
+ line-height: normal;
+ text-align: left;
+`;
diff --git a/src/pages/List/Chart/components/ChartVoteModal/index.jsx b/src/pages/List/Chart/components/ChartVoteModal/index.jsx
new file mode 100644
index 0000000..21c5865
--- /dev/null
+++ b/src/pages/List/Chart/components/ChartVoteModal/index.jsx
@@ -0,0 +1,46 @@
+/** @jsxImportSource @emotion/react */
+import { css } from "@emotion/react";
+import LoadingError from "../../../../../components/Error";
+import { useChartVoteModal } from "../../../../../hooks/useChartVoteModal";
+import VoteIdolSkeleton from "./Skeleton";
+import VoteButton from "./VoteButton";
+import VoteIdolList from "./VoteIdolList";
+
+export default function ChartVoteModal({ gender, closeModal }) {
+ const {
+ idols,
+ selectedIdolId,
+ handleVote,
+ handleIdolSelect,
+ loading,
+ error,
+ } = useChartVoteModal(gender, closeModal);
+
+ return (
+
+ );
+}
+
+const VoteFormStyles = css`
+ max-height: 100vh;
+ overflow-y: hidden;
+ position: relative;
+ @media (max-width: 425px) {
+ height: 100vh;
+ }
+`;
diff --git a/src/pages/List/Chart/components/IdolProfileModal.jsx b/src/pages/List/Chart/components/IdolProfileModal.jsx
new file mode 100644
index 0000000..02a91c6
--- /dev/null
+++ b/src/pages/List/Chart/components/IdolProfileModal.jsx
@@ -0,0 +1,97 @@
+/** @jsxImportSource @emotion/react */
+import React from "react";
+import Circle from "../../../../components/Circle";
+import Modal from "../../../../components/Modal";
+import {
+ CircleContainer,
+ CloseButton,
+ DetailLabel,
+ DetailValue,
+ EnglishName,
+ GroupLogo,
+ Logo,
+ Profile,
+ ProfileDetail,
+ ProfileHeader,
+ ProfileInfo,
+ ProfileName,
+ ProfileWrapper,
+ Role,
+ Separator,
+ SubInfo,
+ VoteCount,
+ VoteLabel,
+ VoteSection,
+} from "./IdolProfileModal.styles";
+
+const IdolProfileModal = ({ idol, onClose }) => {
+ if (!idol) return null;
+
+ return (
+
+
+
+
+
+
+
+
+
+ {idol.name}
+
+
+
+ {idol.englishName}
+ ·
+ 가수
+
+
+
+
+
+
+
+
+ 출생:
+ {idol.birth || "-"}
+ 고향:
+ {idol.hometown || "-"}
+ 취미:
+ {idol.hobby || "-"}
+ MBTI:
+ {idol.mbti || "-"}
+ 그룹:
+ {idol.group}
+ 소속사:
+ {idol.agency || "-"}
+
+
+
+
+ 투표수
+ {idol.totalVotes?.toLocaleString() || "0"}
+
+
+
+ {idol.instagram && (
+
+
+
+ )}
+ {idol.fancam && (
+
+
+
+ )}
+
+
+
+
+ );
+};
+
+export default IdolProfileModal;
diff --git a/src/pages/List/Chart/components/IdolProfileModal.styles.js b/src/pages/List/Chart/components/IdolProfileModal.styles.js
new file mode 100644
index 0000000..15f6612
--- /dev/null
+++ b/src/pages/List/Chart/components/IdolProfileModal.styles.js
@@ -0,0 +1,180 @@
+import styled from "@emotion/styled";
+
+export const Profile = styled.div`
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ max-width: 400px;
+ height: 540px;
+ background-color:#02000e;
+ margin: 0 auto;
+ position: fixed;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ z-index: 1001;
+ box-shadow: 0 0 15px rgba(0, 0, 0, 0.5);
+ width: clamp(325px, 90vw, 1200px);
+
+
+`;
+
+export const ModalOverlay = styled.div`
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100vw;
+ height: 100vh;
+ background: rgba(0, 0, 0, 0.6);
+ z-index: 1000;
+`;
+
+export const ProfileWrapper = styled.div`
+ width: 450px;
+ height: 90%;
+ border: 1px solid #e36b6b;
+ background-color: #0b0b13;
+ border-radius: 15px;
+`;
+
+export const ProfileHeader = styled.div`
+ width: 100%;
+ height: 120px;
+ padding:40px 20px 0;
+ display: flex;
+ flex-direction: column;
+ justify-content: flex-start;
+`;
+
+export const ProfileName = styled.div`
+ font-size: 45px;
+ width: 100%;
+ height: 50px;
+ font-weight: 700;
+ color: #e59fa0;
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ justify-content: space-between;
+`;
+
+export const GroupLogo = styled.img`
+ width: 100px;
+ height: auto;
+`;
+
+export const SubInfo = styled.div`
+ font-size: 16px;
+ margin-top: 4px;
+ display: flex;
+ align-items: center;
+ gap: 6px;
+`;
+
+export const EnglishName = styled.span`
+ color: #e59fa0;
+ font-weight: 600;
+ font-size: 20px;
+`;
+
+export const Separator = styled.span`
+ color: #e59fa0;
+`;
+
+export const Role = styled.span`
+ color: #aaa;
+ font-weight: 400;
+ font-size: 20px;
+`;
+
+export const ProfileInfo = styled.div`
+ display: flex;
+ align-items: center;
+ padding-left: 20px;
+ gap:12px;
+ padding-top:20px;
+
+`;
+
+export const CircleContainer = styled.div`
+ width :180px;
+ `;
+
+export const ProfileDetail = styled.div`
+ display: grid;
+ grid-template-columns: 1fr 2fr;
+ row-gap: 8px;
+ column-gap:8px;
+ color: #e59fa0;
+ font-size: 16px;
+
+`;
+
+export const DetailLabel = styled.div`
+ font-weight: 600;
+`;
+
+export const DetailValue = styled.div`
+ color: #e0e0e0;
+`;
+
+export const VoteSection = styled.div`
+ width:90%;
+ border-top: 1px solid #e36b6b;
+ margin: 28px auto 0;
+ padding: 16px 20px 0 20px;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+`;
+
+export const VoteLabel = styled.span`
+ color: #e59fa0;
+ font-size: 20px;
+ font-weight: 700;
+
+`;
+
+export const VoteCount = styled.span`
+ color: #f3f3f3;
+ font-size: 24px;
+ font-weight: 700;
+`;
+
+export const StyledVoteButton = styled.div`
+ display: flex;
+ justify-content: center;
+ margin-top: 20px;
+ color:white;
+`;
+
+export const Logo = styled.div`
+ width:150px;
+ height:200px;
+ display: flex;
+ gap: 4px;
+ padding-left: 15px;
+ margin-top:10px;
+
+
+ img {
+ width: 120px;
+ height: 80px;
+ object-fit: contain;
+ }
+`;
+export const CloseButton = styled.button`
+ position: absolute;
+ top: 40px;
+ right: 12px;
+ background: transparent;
+ border: none;
+ cursor: pointer;
+ padding: 0;
+ z-index: 10;
+
+ img {
+ width: 24px;
+ height: 24px;
+ }
+`;
diff --git a/src/pages/List/Chart/components/IdolProfiles.js b/src/pages/List/Chart/components/IdolProfiles.js
new file mode 100644
index 0000000..8b19de4
--- /dev/null
+++ b/src/pages/List/Chart/components/IdolProfiles.js
@@ -0,0 +1,698 @@
+export const idolProfiles = {
+ 장원영: {
+ englishName: "JANG WONYOUNG",
+ birth: "2004",
+ hometown: "서울 서초구",
+ hobby: "필라테스",
+ mbti: "ISFP",
+ group: "IVE",
+ agency: "STARSHIP",
+ groupLogo: "/images/STARSHIP.png",
+ instagram: "https://www.instagram.com/ivestarship/",
+ fancam: "https://www.youtube.com/watch?v=4BeckMw9vao",
+ },
+ 안유진: {
+ englishName: "AN YUJIN",
+ birth: "2003",
+ hometown: "대전",
+ hobby: "사진 찍기",
+ mbti: "ENFP",
+ group: "IVE",
+ agency: "STARSHIP",
+ groupLogo: "/images/STARSHIP.png",
+ instagram: "https://www.instagram.com/ivestarship/",
+ fancam: "https://www.youtube.com/watch?v=Vk4urpcsntk",
+ },
+ 리즈: {
+ englishName: "Liz",
+ birth: "2007",
+ hometown: "경기도",
+ hobby: "음악 감상",
+ mbti: "ISFP",
+ group: "IVE",
+ agency: "STARSHIP",
+ groupLogo: "/images/STARSHIP.png",
+ instagram: "https://www.instagram.com/ivestarship/",
+ fancam: "https://www.youtube.com/watch?v=h7OmJZS7sxY",
+ },
+ 레이: {
+ englishName: "REI",
+ birth: "2004",
+ hometown: "일본",
+ hobby: "독서",
+ mbti: "INFJ",
+ group: "IVE",
+ agency: "STARSHIP",
+ groupLogo: "/images/STARSHIP.png",
+ instagram: "https://www.instagram.com/ivestarship/",
+ fancam: "https://www.youtube.com/watch?v=mKLLdYSl3Lk",
+ },
+ 이서: {
+ englishName: "LEESEO",
+ birth: "2007",
+ hometown: "경북 대구",
+ hobby: "그림 그리기",
+ mbti: "ENFP",
+ group: "IVE",
+ agency: "STARSHIP",
+ groupLogo: "/images/STARSHIP.png",
+ instagram: "https://www.instagram.com/ivestarship/",
+ fancam: "https://www.youtube.com/watch?v=NOVaFSRBZrk",
+ },
+ 가을: {
+ englishName: "GAEUL",
+ birth: "2002",
+ hometown: "인천",
+ hobby: "영화 감상",
+ mbti: "ISTJ",
+ group: "IVE",
+ agency: "STARSHIP",
+ groupLogo: "/images/STARSHIP.png",
+ instagram: "https://www.instagram.com/ivestarship/",
+ fancam: "https://www.youtube.com/watch?v=rjspo0mId2s",
+ },
+ 하니: {
+ englishName: "HANNI",
+ birth: "2004",
+ hometown: "호치민",
+ hobby: "캘리그래피",
+ mbti: "INFP",
+ group: "NewJeans",
+ agency: "ADOR",
+ groupLogo: "/images/ADOR.png",
+ instagram: "https://www.instagram.com/newjeans_official/",
+ fancam: "https://www.youtube.com/watch?v=lmJPeFW75qQ",
+ },
+ 해린: {
+ englishName: "HAERIN",
+ birth: "2006",
+ hometown: "김해",
+ hobby: "고양이 영상 보기",
+ mbti: "ISTP",
+ group: "NewJeans",
+ agency: "ADOR",
+ groupLogo: "/images/ADOR.png",
+ instagram: "https://www.instagram.com/newjeans_official/",
+ fancam: "https://www.youtube.com/watch?v=k3jV6DMTCSE",
+ },
+ 민지: {
+ englishName: "MINJI",
+ birth: "2004",
+ hometown: "강릉",
+ hobby: "산책",
+ mbti: "ESTJ",
+ group: "NewJeans",
+ agency: "ADOR",
+ groupLogo: "/images/ADOR.png",
+ instagram: "https://www.instagram.com/newjeans_official/",
+ fancam: "https://www.youtube.com/watch?v=p1cE9T0CFCQ",
+ },
+ 혜인: {
+ englishName: "HYEIN",
+ birth: "2008",
+ hometown: "인천",
+ hobby: "사진 수집",
+ mbti: "ENFP",
+ group: "NewJeans",
+ agency: "ADOR",
+ groupLogo: "/images/ADOR.png",
+ instagram: "https://www.instagram.com/newjeans_official/",
+ fancam: "https://www.youtube.com/watch?v=Wz_mVys9Z4w",
+ },
+ 다니엘: {
+ englishName: "DANIELLE",
+ birth: "2005",
+ hometown: "뉴캐슬",
+ hobby: "요리",
+ mbti: "ENFP",
+ group: "NewJeans",
+ agency: "ADOR",
+ groupLogo: "/images/ADOR.png",
+ instagram: "https://www.instagram.com/newjeans_official/",
+ fancam: "https://www.youtube.com/watch?v=D0x7KSdVWYs",
+ },
+ 카리나: {
+ englishName: "KARINA",
+ birth: "2000",
+ hometown: "수원",
+ hobby: "댄스",
+ mbti: "ENFP",
+ group: "AESPA",
+ agency: "SM",
+ groupLogo: "/images/SM.png",
+ instagram: "https://www.instagram.com/aespa_official/",
+ fancam: "https://www.youtube.com/watch?v=pgP3l3VfTJE",
+ },
+ 윈터: {
+ englishName: "WINTER",
+ birth: "2001",
+ hometown: "양산",
+ hobby: "게임",
+ mbti: "INFJ",
+ group: "AESPA",
+ agency: "SM",
+ groupLogo: "/images/SM.png",
+ instagram: "https://www.instagram.com/aespa_official/",
+ fancam: "https://www.youtube.com/watch?v=UAQT5Hgrm1Q",
+ },
+ 닝닝: {
+ englishName: "NINGNING",
+ birth: "2002",
+ hometown: "중국 하얼빈",
+ hobby: "노래 부르기",
+ mbti: "ISFP",
+ group: "AESPA",
+ agency: "SM",
+ groupLogo: "/images/SM.png",
+ instagram: "https://www.instagram.com/aespa_official/",
+ fancam: "https://www.youtube.com/watch?v=NRvqGh6nGWA",
+ },
+ 지젤: {
+ englishName: "GISELLE",
+ birth: "2000",
+ hometown: "일본 도쿄",
+ hobby: "랩 작사",
+ mbti: "ENFP",
+ group: "AESPA",
+ agency: "SM",
+ groupLogo: "/images/SM.png",
+ instagram: "https://www.instagram.com/aespa_official/",
+ fancam: "https://www.youtube.com/watch?v=xWyeg5qYL2I",
+ },
+ 카즈하: {
+ englishName: "KAZUHA",
+ birth: "2003",
+ hometown: "일본",
+ hobby: "발레",
+ mbti: "ISFP",
+ group: "LE SSERAFIM",
+ agency: "SOURCE MUSIC",
+ groupLogo: "/images/SOURCEmusic.png",
+ instagram: "https://www.instagram.com/le_sserafim/",
+ fancam: "https://www.youtube.com/watch?v=tRU_QbQCYn0",
+ },
+ 김채원: {
+ englishName: "KIM CHAEWON",
+ birth: "2000",
+ hometown: "서울",
+ hobby: "요가",
+ mbti: "ISTP",
+ group: "LE SSERAFIM",
+ agency: "SOURCE MUSIC",
+ groupLogo: "/images/SOURCEmusic.png",
+ instagram: "https://www.instagram.com/le_sserafim/",
+ fancam: "https://www.youtube.com/watch?v=_w60TKkK62I",
+ },
+ 사쿠라: {
+ englishName: "SAKURA",
+ birth: "1998",
+ hometown: "일본 가고시마",
+ hobby: "게임 방송 보기",
+ mbti: "INFP",
+ group: "LE SSERAFIM",
+ agency: "SOURCE MUSIC",
+ groupLogo: "/images/SOURCEmusic.png",
+ instagram: "https://www.instagram.com/le_sserafim/",
+ fancam: "https://www.youtube.com/watch?v=PDNpCOAEKOA",
+ },
+ 홍은채: {
+ englishName: "HONG EUNCHAE",
+ birth: "2006",
+ hometown: "광주",
+ hobby: "요리",
+ mbti: "ENFP",
+ group: "LE SSERAFIM",
+ agency: "SOURCE MUSIC",
+ groupLogo: "/images/SOURCEmusic.png",
+ instagram: "https://www.instagram.com/le_sserafim/",
+ fancam: "https://www.youtube.com/watch?v=ySg9EIhiMgE",
+ },
+ 허윤진: {
+ englishName: "HUH YUNJIN",
+ birth: "2001",
+ hometown: "미국",
+ hobby: "기타 연주",
+ mbti: "ENFP",
+ group: "LE SSERAFIM",
+ agency: "SOURCE MUSIC",
+ groupLogo: "/images/SOURCEmusic.png",
+ instagram: "https://www.instagram.com/le_sserafim/",
+ fancam: "https://www.youtube.com/watch?v=eSKN_H2D25M",
+ },
+ 설윤: {
+ englishName: "SULLYOON",
+ birth: "2004",
+ hometown: "대구",
+ hobby: "패션 스타일링",
+ mbti: "INFJ",
+ group: "NMIXX",
+ agency: "JYP",
+ groupLogo: "/images/JYP.png",
+ instagram: "https://www.instagram.com/nmixx_official/",
+ fancam: "https://www.youtube.com/watch?v=q0F0hrpHxdo",
+ },
+ 해원: {
+ englishName: "HAEWON",
+ birth: "2003",
+ hometown: "인천",
+ hobby: "노래 연습",
+ mbti: "ISFJ",
+ group: "NMIXX",
+ agency: "JYP",
+ groupLogo: "/images/JYP.png",
+ instagram: "https://www.instagram.com/nmixx_official/",
+ fancam: "https://www.youtube.com/watch?v=eyhjqIxgvug",
+ },
+ 지우: {
+ englishName: "JIWOO",
+ birth: "2005",
+ hometown: "서울",
+ hobby: "춤 연습",
+ mbti: "ENTP",
+ group: "NMIXX",
+ agency: "JYP",
+ groupLogo: "/images/JYP.png",
+ instagram: "https://www.instagram.com/nmixx_official/",
+ fancam: "https://www.youtube.com/watch?v=S9800GcCTco",
+ },
+ 베이: {
+ englishName: "BAE",
+ birth: "2004",
+ hometown: "양산",
+ hobby: "그림 그리기",
+ mbti: "ESFP",
+ group: "NMIXX",
+ agency: "JYP",
+ groupLogo: "/images/JYP.png",
+ instagram: "https://www.instagram.com/nmixx_official/",
+ fancam: "https://www.youtube.com/watch?v=v1iPeohKoKg",
+ },
+ 규진: {
+ englishName: "KYUJIN",
+ birth: "2006",
+ hometown: "성남",
+ hobby: "운동",
+ mbti: "ESTJ",
+ group: "NMIXX",
+ agency: "JYP",
+ groupLogo: "/images/JYP.png",
+ instagram: "https://www.instagram.com/nmixx_official/",
+ fancam: "https://www.youtube.com/live/qAkFm4PZYYs",
+ },
+ 릴리: {
+ englishName: "LILY",
+ birth: "2002",
+ hometown: "호주",
+ hobby: "기타 연주",
+ mbti: "ENFP",
+ group: "NMIXX",
+ agency: "JYP",
+ groupLogo: "/images/JYP.png",
+ instagram: "https://www.instagram.com/nmixx_official/",
+ fancam: "https://www.youtube.com/watch?v=2hsNDr0QWNg",
+ },
+ 지수: {
+ englishName: "JISOO",
+ birth: "1995",
+ hometown: "서울",
+ hobby: "그림 그리기",
+ mbti: "INFJ",
+ group: "BLACKPINK",
+ agency: "YG",
+ groupLogo: "/images/YG.png",
+ instagram: "https://www.instagram.com/blackpinkofficial/",
+ fancam: "https://www.youtube.com/watch?v=67aBenO-MeU",
+ },
+ 제니: {
+ englishName: "JENNIE",
+ birth: "1996",
+ hometown: "서울",
+ hobby: "패션 디자인",
+ mbti: "INFP",
+ group: "BLACKPINK",
+ agency: "YG",
+ groupLogo: "/images/YG.png",
+ instagram: "https://www.instagram.com/blackpinkofficial/",
+ fancam: "https://www.youtube.com/watch?v=3ipaG-bWI7s",
+ },
+ 로제: {
+ englishName: "ROSÉ",
+ birth: "1997",
+ hometown: "뉴질랜드",
+ hobby: "기타 연주",
+ mbti: "ENFP",
+ group: "BLACKPINK",
+ agency: "YG",
+ groupLogo: "/images/YG.png",
+ instagram: "https://www.instagram.com/blackpinkofficial/",
+ fancam: "https://www.youtube.com/watch?feature=shared&v=m1wtAs4cyT4",
+ },
+ 리사: {
+ englishName: "LISA",
+ birth: "1997",
+ hometown: "태국",
+ hobby: "춤",
+ mbti: "ISFP",
+ group: "BLACKPINK",
+ agency: "YG",
+ groupLogo: "/images/YG.png",
+ instagram: "https://www.instagram.com/blackpinkofficial/",
+ fancam: "https://www.youtube.com/watch?v=9bHDfHP3Rb8",
+ },
+ 원빈: {
+ englishName: "WONBIN",
+ birth: "2002",
+ hometown: "울산",
+ hobby: "기타 연주",
+ mbti: "INFP",
+ group: "RIIZE",
+ agency: "SM",
+ groupLogo: "/images/SM.png",
+ instagram: "https://www.instagram.com/riize_official/",
+ fancam: "https://www.youtube.com/watch?v=6UnE-maug2k",
+ },
+ 앤톤: {
+ englishName: "ANTON",
+ birth: "2004",
+ hometown: "미국",
+ hobby: "작곡",
+ mbti: "ISFP",
+ group: "RIIZE",
+ agency: "SM",
+ groupLogo: "/images/SM.png",
+ instagram: "https://www.instagram.com/riize_official/",
+ fancam: "https://www.youtube.com/watch?v=QiRNwY_OO6Q",
+ },
+ 차은우: {
+ englishName: "CHA EUNWOO",
+ birth: "1997",
+ hometown: "경기",
+ hobby: "사진 찍기",
+ mbti: "INFJ",
+ group: "ASTRO",
+ agency: "FANTAGIO",
+ groupLogo: "/images/FANTAGIO.png",
+ instagram: "https://www.instagram.com/officialastro/",
+ fancam: "https://www.youtube.com/watch?v=xiodq-7seYA",
+ },
+ 문빈: {
+ englishName: "MOON BIN",
+ birth: "1998",
+ hometown: "청주",
+ hobby: "운동",
+ mbti: "ISFP",
+ group: "ASTRO",
+ agency: "FANTAGIO",
+ groupLogo: "/images/FANTAGIO.png",
+ instagram: "https://www.instagram.com/officialastro/",
+ fancam: "https://www.youtube.com/watch?v=TL2xzJprAZo",
+ },
+ 라키: {
+ englishName: "ROCKY",
+ birth: "1999",
+ hometown: "진주",
+ hobby: "안무 창작",
+ mbti: "ENFP",
+ group: "ASTRO",
+ agency: "FANTAGIO",
+ groupLogo: "/images/FANTAGIO.png",
+ instagram: "https://www.instagram.com/officialastro/",
+ fancam: "https://www.youtube.com/watch?v=xZv39_JqE6g",
+ },
+ 윤산하: {
+ englishName: "YOON SANHA",
+ birth: "2000",
+ hometown: "서울",
+ hobby: "게임",
+ mbti: "INFP",
+ group: "ASTRO",
+ agency: "FANTAGIO",
+ groupLogo: "/images/FANTAGIO.png",
+ instagram: "https://www.instagram.com/officialastro/",
+ fancam: "https://www.youtube.com/watch?v=qloR_k6ZI_U",
+ },
+ 키: {
+ englishName: "KEY",
+ birth: "1991",
+ hometown: "대구",
+ hobby: "패션 스타일링",
+ mbti: "ENTJ",
+ group: "SHINee",
+ agency: "SM",
+ groupLogo: "/images/SM.png",
+ instagram: "https://www.instagram.com/shinee/",
+ fancam: "https://www.youtube.com/watch?v=l_kMRwCe9h0",
+ },
+ 민호: {
+ englishName: "MINHO",
+ birth: "1991",
+ hometown: "인천",
+ hobby: "농구",
+ mbti: "ESTP",
+ group: "SHINee",
+ agency: "SM",
+ groupLogo: "/images/SM.png",
+ instagram: "https://www.instagram.com/shinee/",
+ fancam: "https://www.youtube.com/watch?v=Az6yUzFDz1U",
+ },
+ 태민: {
+ englishName: "TAEMIN",
+ birth: "1993",
+ hometown: "서울",
+ hobby: "그림 그리기",
+ mbti: "INFP",
+ group: "SHINee",
+ agency: "SM",
+ groupLogo: "/images/SM.png",
+ instagram: "https://www.instagram.com/shinee/",
+ fancam: "https://www.youtube.com/watch?v=K2oByTvJ6lk",
+ },
+ 종현: {
+ englishName: "JONGHYUN",
+ birth: "1990",
+ hometown: "서울",
+ hobby: "작사",
+ mbti: "ISFP",
+ group: "SHINee",
+ agency: "SM",
+ groupLogo: "/images/SM.png",
+ instagram: "https://www.instagram.com/shinee/",
+ fancam: "https://www.youtube.com/watch?v=btEdyStLjRQ",
+ },
+ 온유: {
+ englishName: "ONEW",
+ birth: "1989",
+ hometown: "경기",
+ hobby: "요리",
+ mbti: "ISFJ",
+ group: "SHINee",
+ agency: "SM",
+ groupLogo: "/images/SM.png",
+ instagram: "https://www.instagram.com/shinee/",
+ fancam: "https://www.youtube.com/watch?v=3yFiW-HupY4",
+ },
+ 재현: {
+ englishName: "JAEHYUN",
+ birth: "1997",
+ hometown: "서울",
+ hobby: "피아노 연주",
+ mbti: "INFJ",
+ group: "NCT",
+ agency: "SM",
+ groupLogo: "/images/SM.png",
+ instagram: "https://www.instagram.com/nct/",
+ fancam: "https://www.youtube.com/watch?v=szvw-WOg3aU",
+ },
+ 도영: {
+ englishName: "DOYOUNG",
+ birth: "1996",
+ hometown: "서울",
+ hobby: "독서",
+ mbti: "ISFJ",
+ group: "NCT",
+ agency: "SM",
+ groupLogo: "/images/SM.png",
+ instagram: "https://www.instagram.com/nct/",
+ fancam: "https://www.youtube.com/watch?v=RwJ2TBB43lM",
+ },
+ 태용: {
+ englishName: "TAEYONG",
+ birth: "1995",
+ hometown: "서울",
+ hobby: "춤추기",
+ mbti: "ENFP",
+ group: "NCT",
+ agency: "SM",
+ groupLogo: "/images/SM.png",
+ instagram: "https://www.instagram.com/nct/",
+ fancam: "https://www.youtube.com/watch?v=sECXv8Mbjbg",
+ },
+ 신유: {
+ englishName: "SHIN YU",
+ birth: "2004",
+ hometown: "서울",
+ hobby: "운동",
+ mbti: "ENFP",
+ group: "TWS",
+ agency: "PLEDIS",
+ groupLogo: "/images/PLEDIS.png",
+ instagram: "https://www.instagram.com/tws.official/",
+ fancam: "https://www.youtube.com/watch?v=BtFPgl0tC0k",
+ },
+ 한진: {
+ englishName: "HANJIN",
+ birth: "2004",
+ hometown: "경기",
+ hobby: "영화 감상",
+ mbti: "ISTP",
+ group: "TWS",
+ agency: "PLEDIS",
+ groupLogo: "/images/PLEDIS.png",
+ instagram: "https://www.instagram.com/tws.official/",
+ fancam: "https://www.youtube.com/watch?v=X-DLqSIv3YA",
+ },
+ 경민: {
+ englishName: "KYUNGMIN",
+ birth: "2005",
+ hometown: "인천",
+ hobby: "독서",
+ mbti: "INFP",
+ group: "TWS",
+ agency: "PLEDIS",
+ groupLogo: "/images/PLEDIS.png",
+ instagram: "https://www.instagram.com/tws.official/",
+ fancam: "https://www.youtube.com/watch?v=YHPGeGm32O4",
+ },
+ 지훈: {
+ englishName: "JIHOON",
+ birth: "2004",
+ hometown: "대전",
+ hobby: "게임",
+ mbti: "ISFP",
+ group: "TWS",
+ agency: "PLEDIS",
+ groupLogo: "/images/PLEDIS.png",
+ instagram: "https://www.instagram.com/tws.official/",
+ fancam: "https://www.youtube.com/watch?v=BzSV1_K-P7A",
+ },
+ 연준: {
+ englishName: "YEONJUN",
+ birth: "1999",
+ hometown: "서울",
+ hobby: "패션 코디",
+ mbti: "ENTP",
+ group: "TXT",
+ agency: "HYBE",
+ groupLogo: "/images/HYBE.png",
+ instagram: " https://www.instagram.com/txt_bighit/",
+ fancam: "https://www.youtube.com/watch?v=sx4hPjppi6E",
+ },
+ 범규: {
+ englishName: "BEOMGYU",
+ birth: "2001",
+ hometown: "대구",
+ hobby: "기타 치기",
+ mbti: "INFP",
+ group: "TXT",
+ agency: "HYBE",
+ groupLogo: "/images/HYBE.png",
+ instagram: " https://www.instagram.com/txt_bighit/",
+ fancam: "https://www.youtube.com/watch?v=rDnPaRFDnUk",
+ },
+ 수빈: {
+ englishName: "SOOBIN",
+ birth: "2000",
+ hometown: "서울",
+ hobby: "동물 돌보기",
+ mbti: "ISFJ",
+ group: "TXT",
+ agency: "HYBE",
+ groupLogo: "/images/HYBE.png",
+ instagram: " https://www.instagram.com/txt_bighit/",
+ fancam: "https://www.youtube.com/watch?v=ibnQ1bH8fIM",
+ },
+ 진: {
+ englishName: "JIN",
+ birth: "1992",
+ hometown: "과천",
+ hobby: "요리",
+ mbti: "INFP",
+ group: "BTS",
+ agency: "HYBE",
+ groupLogo: "/images/HYBE.png",
+ instagram: "https://www.instagram.com/bts.bighitofficial/",
+ fancam: "https://www.youtube.com/watch?v=MilKNyALZ6w",
+ },
+ 정국: {
+ englishName: "JUNGKOOK",
+ birth: "1997",
+ hometown: "부산",
+ hobby: "복싱",
+ mbti: "ISFP",
+ group: "BTS",
+ agency: "HYBE",
+ groupLogo: "/images/HYBE.png",
+ instagram: "https://www.instagram.com/bts.bighitofficial/",
+ fancam: "https://www.youtube.com/watch?v=w5XxXWJrARU",
+ },
+ 뷔: {
+ englishName: "V",
+ birth: "1995",
+ hometown: "대구",
+ hobby: "사진 찍기",
+ mbti: "ENFP",
+ group: "BTS",
+ agency: "HYBE",
+ groupLogo: "/images/HYBE.png",
+ instagram: "https://www.instagram.com/bts.bighitofficial/",
+ fancam: "https://www.youtube.com/watch?v=AX0CIHA2f1M",
+ },
+ 유진: {
+ englishName: "YUJIN",
+ birth: "2001",
+ hometown: "서울",
+ hobby: "운동",
+ mbti: "ENFP",
+ group: "ZEROBASEONE",
+ agency: "WAKEONE",
+ groupLogo: "/images/WAKEONE.png",
+ instagram: "https://www.instagram.com/zb1_official/",
+ fancam: "https://www.youtube.com/watch?v=T0h0r3q_9UA",
+ },
+ 요시: {
+ englishName: "YOSHI",
+ birth: "2000",
+ hometown: "일본",
+ hobby: "그림 그리기",
+ mbti: "ISTJ",
+ group: "TREASURE",
+ agency: "YG",
+ groupLogo: "/images/YG.png",
+ instagram: "https://www.instagram.com/yg_treasure_official/",
+ fancam: "https://www.youtube.com/watch?v=-IN7-p7ElSw",
+ },
+ 태산: {
+ englishName: "TAESAN",
+ birth: "2004",
+ hometown: "부산",
+ hobby: "축구",
+ mbti: "INFP",
+ group: "BOYNEXTDOOR",
+ agency: "KOZ",
+ groupLogo: "/images/KOZ.png",
+ instagram: " https://www.instagram.com/official.boynextdoor/",
+ fancam: "https://www.youtube.com/watch?v=bxxCbRKfjHs",
+ },
+ 명재현: {
+ englishName: "MYUNG JAEHYUN",
+ birth: "2003",
+ hometown: "서울",
+ hobby: "게임",
+ mbti: "ISFJ",
+ group: "BOYNEXTDOOR",
+ agency: "KOZ",
+ groupLogo: "/images/KOZ.png",
+ instagram: " https://www.instagram.com/official.boynextdoor/",
+ fancam: "https://www.youtube.com/watch?v=PX05ht8so7s",
+ },
+};
diff --git a/src/pages/List/Chart/index.jsx b/src/pages/List/Chart/index.jsx
new file mode 100644
index 0000000..a32b797
--- /dev/null
+++ b/src/pages/List/Chart/index.jsx
@@ -0,0 +1,192 @@
+import styled from "@emotion/styled";
+import React, { useState } from "react";
+import chartImg from "../../../../public/images/Chart.png";
+import Button from "../../../components/Button/Button";
+import Circle from "../../../components/Circle";
+import Modal from "../../../components/Modal";
+import useChart from "../../../hooks/useChart"; // useChart 훅 import
+import ChartVoteModal from "./components/ChartVoteModal";
+import IdolProfileModal from "./components/IdolProfileModal";
+import { idolProfiles } from "./components/IdolProfiles";
+
+import {
+ ChartButtonWrap,
+ ChartContainer,
+ ChartHeaderWrap,
+ ChartIdol,
+ ChartIdolLeft,
+ ChartIdolRight,
+ ChartList,
+ ChartTitle,
+ ListItem,
+ MoreButton,
+ ProfileInfo,
+ RankAndName,
+ VoteChart,
+ Votes,
+} from "./Chart.styles";
+
+const Overlay = styled.div`
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100vw;
+ height: 100vh;
+ background-color: rgba(0, 0, 0, 0.6);
+ z-index: 1000;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+`;
+
+const Chart = () => {
+ const {
+ idols,
+ setIdols,
+ activeTab,
+ loading,
+ visibleList,
+ handleMore,
+ handleTabChange,
+ femaleIdols,
+ maleIdols,
+ } = useChart();
+
+ const [isModalOpen, setIsModalOpen] = useState(false);
+ const [selectedIdol, setSelectedIdol] = useState(null);
+
+ const openModal = () => setIsModalOpen(true);
+ const closeModal = () => setIsModalOpen(false);
+
+ const handleIdolClick = (idol) => {
+ const mockData = idolProfiles[idol.name];
+ if (mockData) {
+ setSelectedIdol({ ...idol, ...mockData });
+ } else {
+ setSelectedIdol(idol);
+ }
+ };
+
+ const IdolItem = ({ idol, index }) => (
+ handleIdolClick(idol)}
+ style={{ cursor: "pointer" }}
+ >
+
+
+
+ {index + 1}
+ {idol.group}
+ {idol.name}
+
+
+ {idol.totalVotes}
+
+ );
+
+ return (
+ <>
+
+
+ 이달의 차트
+
+
+
+
+ 차트 투표하기
+
+
+
+
+
+
+
+
+
+
+ handleTabChange("female")}
+ style={{
+ cursor: "pointer",
+ fontWeight: activeTab === "female" ? 700 : 400,
+ backgroundColor:
+ activeTab === "female" ? "#ffffff1a" : "transparent",
+ borderBottom:
+ activeTab === "female" ? "1px solid #ffffff" : "none",
+ transition: "all 0.3s ease",
+ }}
+ >
+ 이달의 여자 아이돌
+
+ handleTabChange("male")}
+ style={{
+ cursor: "pointer",
+ fontWeight: activeTab === "male" ? 700 : 400,
+ backgroundColor:
+ activeTab === "male" ? "#ffffff1a" : "transparent",
+ borderBottom: activeTab === "male" ? "1px solid #ffffff" : "none",
+ transition: "all 0.3s ease",
+ }}
+ >
+ 이달의 남자 아이돌
+
+
+
+ {loading ? (
+
+ 불러오는 중...
+
+ ) : (
+ <>
+
+ {visibleList.map((idol, index) => (
+
+ ))}
+
+
+ {visibleList.length <
+ (activeTab === "female"
+ ? femaleIdols.length
+ : maleIdols.length) && (
+
+
+ 더 보기
+
+
+ )}
+ >
+ )}
+
+
+ {selectedIdol && (
+ setSelectedIdol(null)}>
+ e.stopPropagation()}
+ style={{ all: "unset", cursor: "pointer" }}
+ >
+ setSelectedIdol(null)}
+ />
+
+
+ )}
+ >
+ );
+};
+
+export default Chart;
diff --git a/src/pages/List/Donation/Donation.style.js b/src/pages/List/Donation/Donation.style.js
new file mode 100644
index 0000000..92b4f63
--- /dev/null
+++ b/src/pages/List/Donation/Donation.style.js
@@ -0,0 +1,75 @@
+/** @jsxImportSource @emotion/react */
+import { css } from "@emotion/react";
+
+/**
+ * 슬라이더 위에 고정되어 나타나는 타이틀 스타일
+ */
+export const donationTitle = css`
+margin-top: 50px;
+
+color: #ffffff;
+font-size: 24px;
+font-weight: 700;
+
+@media (max-width: 426px) {
+font-size: 4.27vw;
+margin-top: 10.67vw;
+}`;
+
+export const donationPageNation = css`
+position: relative;`;
+
+/**
+ * 오른쪽 화살표 버튼 스타일
+ * 왼쪽 여백을 두어 Card와 분리
+ * donationContent(슬라이더 내부)를 기준으로 오른쪽으로 배치
+ */
+export const pageNationRight = css`
+ width: 40px;
+ height: 78px;
+ flex-shrink: 0;
+
+ position: absolute;
+ right: -80px;
+ top: 50%;
+ transform: translateY(-50%);
+
+ margin-left: 30px;
+
+ &:hover {
+ background-color: #1B1B1B;
+ border-radius: 4px;
+ }
+
+ @media (max-width: 1365px) {
+ display: none;
+ }
+
+`;
+
+/**
+ * 왼쪽 화살표 버튼 스타일
+ * 오른쪽 여백을 두어 Card와 분리
+ * donationContent(슬라이더 내부)를 기준으로 왼쪽으로 배치
+ */
+export const pageNationLeft = css`
+ width: 40px;
+ height: 78px;
+ flex-shrink: 0;
+
+ position: absolute;
+ left: -80px;
+ top: 50%;
+ transform: translateY(-50%);
+
+ margin-right: 30px;
+
+ &:hover {
+ background-color: #1B1B1B;
+ border-radius: 4px;
+ }
+
+ @media (max-width: 1365px) {
+ display: none;
+ }
+`;
diff --git a/src/pages/List/Donation/components/Card/Card.style.js b/src/pages/List/Donation/components/Card/Card.style.js
new file mode 100644
index 0000000..520a500
--- /dev/null
+++ b/src/pages/List/Donation/components/Card/Card.style.js
@@ -0,0 +1,227 @@
+/** @jsxImportSource @emotion/react */
+import { css } from "@emotion/react";
+
+/**
+ * 카드 전체 컨테이너
+ * - 세로 방향 정렬
+ * - 가운데 정렬
+ * - 크기 고정
+ */
+export const donationCardContainer = css`
+ display: flex;
+ width: 282px;
+ height: 402px;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ flex-shrink: 0;
+
+ @media (max-width: 426px) {
+ width: 158px;
+ height: 303px;
+ }
+`;
+
+/**
+ * 이미지 영역 컨테이너
+ * - 버튼 및 오버레이를 절대 위치로 배치하기 위한 기준 요소
+ */
+export const donatioImgContainer = css`
+ position: relative;
+ width: 282px;
+ height: 293px;
+
+ @media (max-width: 426px) {
+ width: 158px;
+ height: 206px;
+ }
+`;
+
+/**
+ * 프로필 이미지 + 오버레이를 감싸는 wrapper
+ * - border-radius 및 overflow 처리
+ */
+export const imgWrapper = css`
+ width: 100%;
+ height: 100%;
+ border-radius: 8px;
+ overflow: hidden;
+`;
+
+/**
+ * 프로필 이미지
+ * - 전체 채우기 + 라운드
+ */
+export const donationImg = css`
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+ border-radius: 8px;
+ position: relative;
+ z-index: 1;
+`;
+
+/**
+ * 이미지 위에 겹쳐지는 SVG 오버레이
+ */
+export const overlaySvg = css`
+ position: absolute;
+ top: 0;
+ left: 0;
+ height: 100%;
+ z-index: 2;
+`;
+
+/**
+ * 후원하기 버튼
+ * - 이미지 하단에 절대 위치로 배치
+ * - 배치 제외 나머지는 공통 컴포넌트로 대체 예정
+ */
+export const donationButton = css`
+position: absolute;
+bottom: 30px;
+left: 22px;
+
+z-index: 3;
+
+font-size: 13px;
+
+@media (max-width: 426px) {
+ bottom: 4vw;
+ left: 1.87vw;
+}
+`;
+
+/**
+ * 카드 하단 텍스트 영역 전체
+ * - 수직 정렬
+ */
+export const donationDescription = css`
+display: flex;
+width: 100%;
+flex-direction: column;
+align-items: flex-start;
+gap: 24px;
+`;
+
+/**
+ * 제목 영역 (서브제목 + 제목)
+ */
+export const donationTitleContainer = css`
+display: flex;
+width: 100%;
+flex-direction: column;
+align-items: flex-start;
+gap: 8px;
+
+@media (max-width: 426px) {
+gap: 1.6vw;
+}`;
+
+/**
+ * 장소(서브제목) 스타일
+ * - 작은 회색 텍스트
+ * - 투명도 적용
+ */
+export const descriptionSubtitle = css`
+color: #FFF;
+
+text-align: center;
+font-size: 16px;
+font-style: normal;
+font-weight: 400;
+line-height: 18px; /* 112.5% */
+letter-spacing: -0.165px;
+
+opacity: 0.4;
+
+@media (max-width: 426px) {
+font-size: 2.82vw;
+`;
+
+/**
+ * 제목(조공 제목) 텍스트 스타일
+ */
+export const descriptionTitle = css`
+color: var(--white-whtie_F7F7F8, #F7F7F8);
+text-align: center;
+font-size: 18px;
+font-style: normal;
+font-weight: 500;
+line-height: normal;
+
+@media (max-width: 426px) {
+font-size: 3.05vw;
+`;
+
+/**
+ * 하단 정보 전체 영역
+ * - 세로 정렬로 D-day, 진행률 등 순차적으로 배치
+ */
+export const donationFooter = css`
+width: 100%;
+display: flex;
+flex-direction: column;`;
+
+/**
+ * 하단 정보 라인 (크레딧 + D-day)
+ * - 좌우로 나란히 배치
+ */
+export const donationFooterUp = css`
+width: 100%;
+display: flex;
+justify-content: space-between;
+
+margin-bottom: 4px;
+
+@media (max-width: 426px) {
+margin-bottom: 0.27vw;
+};
+`;
+
+/**
+ * 크레딧 영역 (아이콘 + 금액)
+ * - 아이콘 옆에 텍스트 배치
+ */
+export const donationFooterLeft = css`
+display: flex;
+align-items: center`;
+
+/**
+ * 크레딧 아이콘 이미지
+ */
+export const creditImg = css`
+width: 12px;
+height: 12px`;
+
+/**
+ * 목표 금액 텍스트 스타일
+ */
+export const targetDonation = css`
+color: var(--orange-F96D69);
+font-size: 12px;
+font-style: normal;
+font-weight: 400;
+line-height: 18px; /* 150% */
+letter-spacing: -0.165px;
+
+@media (max-width: 426px) {
+font-size: 2.67vw;
+}`;
+
+/**
+ * D-Day 남은 일 수 텍스트
+ * - 오른쪽 정렬 스타일
+ */
+export const donationDday = css`
+color: var(--white-F7F7F8);
+text-align: right;
+font-size: 12px;
+font-style: normal;
+font-weight: 400;
+line-height: 18px; /* 150% */
+letter-spacing: -0.165px;
+
+@media (max-width: 426px) {
+font-size: 2.67vw;
+}`;
diff --git a/src/pages/List/Donation/components/Card/ProgressBar/ProgressBar.style.js b/src/pages/List/Donation/components/Card/ProgressBar/ProgressBar.style.js
new file mode 100644
index 0000000..aad3f6c
--- /dev/null
+++ b/src/pages/List/Donation/components/Card/ProgressBar/ProgressBar.style.js
@@ -0,0 +1,32 @@
+/** @jsxImportSource @emotion/react */
+import { css } from "@emotion/react";
+
+/**
+ * 프로그레스 바의 전체 컨테이너 스타일
+ *
+ * - 너비는 100%로 설정되어 부모 요소를 꽉 채움
+ * - 높이는 얇은 선처럼 보이도록 설정
+ * - 배경색은 흰색으로 설정되어, 진행 바의 배경 역할
+ */
+export const progressContainer = css`
+width: 100%;
+height: 1px;
+flex-shrink: 0;
+
+background-color: #ffffff
+;
+`;
+
+/**
+ * 프로그레스 바 내부 채워지는 영역 스타일
+ *
+ * @param {number} progress - 현재 진행률 (0 ~ 100)
+ *
+ * - 너비는 전달받은 `progress` 값에 따라 결정됨
+ * - 부모 컨테이너 높이에 맞춤
+ * - 배경색은 CSS 변수 `--orange-F96D69` 사용
+ */
+export const progressContent = (progress) => css`
+width:${progress}%;
+height: 100%;
+background-color: var(--orange-F96D69)`;
diff --git a/src/pages/List/Donation/components/Card/ProgressBar/index.jsx b/src/pages/List/Donation/components/Card/ProgressBar/index.jsx
new file mode 100644
index 0000000..80c2a46
--- /dev/null
+++ b/src/pages/List/Donation/components/Card/ProgressBar/index.jsx
@@ -0,0 +1,20 @@
+/** @jsxImportSource @emotion/react */
+
+import { progressContainer, progressContent } from "./ProgressBar.style";
+
+/**
+ * ProgressBar 컴포넌트
+ *
+ * 달성률 전달받아 시각적으로 표시하는 프로그레스 바
+ *
+ * @param {number} props.progress - 현재 달성률
+ */
+function ProgressBar({ progress }) {
+ return (
+
+ );
+}
+
+export default ProgressBar;
diff --git a/src/pages/List/Donation/components/Card/index.jsx b/src/pages/List/Donation/components/Card/index.jsx
new file mode 100644
index 0000000..2470c85
--- /dev/null
+++ b/src/pages/List/Donation/components/Card/index.jsx
@@ -0,0 +1,103 @@
+/** @jsxImportSource @emotion/react */
+import {
+ creditImg,
+ descriptionSubtitle,
+ descriptionTitle,
+ donatioImgContainer,
+ donationButton,
+ donationCardContainer,
+ donationDday,
+ donationDescription,
+ donationFooter,
+ donationFooterLeft,
+ donationFooterUp,
+ donationImg,
+ donationTitleContainer,
+ imgWrapper,
+ overlaySvg,
+ targetDonation,
+} from "./Card.style"; // 스타일 import
+
+import { useNavigate } from "react-router-dom";
+import Button from "../../../../../components/Button/Button";
+import ProgressBar from "./ProgressBar";
+
+/**
+ * 단일 후원 카드 컴포넌트
+ *
+ * @param {Object} donation - 후원 데이터 객체
+ * @returns JSX
+ */
+function Card({ donation }) {
+ const navigate = useNavigate();
+ const idol = donation.idol; // 아이돌 정보 분리 추출
+
+ // 남은 날짜 구하기
+ const today = new Date();
+ const deadline = new Date(donation.deadline);
+ const diffTime = deadline - today;
+ const dDay = Math.ceil(diffTime / (1000 * 60 * 60 * 24)); // 일 단위로 변환
+
+ // 진행률 구하기
+ const progress = Math.min(
+ (donation.receivedDonations / donation.targetDonation) * 100,
+ 100,
+ ); // 100% 초과 방지
+
+ // 조건에 따라 버튼 비활성화
+ const isButtonDisabled = dDay <= 0 || progress >= 100;
+
+ return (
+
+ {/* 상단 이미지 영역 */}
+
+
+ {/* 아이돌 프로필 이미지 */}
+
+ {/* 그라데이션 SVG 오버레이 */}
+
+
+
+ {/* 후원하기 버튼 */}
+
+ navigate(`/donation-detail/${donation.id}`)}
+ >
+ {isButtonDisabled ? "후원 마감 🎉" : "후원 하기"}
+
+
+
+
+ {/* 카드 하단 텍스트 영역 */}
+
+ {/* 제목 영역 (장소, 제목 등) */}
+
+
{donation.subtitle}
+ {donation.title}
+
+
+ {/* 하단 정보 (크레딧, D-day) */}
+
+
+
+ {/* 크레딧 아이콘 및 목표 금액 */}
+
+
+ {Number(donation.targetDonation).toLocaleString()}
+
+
+ {/* 남은 날짜 */}
+
+ {dDay <= 0 ? "기한 만료" : `${dDay}일 남음`}
+
+
+
+
+
+
+ );
+}
+
+export default Card;
diff --git a/src/pages/List/Donation/components/Carousel/Carousel.style.js b/src/pages/List/Donation/components/Carousel/Carousel.style.js
new file mode 100644
index 0000000..e7a1484
--- /dev/null
+++ b/src/pages/List/Donation/components/Carousel/Carousel.style.js
@@ -0,0 +1,43 @@
+/** @jsxImportSource @emotion/react */
+import { css } from "@emotion/react";
+
+/**
+ * 슬라이드 트랙
+ * - 아이템 가로 배치
+ * - startIndex에 따라 X축 이동
+ */
+export const sliderTrack = (
+ startIndex,
+ visibleCount,
+ itemWidth = 282,
+ gap = 24,
+) => css`
+ display: flex;
+ transition: transform 0.8s ease-in-out;
+ transform: translateX(-${(itemWidth + gap) * startIndex}px);
+ gap: 24px;
+ margin-top: 34px;
+
+ @media (max-width: 426px) {
+ margin-top: 3.76vw;
+ gap: 1.88vw;
+ };
+`;
+
+/**
+ * 뷰포트 영역
+ * - 가로 스크롤 영역
+ * - 스크롤 숨김 처리
+ */
+export const sliderViewport = css`
+ overflow-x: auto;
+ scroll-snap-type: x mandatory;
+ scroll-behavior: smooth;
+ -webkit-overflow-scrolling: touch;
+
+ width: 100%;
+
+ &::-webkit-scrollbar {
+ display: none;
+ }
+`;
diff --git a/src/pages/List/Donation/components/Carousel/index.jsx b/src/pages/List/Donation/components/Carousel/index.jsx
new file mode 100644
index 0000000..09bf79e
--- /dev/null
+++ b/src/pages/List/Donation/components/Carousel/index.jsx
@@ -0,0 +1,63 @@
+/** @jsxImportSource @emotion/react */
+import { useRef, useState } from "react";
+import btnLeft from "/images/btn-donation-arrow-left.svg";
+import btnRight from "/images/btn-donation-arrow-right.svg";
+import {
+ donationPageNation,
+ pageNationLeft,
+ pageNationRight,
+} from "../../Donation.style";
+import { sliderTrack, sliderViewport } from "./Carousel.style";
+
+/**
+ * 캐러셀(슬라이더) 컴포넌트
+ *
+ * @component
+ * @param {Array} props.items - 렌더링할 아이템 배열
+ * @param {number} [props.visibleCount=4] - 한 번에 보여지는 아이템 개수
+ * @param {number} [props.step=4] - 좌우 이동 시 한 번에 이동할 아이템 개수
+ * @param {(item: Object) => React.ReactNode} props.renderItem - 각 아이템을 렌더링할 함수
+ */
+function Carousel({ items, visibleCount = 4, step = 4, renderItem }) {
+ const [startIndex, setStartIndex] = useState(0);
+
+ // 다음 슬라이드로 이동
+ const handleNext = () => {
+ if (startIndex + visibleCount < items.length) {
+ setStartIndex((prev) => prev + step);
+ }
+ };
+
+ // 이전 슬라이드로 이동
+ const handlePrev = () => {
+ if (startIndex - step >= 0) {
+ setStartIndex((prev) => prev - step);
+ }
+ };
+
+ return (
+
+ {startIndex > 0 && (
+
+
+
+ )}
+
+
+
+ {items.map((item) => (
+
{renderItem(item)}
+ ))}
+
+
+
+ {startIndex + visibleCount < items.length && (
+
+
+
+ )}
+
+ );
+}
+
+export default Carousel;
diff --git a/src/pages/List/Donation/components/Skeleton/index.jsx b/src/pages/List/Donation/components/Skeleton/index.jsx
new file mode 100644
index 0000000..1f3c145
--- /dev/null
+++ b/src/pages/List/Donation/components/Skeleton/index.jsx
@@ -0,0 +1,55 @@
+import {
+ container,
+ credit,
+ dDay,
+ description,
+ footer,
+ footerUp,
+ img,
+ mainTitle,
+ progress,
+ subTitle,
+ text,
+} from "./style/CardSkeleton.style";
+/** @jsxImportSource @emotion/react */
+import { button, content, pageNation } from "./style/Skeleton.style";
+
+function CardSkeleton() {
+ return (
+
+
+
+
+ );
+}
+
+function DonationSkeleton() {
+ return (
+
+ );
+}
+
+export default DonationSkeleton;
diff --git a/src/pages/List/Donation/components/Skeleton/style/CardSkeleton.style.js b/src/pages/List/Donation/components/Skeleton/style/CardSkeleton.style.js
new file mode 100644
index 0000000..4278cb0
--- /dev/null
+++ b/src/pages/List/Donation/components/Skeleton/style/CardSkeleton.style.js
@@ -0,0 +1,110 @@
+/** @jsxImportSource @emotion/react */
+import { css, keyframes } from "@emotion/react";
+import { shimmerStyle } from "../../../../../../styles/skeletonAnimation";
+
+export const container = css`
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+
+ width: 282px;
+ height: 402px;
+
+ animation-delay: 0s;
+
+ gap: 5px;
+
+ @media (max-width: 426px) {
+ width: 158px;
+ height: 303px;
+ }
+ ;`;
+
+export const img = css`
+ width: 100%;
+ height: 293px;
+
+ border-radius: 8px;
+
+ ${shimmerStyle}
+`;
+
+export const description = css`
+ display: flex;
+ width: 100%;
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 24px;
+`;
+
+export const text = css`
+ display: flex;
+ width: 100%;
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 8px;
+
+ @media (max-width: 426px) {
+ gap: 1.6vw;
+ }
+ `;
+
+export const subTitle = css`
+ width: 73px;
+ height: 18px;
+
+ ${shimmerStyle}
+ border-radius: 5px;
+`;
+
+export const mainTitle = css`
+ width: 154px;
+ height: 21px;
+
+ ${shimmerStyle}
+ border-radius: 5px;
+`;
+
+export const footer = css`
+ display: flex;
+ flex-direction: column;
+ width: 100%;
+`;
+
+export const footerUp = css`
+ width: 100%;
+
+ display: flex;
+ justify-content: space-between;
+
+ margin-bottom: 4px;
+
+ @media (max-width: 426px) {
+ margin-bottom: 0.27vw;
+ }
+`;
+
+export const credit = css`
+ width: 50px;
+ height: 18px;
+
+ ${shimmerStyle}
+ border-radius: 5px;
+`;
+
+export const dDay = css`
+ width: 41px;
+ height: 18px;
+
+ ${shimmerStyle}
+ border-radius: 5px;
+`;
+
+export const progress = css`
+ width: 100%;
+ height: 1px;
+
+ ${shimmerStyle}
+ border-radius: 5px;
+`;
diff --git a/src/pages/List/Donation/components/Skeleton/style/Skeleton.style.js b/src/pages/List/Donation/components/Skeleton/style/Skeleton.style.js
new file mode 100644
index 0000000..325071b
--- /dev/null
+++ b/src/pages/List/Donation/components/Skeleton/style/Skeleton.style.js
@@ -0,0 +1,36 @@
+/** @jsxImportSource @emotion/react */
+import { css, keyframes } from "@emotion/react";
+import { shimmerStyle } from "../../../../../../styles/skeletonAnimation";
+
+export const pageNation = css`
+ position: relative;
+ `;
+
+export const content = css`
+ display: flex;
+ margin-top: 34px;
+ gap: 24px;
+
+ @media (max-width: 426px){
+ gap: 1.88vw;
+ };
+`;
+
+export const button = css`
+ width: 40px;
+ height: 78px;
+ flex-shrink: 0;
+
+ border-radius: 4px;
+
+ position: absolute;
+ right: -80px;
+ top: 50%;
+ transform: translateY(-50%);
+
+ ${shimmerStyle}
+
+ @media (max-width: 1365px) {
+ display: none;
+ }
+`;
diff --git a/src/pages/List/Donation/index.jsx b/src/pages/List/Donation/index.jsx
new file mode 100644
index 0000000..a178936
--- /dev/null
+++ b/src/pages/List/Donation/index.jsx
@@ -0,0 +1,35 @@
+/** @jsxImportSource @emotion/react */
+import { donationTitle } from "./Donation.style";
+
+import LoadingError from "../../../components/Error";
+import { useDonations } from "../../../hooks/useDonation";
+import Card from "./components/Card";
+import Carousel from "./components/Carousel";
+import DonationSkeleton from "./components/Skeleton";
+
+function Donation() {
+ const { donations, loading, error } = useDonations();
+
+ return (
+
+ 후원을 기다리는 조공
+
+ {loading ? (
+
+
+
+ ) : error ? (
+
+ ) : (
+ (
+
+ )}
+ />
+ )}
+
+ );
+}
+
+export default Donation;
diff --git a/src/pages/List/index.jsx b/src/pages/List/index.jsx
new file mode 100644
index 0000000..60abe4b
--- /dev/null
+++ b/src/pages/List/index.jsx
@@ -0,0 +1,39 @@
+import { css } from "@emotion/react";
+import CreditCharge from "./Charge/components/CreditCharge";
+import Chart from "./Chart/index";
+import Donation from "./Donation/index";
+/** @jsxImportSource @emotion/react */
+
+const List = () => {
+ return (
+ <>
+
+
+
+
+
+
+
+
+ >
+ );
+};
+
+export default List;
+const BlurStyle = css`
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 800px;
+ height: 800px;
+ transform: translate(-50%, -50%);
+ img {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+ }
+ @media (max-width: 768px) {
+ width: 94.12vw;
+ height: 94.12vw;
+ }
+`;
diff --git a/src/pages/Mypage/Mypage.styles.js b/src/pages/Mypage/Mypage.styles.js
new file mode 100644
index 0000000..0c5fc32
--- /dev/null
+++ b/src/pages/Mypage/Mypage.styles.js
@@ -0,0 +1,90 @@
+import { css } from "@emotion/react";
+
+//나의 아이돌
+const myIdolWrapper = css`
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
+ padding: 70px 0px 40px 0px;
+ color: white;
+
+ > h2 {
+ font-size: 26px;
+ margin-bottom: 32px;
+ }
+
+ > h3 {
+ font-size: 20px;
+ color: rgba(255, 255, 255, 0.6)
+ }
+
+ @media (max-width: 768px) {
+ > h2 {
+ font-size: 20px;
+ margin-bottom: 24px;
+ }
+
+ > h3 {
+ font-size: 16px;
+ }
+ }
+
+ @media (max-width: 425px) {
+ > h2 {
+ font-size: 16px;
+ margin-bottom: 12px;
+ }
+ > h3 {
+ font-size: 12px;
+ }
+ }
+`;
+
+// 나의 아이돌 리스트
+const myIdolList = css`
+ display: flex;
+ gap: 28px;
+ width: 100%;
+ overflow-x: auto;
+ -webkit-overflow-scrolling: touch;
+ &::-webkit-scrollbar {
+ display: none;
+ }
+ `;
+
+// 아이돌 추가하기
+const addIdol = css`
+ display: flex;
+ flex-direction: column;
+ width: 100%;
+ color: white;
+
+ > h2 {
+ font-size: 26px;
+ margin-top: 40px;
+ margin-bottom: 32px;
+ }
+
+ @media (max-width: 768px) {
+ > h2 {
+ font-size: 20px;
+ margin-top: 32px;
+ margin-bottom: 56px;
+ }
+ }
+
+ @media (max-width: 425px) {
+ > h2 {
+ font-size: 16px;
+ margin-top: 32px;
+ margin-bottom: 16px;
+ }
+ }
+`;
+
+// 추가하기 버튼
+const addButton = css`
+ display: flex;
+ justify-content: center;
+ margin-top: 48px;
+`;
+
+export { myIdolWrapper, myIdolList, addIdol, addButton };
diff --git a/src/pages/Mypage/components/IdolList/IdolList.styles.js b/src/pages/Mypage/components/IdolList/IdolList.styles.js
new file mode 100644
index 0000000..a17c7d1
--- /dev/null
+++ b/src/pages/Mypage/components/IdolList/IdolList.styles.js
@@ -0,0 +1,31 @@
+import { css } from "@emotion/react";
+
+// &.small을 통해 다른 사이즈 제공
+const idolList = css`
+ // width: 128px;
+ // height: 183px;
+ text-align : center;
+ // margin: 16px 0px;
+ display: flex;
+ flex-direction: column;
+ // justify-content: space-between;
+
+ > h3 {
+ font-size: 16px;
+ font-weight: bold;
+ margin-top: 8px;
+ }
+
+ > h4 {
+ font-size: 14px;
+ color: rgba(255, 255, 255, 0.6)
+ }
+
+ // myIdolList 에 들어가는 사이즈 -> 나중에 ustState 쓸때 사이즈 쓸 예정
+ // &.small {
+ // width: 100px;
+ // height: 153px;
+ // }
+`;
+
+export { idolList };
diff --git a/src/pages/Mypage/components/IdolList/index.jsx b/src/pages/Mypage/components/IdolList/index.jsx
new file mode 100644
index 0000000..9a9e6e3
--- /dev/null
+++ b/src/pages/Mypage/components/IdolList/index.jsx
@@ -0,0 +1,65 @@
+import { css } from "@emotion/react";
+import CheckIdol from "../../../../components/CheckIdol";
+import Circle from "../../../../components/Circle";
+import ProfileXIcon from "../../../../components/ProfileXIcon";
+import { idolList } from "./IdolList.styles";
+
+/** @jsxImportSource @emotion/react */
+
+//imageWrapper 로 감싸면 CheckIdol 사용 가능
+const imageWrapper = (size) => css`
+ position: relative;
+ width: ${size};
+ height: ${size};
+ margin: 0 auto;
+`;
+
+const IdolList = ({
+ idol,
+ size = "128px", //circle 사이즈
+ isChecked = false,
+ isMyIdol = false,
+ onRemove,
+}) => {
+ if (!idol) return null; // idol이 없으면 아무 것도 렌더링하지 않음
+
+ return (
+ <>
+
+
+
+
+
+ {isMyIdol && (
+ // biome-ignore lint/a11y/useKeyWithClickEvents:
+ onRemove(idol.id)}
+ css={css`
+ position: absolute;
+ top: 0px;
+ right: 0px;
+ cursor: pointer;
+ z-index: 10;
+ `}
+ >
+
+
+ )}
+
+
{idol.name}
+
{idol.group}
+
+ >
+ );
+};
+
+export default IdolList;
diff --git a/src/pages/Mypage/hooks/useWindowSize.js b/src/pages/Mypage/hooks/useWindowSize.js
new file mode 100644
index 0000000..c6f531b
--- /dev/null
+++ b/src/pages/Mypage/hooks/useWindowSize.js
@@ -0,0 +1,15 @@
+import { useEffect, useState } from "react";
+
+const useWindowSize = () => {
+ const [windowWidth, setWindowWidth] = useState(window.innerWidth);
+
+ useEffect(() => {
+ const handleResize = () => setWindowWidth(window.innerWidth);
+ window.addEventListener("resize", handleResize);
+ return () => window.removeEventListener("resize", handleResize);
+ }, []);
+
+ return windowWidth;
+};
+
+export default useWindowSize;
diff --git a/src/pages/Mypage/index.jsx b/src/pages/Mypage/index.jsx
new file mode 100644
index 0000000..193ba05
--- /dev/null
+++ b/src/pages/Mypage/index.jsx
@@ -0,0 +1,230 @@
+import "../Mypage/slider/slick-theme.css";
+import "../Mypage/slider/slick.css";
+import { css } from "@emotion/react";
+import { useEffect, useState } from "react";
+import Slider from "react-slick";
+import { idolsAPI } from "../../apis/idolsAPI";
+import Button from "../../components/Button/Button";
+import { addButton, addIdol, myIdolList, myIdolWrapper } from "./Mypage.styles";
+import IdolList from "./components/IdolList";
+import useWindowSize from "./hooks/useWindowSize";
+/** @jsxImportSource @emotion/react */
+
+// ---------- slider 함수 관련 -------------
+// 오른쪽 화살표 스타일
+const nextArrowStyle = {
+ display: "flex",
+ background: "rgba(27, 27, 27, 0.8)",
+ width: "29px",
+ height: "135px",
+ borderRadius: "4px",
+ justifyContent: "center",
+ alignItems: "center",
+ right: "-57px",
+ zIndex: 1,
+};
+
+//왼쪽 화살표 스타일
+const prevArrowStyle = {
+ display: "flex",
+ background: "rgba(27, 27, 27, 0.8)",
+ width: "29px",
+ height: "135px",
+ borderRadius: "4px",
+ justifyContent: "center",
+ alignItems: "center",
+ left: "-57px",
+ zIndex: 1,
+};
+
+//슬라이더 왼쪽 화살표 버튼
+function PrevArrow(props) {
+ const { className, style, onClick } = props;
+ const windowWidth = useWindowSize();
+ if (windowWidth < 1280) return null;
+ return (
+ 버튼 테그로 작성 하면 탭 인덱스, 온키 다운 안써도 들어있음
+ className={className}
+ style={{ ...style, ...prevArrowStyle }} //style 덮어 씌우기
+ onClick={onClick}
+ type="button"
+ />
+ );
+}
+
+//슬라이더 오른쪽 화살표 버튼
+function NextArrow(props) {
+ const { className, style, onClick } = props;
+ const windowWidth = useWindowSize();
+ if (windowWidth < 1280) return null;
+ return (
+
+ );
+}
+
+//슬라이더 함수 세팅값
+const settings = {
+ infinite: false,
+ speed: 500,
+ slidesToShow: 8,
+ slidesToScroll: 2,
+ rows: 2,
+ prevArrow: ,
+ nextArrow: ,
+ responsive: [
+ {
+ breakpoint: 1280,
+ settings: {
+ slidesToShow: 6,
+ },
+ },
+ {
+ breakpoint: 1024,
+ settings: {
+ slidesToShow: 4,
+ },
+ },
+ {
+ breakpoint: 768,
+ settings: {
+ slidesToShow: 4,
+ },
+ },
+ {
+ breakpoint: 585,
+ settings: {
+ slidesToShow: 3,
+ },
+ },
+ ],
+};
+
+//슬라이드 2줄 사이 간격
+const slideStyle = css`
+ .slick-slide > div {
+ padding-top: 16px;
+ padding-bottom: 16px;
+ }
+`;
+
+const Mypage = () => {
+ const windowWidth = useWindowSize();
+ const isMobile = windowWidth <= 425;
+
+ // api에서 온 아이돌을 담을 idols
+ const [idols, setIdols] = useState([]);
+ // 선택한 아이돌 담는 임시 statechl
+ const [checkedIdol, setCheckedIdol] = useState([]);
+ // 내가 선택한 아이돌 (id만 저장)
+ const [myIdol, setMyIdol] = useState([]);
+ // 내가 선택한 아이돌과 같은 아이디를 같는 요소를 새롭게 만들어줌
+ const selectedIdolList = idols.filter((idol) => myIdol.includes(idol.id));
+ // idols 목록에서 선택된 아이돌을 제외한 남은 아이돌
+ const remainIdols = idols.filter((idol) => !myIdol.includes(idol.id));
+
+ // 처음에 한 번 idols 불러오기
+ useEffect(() => {
+ const fetchData = async () => {
+ const result = await idolsAPI.getIdols(30); // 불러올 개수
+ const idollist = result.list; // api에서 list만 가져오기
+ setIdols(idollist);
+ //에러처리
+ };
+ fetchData();
+ }, []);
+
+ // 아이돌 선택하는 함수
+ const toggleCheckedIdol = (idolId) => {
+ setCheckedIdol((prev) => {
+ const updated = prev.includes(idolId)
+ ? prev.filter((id) => id !== idolId)
+ : [...checkedIdol, idolId];
+ console.log(updated);
+ return updated;
+ });
+ };
+
+ // 로컬에 저장된 myIdol 불러오기
+ useEffect(() => {
+ const stored = localStorage.getItem("myIdols");
+ if (stored) {
+ setMyIdol(JSON.parse(stored));
+ }
+ }, []);
+
+ // 추가하기 클릭시 추가된 아이돌 로컬에 저장하는 함수
+ const handleAddIdol = () => {
+ if (checkedIdol.length === 0) {
+ alert("아이돌이 선택되지 않았습니다."); // 그냥 추가하기 눌렀을 경우 보여줄 것
+ return;
+ }
+ setMyIdol((prev) => {
+ const updated = [...prev, ...checkedIdol];
+ console.log("추가된 리스트", updated);
+ localStorage.setItem("myIdols", JSON.stringify(updated)); // ==> set 함수 안에 로컬은 분리하는게 좋다(추후)
+ return updated;
+ });
+ setCheckedIdol([]); // 선택된 아이돌 초기화
+ };
+
+ const handleRemoveIdol = (idol) => {
+ setMyIdol((prev) => {
+ const updated = prev.filter((id) => id !== idol.id);
+ // console.log("없애기");
+ localStorage.setItem("myIdols", JSON.stringify(updated));
+ return updated;
+ });
+ setCheckedIdol((prev) => [...prev, idol]);
+ };
+
+ return (
+
+
+
내가 선택한 아이돌
+ {selectedIdolList.length === 0 && (
+
아직 아이돌이 선택되지 않았습니다
+ )}
+
+ {selectedIdolList.map((idol) => (
+ handleRemoveIdol(idol)}
+ />
+ ))}
+
+
+
+
관심있는 아이돌을 추가해보세요!
+ {/* 슬라이더 사용 */}
+
+ {remainIdols.map((idol) => (
+ // biome-ignore lint/a11y/useKeyWithClickEvents: .
+ toggleCheckedIdol(idol.id)}>
+
+
+ ))}
+
+
+
+
+ 추가하기
+
+
+
+
+ );
+};
+
+export default Mypage;
diff --git a/src/pages/Mypage/slider/slick-theme.css b/src/pages/Mypage/slider/slick-theme.css
new file mode 100644
index 0000000..4ed2955
--- /dev/null
+++ b/src/pages/Mypage/slider/slick-theme.css
@@ -0,0 +1,175 @@
+/* Icons */
+@font-face {
+ font-family: "slick";
+ font-weight: normal;
+ font-style: normal;
+
+ src: url("./fonts/slick.eot");
+ src: url("./fonts/slick.eot?#iefix") format("embedded-opentype"),
+ url("./fonts/slick.woff") format("woff"), url("./fonts/slick.ttf")
+ format("truetype"), url("./fonts/slick.svg#slick") format("svg");
+}
+/* Arrows */
+.slick-prev,
+.slick-next {
+ font-size: 0;
+ line-height: 0;
+
+ position: absolute;
+ top: 50%;
+
+ display: block;
+
+ width: 20px;
+ height: 20px;
+ padding: 0;
+ -webkit-transform: translate(0, -50%);
+ -ms-transform: translate(0, -50%);
+ transform: translate(0, -50%);
+
+ cursor: pointer;
+
+ color: transparent;
+ border: none;
+ outline: none;
+ background: transparent;
+}
+.slick-prev:hover,
+.slick-prev:focus,
+.slick-next:hover,
+.slick-next:focus {
+ color: transparent;
+ outline: none;
+ background: transparent;
+}
+.slick-prev:hover:before,
+.slick-prev:focus:before,
+.slick-next:hover:before,
+.slick-next:focus:before {
+ opacity: 1;
+}
+.slick-prev.slick-disabled:before,
+.slick-next.slick-disabled:before {
+ opacity: 0.25;
+}
+
+.slick-prev:before,
+.slick-next:before {
+ font-size: 20px;
+ line-height: 1;
+
+ opacity: 0.75;
+ color: white;
+
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+.slick-prev {
+ left: -25px;
+}
+[dir="rtl"] .slick-prev {
+ right: -25px;
+ left: auto;
+}
+.slick-prev:before {
+ content: "←";
+}
+[dir="rtl"] .slick-prev:before {
+ content: "→";
+}
+
+.slick-next {
+ right: -25px;
+}
+[dir="rtl"] .slick-next {
+ right: auto;
+ left: -25px;
+}
+.slick-next:before {
+ content: "→";
+}
+[dir="rtl"] .slick-next:before {
+ content: "←";
+}
+
+/* Dots */
+.slick-dotted.slick-slider {
+ margin-bottom: 30px;
+}
+
+.slick-dots {
+ position: absolute;
+ bottom: -25px;
+
+ display: block;
+
+ width: 100%;
+ padding: 0;
+ margin: 0;
+
+ list-style: none;
+
+ text-align: center;
+}
+.slick-dots li {
+ position: relative;
+
+ display: inline-block;
+
+ width: 20px;
+ height: 20px;
+ margin: 0 5px;
+ padding: 0;
+
+ cursor: pointer;
+}
+.slick-dots li button {
+ font-size: 0;
+ line-height: 0;
+
+ display: block;
+
+ width: 20px;
+ height: 20px;
+ padding: 5px;
+
+ cursor: pointer;
+
+ color: transparent;
+ border: 0;
+ outline: none;
+ background: transparent;
+}
+.slick-dots li button:hover,
+.slick-dots li button:focus {
+ outline: none;
+}
+.slick-dots li button:hover:before,
+.slick-dots li button:focus:before {
+ opacity: 1;
+}
+.slick-dots li button:before {
+ font-size: 6px;
+ line-height: 20px;
+
+ position: absolute;
+ top: 0;
+ left: 0;
+
+ width: 20px;
+ height: 20px;
+
+ content: "•";
+ text-align: center;
+
+ opacity: 0.25;
+ color: black;
+
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+.slick-dots li.slick-active button:before {
+ opacity: 0.75;
+ color: black;
+}
diff --git a/src/pages/Mypage/slider/slick.css b/src/pages/Mypage/slider/slick.css
new file mode 100644
index 0000000..cd88772
--- /dev/null
+++ b/src/pages/Mypage/slider/slick.css
@@ -0,0 +1,101 @@
+/* Slider */
+.slick-slider {
+ position: relative;
+
+ display: block;
+ box-sizing: border-box;
+
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+
+ -webkit-touch-callout: none;
+ -ms-touch-action: pan-y;
+ touch-action: pan-y;
+ -webkit-tap-highlight-color: transparent;
+}
+
+.slick-list {
+ position: relative;
+
+ display: block;
+ overflow: hidden;
+
+ margin: 0;
+ padding: 0;
+}
+.slick-list:focus {
+ outline: none;
+}
+.slick-list.dragging {
+ cursor: pointer;
+ cursor: hand;
+}
+
+.slick-slider .slick-track,
+.slick-slider .slick-list {
+ -webkit-transform: translate3d(0, 0, 0);
+ -moz-transform: translate3d(0, 0, 0);
+ -ms-transform: translate3d(0, 0, 0);
+ -o-transform: translate3d(0, 0, 0);
+ transform: translate3d(0, 0, 0);
+}
+
+.slick-track {
+ position: relative;
+ top: 0;
+ left: 0;
+
+ display: block;
+ margin-left: auto;
+ margin-right: auto;
+}
+.slick-track:before,
+.slick-track:after {
+ display: table;
+
+ content: "";
+}
+.slick-track:after {
+ clear: both;
+}
+.slick-loading .slick-track {
+ visibility: hidden;
+}
+
+.slick-slide {
+ display: none;
+ float: left;
+
+ height: 100%;
+ min-height: 1px;
+}
+[dir="rtl"] .slick-slide {
+ float: right;
+}
+.slick-slide img {
+ display: block;
+}
+.slick-slide.slick-loading img {
+ display: none;
+}
+.slick-slide.dragging img {
+ pointer-events: none;
+}
+.slick-initialized .slick-slide {
+ display: block;
+}
+.slick-loading .slick-slide {
+ visibility: hidden;
+}
+.slick-vertical .slick-slide {
+ display: block;
+
+ height: auto;
+
+ border: 1px solid transparent;
+}
+.slick-arrow.slick-hidden {
+ display: none;
+}
diff --git a/src/pages/NotFound/NotFound.style.js b/src/pages/NotFound/NotFound.style.js
new file mode 100644
index 0000000..1f9888c
--- /dev/null
+++ b/src/pages/NotFound/NotFound.style.js
@@ -0,0 +1,114 @@
+/** @jsxImportSource @emotion/react */
+import { css } from "@emotion/react";
+
+export const container = css`
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: space-around;
+
+ height: 100vh;
+ overflow-y: auto;
+ `;
+
+export const imgContainer = css`
+position: relative;
+width: 350px;
+height: 350px;
+
+@media(max-width: 769px) {
+ width: 200px;
+ height: 200px;
+}`;
+
+export const imgIdol = css`
+position: relative;
+width: 100%;
+height: 100%;
+opacity: 0.7;
+
+object-fit: cover;
+`;
+
+export const overlay = css`
+position: absolute;
+top: 0;
+left: 0;
+width: 100%;
+height: 100%;
+background: radial-gradient(
+ 50% 50% at 50% 50%,
+ rgba(2, 0, 14, 0) 0%,
+ rgba(2, 0, 14, 0.18) 37.5%,
+ rgba(2, 0, 14, 0.5) 79.5%,
+ #02000e 100%
+ );
+ `;
+
+export const text = css`
+ color: #ffffff;
+ text-align: center;
+
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+
+ gap: 30px;
+
+ @media(max-width: 769px) {
+ gap: 20px;
+ }
+ `;
+
+export const logo = css`
+ width: 200px;
+
+ opacity: 0.5;
+
+ z-index: 3;
+
+@media(max-width: 769px) {
+ width: 100px;
+}`;
+
+export const title = css`
+font-size: 30px;
+font-weight: 800;
+
+@media(max-width: 769px) {
+ font-size: 20px;
+ font-weight: 600;
+}`;
+
+export const description = css`
+font-size: 15px;
+color: var(--gray-A3A5A8);
+
+@media(max-width: 769px) {
+ font-size: 13px;
+}`;
+
+export const buttons = css`
+display: flex;
+gap: 30px;
+`;
+
+export const button = css`
+width: 150px;
+height: 40px;
+border: 1px solid #ffffff;
+border-radius: 30px;
+
+font-size: 15px;
+
+&:hover {
+background: linear-gradient(90deg, #F86F65 0%, #FE5493 100%);
+border: none;
+}
+
+@media(max-width: 769px) {
+ width: 110px;
+ height: 30px;
+
+ font-size: 12px;
+}`;
diff --git a/src/pages/NotFound/index.jsx b/src/pages/NotFound/index.jsx
new file mode 100644
index 0000000..3ab066b
--- /dev/null
+++ b/src/pages/NotFound/index.jsx
@@ -0,0 +1,67 @@
+import BLACKPINK from "/images/BLACKPINK.jpg";
+import BoyNextDoor from "/images/BoyNextDoor.jpg";
+import IVE from "/images/IVE.jpg";
+import NMIX from "/images/NMIX.jpg";
+import NewJeans from "/images/NewJeans.jpg";
+import SHINEE from "/images/SHINEE.jpg";
+import Seventeen from "/images/Seventeen.jpg";
+import aespa from "/images/aespa.jpg";
+import Logo from "/images/landing/logo.svg";
+
+/** @jsxImportSource @emotion/react */
+import {
+ button,
+ buttons,
+ container,
+ description,
+ imgContainer,
+ imgIdol,
+ logo,
+ overlay,
+ text,
+ title,
+} from "./NotFound.style";
+
+import { useNavigate } from "react-router-dom";
+
+function NotFound() {
+ const navigate = useNavigate();
+
+ const imgSrc = [
+ SHINEE,
+ IVE,
+ NMIX,
+ NewJeans,
+ Seventeen,
+ aespa,
+ BLACKPINK,
+ BoyNextDoor,
+ ];
+
+ const randomImg = imgSrc[Math.floor(Math.random() * imgSrc.length)];
+
+ return (
+
+
+
+
+
+
+
+
페이지를 찾을 수 없습니다.
+
입력하신 주소가 맞는지 다시 확인해주세요.
+
+
+
+ navigate(-1)} type="button">
+ 이전 페이지로
+
+ navigate("/")} type="button">
+ 랜딩 페이지로
+
+
+
+ );
+}
+
+export default NotFound;
diff --git a/src/pages/Testpage/index.jsx b/src/pages/Testpage/index.jsx
new file mode 100644
index 0000000..c194f51
--- /dev/null
+++ b/src/pages/Testpage/index.jsx
@@ -0,0 +1,176 @@
+/** @jsxImportSource @emotion/react */
+import { css } from "@emotion/react";
+import { useCallback, useEffect, useState } from "react";
+import { toast } from "react-toastify";
+import { idolsAPI } from "../../apis/idolsAPI";
+import Button from "../../components/Button/Button";
+import Modal from "../../components/Modal";
+import RadioButton from "../../components/RadioButton";
+import { useCredit } from "../../context/CreditContext";
+const Testpage = () => {
+ const { credit, addCredit, deductCredit } = useCredit(); // credit context test를 위한 선언
+ const credits = [100, 500, 1000]; // 라디오 버튼 test를 위한 크레딧 배열
+ const [select, setSelect] = useState(null); // 라디오 버튼 test를 위한 state설정 (현재 선택된 값을 저장하는 state)
+ const [isOpen, setIsOpen] = useState(false);
+
+ const handleClose = () => {
+ setIsOpen(false);
+ };
+
+ const handleOpen = () => {
+ setIsOpen(true);
+ };
+
+ const [totalList, setTotalList] = useState([]);
+ const fetchIdols = useCallback(async () => {
+ const response = await idolsAPI.getIdols(100);
+ setTotalList(response.list);
+ }, []);
+
+ useEffect(() => {
+ fetchIdols();
+ }, [fetchIdols]);
+
+ return (
+
+ {/* Toast 사용법
+ * 상단에 import { toast } from "react-toastify"; 선언
+ * 성공일 때 toast.success("나올 문구")
+ * 실패일 때 toast.error("나올 문구")
+ */}
+
toast.success("이렇게 테스트!")}>
+ 성공일 때
+
+
toast.error("애라다 애라!")}>
+ 실패일 때
+
+
+
모달 테스트
+
+ 모달 열기
+
+
+
+ 후원하기
+
+
+ 후원하기
+
+
+
+ 여기에 모달 내용이 들어갑니다.
+
+
+
+ {totalList.map((item) => (
+
+
+
+ ))}
+
+
+ {totalList.map((item) => (
+
+
+
+ ))}
+
+
+
+ {credits.map((credit) => (
+
+ {credit}
+
+ ))}
+ {/* 클래스 적용 확인을 위한 test 같은 credit 배열을 사용해서 같이 작동되는 것입니다! */}
+ {credits.map((credit) => (
+
+ {credit}
+
+ ))}
+
+
+ {/* 크레딧 context test */}
+
+ addCredit(1000)}>
+ 크레딧 충전
+
+ deductCredit(100)}>
+ 크레딧 사용
+
+ 현재 크레딧: {credit}
+
+
+ );
+};
+
+// 라디오버튼 test를위한 css
+const radio = css`
+ color: #ffffff;
+`;
+
+// 크레딧 test를 위한 css
+const creditStyle = css`
+ color: #ffffff;
+`;
+
+const creditContainer = css`
+ display: flex;
+ gap: 24px;
+`;
+
+// API 테스트를 위한 css
+const testIdolsCircle = css`
+ width: 980px;
+ margin: 0 auto;
+ display: flex;
+ flex-wrap: wrap;
+ gap: 10px;
+ > div {
+ width: 80px;
+ height: 80px;
+ border-radius: 50%;
+ overflow: hidden;
+ border: 2px solid var(--orange-F96D69);
+ padding: 5px;
+ > img {
+ width: 100%;
+ height: 100%;
+ border-radius: 50%;
+ object-fit: cover;
+ object-position: center top;
+ }
+ }
+`;
+
+const testIdolsDonation = css`
+ display: grid;
+ grid-template-columns: repeat(5, 1fr);
+ gap: 20px;
+ width: 1920px;
+ margin: 0 auto;
+ > div {
+ height: 400px;
+ overflow: hidden;
+ > img {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+ object-position: center top;
+ }
+ }
+`;
+
+export default Testpage;
diff --git a/src/styles/commonStyles.jsx b/src/styles/commonStyles.jsx
new file mode 100644
index 0000000..e4f4a0b
--- /dev/null
+++ b/src/styles/commonStyles.jsx
@@ -0,0 +1,39 @@
+import { Global, css } from "@emotion/react";
+
+const commonStyles = css`
+ .hide-default {
+ display: none !important;
+ }
+ :root {
+ --orange-F96D69: #f96d69;
+ --pink-FE5493: #fe5493;
+ --black-02000E: #02000e;
+ --black-181D26: #181d26;
+ --gray-67666E: #67666e;
+ --gray-828282: #828282;
+ --gray-8C92AB: #8c92ab;
+ --gray-A3A5A8: #a3a5a8;
+ --white-F7F7F8: #f7f7f8;
+ }
+ @media all and (max-width: 768px) {
+ .hide-768 {
+ display: none !important;
+ }
+ .show-768 {
+ display: block !important;
+ }
+ }
+
+ @media all and (max-width: 425px) {
+ .hide-425 {
+ display: none !important;
+ }
+ .show-425 {
+ display: block !important;
+ }
+ }
+`;
+
+export default function CommonStyles() {
+ return ;
+}
diff --git a/src/styles/layoutStyles.jsx b/src/styles/layoutStyles.jsx
new file mode 100644
index 0000000..f1f6472
--- /dev/null
+++ b/src/styles/layoutStyles.jsx
@@ -0,0 +1,54 @@
+import { Global, css } from "@emotion/react";
+
+export const layoutStyles = css`
+ .mainGrid {
+ width: 1200px;
+ position: relative;
+ margin-inline: auto;
+ }
+
+ .landingGrid {
+ width: 1800px;
+ position: relative;
+ margin-inline: auto;
+ }
+
+ @media all and (max-width: 1900px) {
+ .landingGrid {
+ width: auto;
+ max-width: none;
+ margin-inline: 24px;
+ }
+ }
+
+ @media all and (max-width: 1300px) {
+ .mainGrid,
+ .landingGrid {
+ width: auto;
+ max-width: none;
+ margin-inline: 24px;
+ }
+ }
+
+ @media all and (max-width: 768px) {
+ .mainGrid,
+ .landingGrid {
+ width: auto;
+ max-width: none;
+ margin-inline: 3.23vw;
+ }
+ }
+
+ @media all and (max-width: 425px) {
+ .mainGrid,
+ .landingGrid {
+ width: auto;
+ max-width: none;
+ margin-inline: 6.42vw;
+ }
+ }
+`;
+
+export default function LayoutStyles() {
+ return ;
+}
diff --git a/src/styles/resetStyles.jsx b/src/styles/resetStyles.jsx
new file mode 100644
index 0000000..f5cec9a
--- /dev/null
+++ b/src/styles/resetStyles.jsx
@@ -0,0 +1,216 @@
+import { Global, css } from "@emotion/react";
+
+const resetStyles = css`
+ :root {
+ --orange-F96D69: #f96d69;
+ --pink-FE5493: #fe5493;
+ --black-02000E: #02000e;
+ --black-181D26: #181d26;
+ --gray-67666E: #67666e;
+ --gray-828282: #828282;
+ --gray-8C92AB: #8c92ab;
+ --gray-A3A5A8: #a3a5a8;
+ --white-F7F7F8: #f7f7f8;
+ }
+
+ html,
+ body,
+ div,
+ span,
+ applet,
+ object,
+ iframe,
+ h1,
+ h2,
+ h3,
+ h4,
+ h5,
+ h6,
+ p,
+ blockquote,
+ pre,
+ a,
+ abbr,
+ acronym,
+ address,
+ big,
+ cite,
+ code,
+ del,
+ dfn,
+ em,
+ font,
+ img,
+ ins,
+ kbd,
+ q,
+ s,
+ samp,
+ small,
+ strike,
+ strong,
+ sub,
+ sup,
+ tt,
+ var,
+ b,
+ u,
+ i,
+ center,
+ p,
+ dl,
+ dt,
+ dd,
+ ol,
+ ul,
+ li,
+ fieldset,
+ form,
+ label,
+ legend {
+ margin: 0;
+ padding: 0;
+ border: 0;
+ vertical-align: baseline;
+ background: transparent;
+ box-sizing: border-box;
+ word-break: keep-all;
+ }
+
+ html {
+ overflow-y: scroll;
+ -webkit-text-size-adjust: 100%;
+ }
+
+ html,
+ body {
+ width: 100%;
+ font-size: 14px;
+ line-height: 1.4em;
+ margin: 0 auto;
+ background-color: var(--black-02000E);
+ }
+
+ body {
+ font-size: 1rem;
+ font-family: 'Pretendard', sans-serif;
+ color: var(--black-02000E);
+ }
+
+ figure {
+ display: block;
+ margin: 0;
+ padding: 0;
+ }
+
+ select,
+ input,
+ textarea,
+ button {
+ font-size: 1em;
+ font-family: 'Pretendard', sans-serif;
+ vertical-align: middle;
+ color: var(--white-F7F7F8);
+ background: transparent;
+ box-sizing: border-box;
+ border: none;
+ }
+
+ button,
+ a {
+ cursor: pointer;
+ padding: 0;
+ }
+
+ a {
+ color: var(--white-F7F7F8);
+ text-decoration: none;
+ }
+
+ input[type='text'],
+ input[type='password'] {
+ appearance: none;
+ -webkit-appearance: none;
+ -moz-appearance: none;
+ }
+
+ textarea {
+ appearance: none;
+ -webkit-appearance: none;
+ -moz-appearance: none;
+ -webkit-overflow-scrolling: touch;
+ -moz-overflow-scrolling: touch;
+ -o-overflow-scrolling: touch;
+ }
+
+ h1,
+ h2,
+ h3,
+ h4,
+ h5,
+ h6 {
+ font-size: 100%;
+ font-weight: normal;
+ }
+
+ input {
+ padding: 0;
+ margin: 0;
+ }
+
+ table {
+ border-collapse: collapse;
+ border-spacing: 0;
+ vertical-align: top;
+ }
+
+ th {
+ font-size: 1em;
+ }
+
+ img,
+ fieldset {
+ border: 0;
+ }
+
+ img {
+ display: block;
+ }
+
+ ol,
+ ul {
+ list-style: none;
+ }
+
+ hr {
+ border: 0;
+ height: 1px;
+ background: #ddd;
+ }
+
+ label {
+ cursor: pointer;
+ }
+
+ legend,
+ caption {
+ width: 0;
+ height: 0;
+ overflow: hidden;
+ visibility: hidden;
+ font-size: 0;
+ line-height: 0;
+ }
+
+ em {
+ font-style: normal;
+ }
+
+ ins {
+ text-decoration: none;
+ }
+`;
+
+export default function ResetStyles() {
+ return ;
+}
diff --git a/src/styles/skeletonAnimation.jsx b/src/styles/skeletonAnimation.jsx
new file mode 100644
index 0000000..a45fb2f
--- /dev/null
+++ b/src/styles/skeletonAnimation.jsx
@@ -0,0 +1,38 @@
+/** @jsxImportSource @emotion/react */
+import { css, keyframes } from "@emotion/react";
+
+// 쉐이더 애니메이션
+export const shimmer = keyframes`
+ 0% {
+ background-position: 100%;
+ }
+ 100% {
+ background-position: -100%;
+ }
+`;
+
+// 기본 색상
+export const baseColor = "#181D26";
+export const highlightColor = "rgba(255, 255, 255, 0.12)";
+
+// 공통 스타일
+export const shimmerStyle = css`
+ background: ${baseColor};
+ background-image: linear-gradient(
+ 90deg,
+ ${baseColor} 0%,
+ ${highlightColor} 40%,
+ ${highlightColor} 60%,
+ ${baseColor} 100%
+ );
+ background-size: 200% 100%;
+ background-repeat: no-repeat;
+
+ animation-name: ${shimmer};
+ animation-duration: 2.5s;
+ animation-timing-function: linear;
+ animation-iteration-count: infinite;
+ animation-delay: 0s;
+ animation-fill-mode: forwards;
+ animation-play-state: running;
+`;
diff --git a/src/utils/infiniteCarousel.js b/src/utils/infiniteCarousel.js
new file mode 100644
index 0000000..ced32aa
--- /dev/null
+++ b/src/utils/infiniteCarousel.js
@@ -0,0 +1,116 @@
+import gsap from "gsap";
+import { ScrollTrigger } from "gsap/ScrollTrigger";
+
+gsap.registerPlugin(ScrollTrigger);
+
+export const infiniteCarousel = ({
+ trigger,
+ duration,
+ reverse,
+ pauseOnHover,
+ direction = "vertical",
+}) => {
+ const triggers = document.querySelectorAll(trigger);
+ const timelines = [];
+
+ for (const trigger of triggers) {
+ // 컨테이너 스타일 초기화
+ trigger.style.overflow = "hidden";
+ trigger.style.visibility = "visible";
+
+ const items = trigger.querySelector(".carousel-items") || trigger;
+ const itemElements = trigger.querySelectorAll(".item");
+ const item = [...itemElements];
+
+ // 방향에 따른 크기 계산
+ const itemSizeArr = item.map((item) => {
+ item.style.position = "absolute";
+ return direction === "vertical" ? item.clientHeight : item.clientWidth;
+ });
+
+ let totalSize = 0;
+
+ // 아이템 위치 계산 및 설정
+ for (const [idx, size] of itemSizeArr.entries()) {
+ if (idx === 0) {
+ totalSize = itemSizeArr[itemSizeArr.length - 1];
+ } else if (itemSizeArr[idx - 1]) {
+ totalSize = totalSize + itemSizeArr[idx - 1];
+ }
+
+ // 방향에 따른 위치 설정
+ if (direction === "vertical") {
+ gsap.set(item[idx], {
+ y: totalSize,
+ });
+ } else {
+ gsap.set(item[idx], {
+ x: totalSize,
+ });
+ }
+ }
+
+ // 컨테이너 스타일 설정
+ items.style.position = "relative";
+ if (direction === "vertical") {
+ items.style.width = `${item[0].offsetWidth}px`;
+ items.style.top = `-${Math.max(...itemSizeArr)}px`;
+ } else {
+ items.style.height = `${item[0].offsetHeight}px`;
+ items.style.left = `-${Math.max(...itemSizeArr)}px`;
+ }
+
+ // 애니메이션 설정
+ const tl = gsap.timeline();
+ timelines.push(tl);
+
+ const animationProps = {
+ ease: "none",
+ repeat: -1,
+ };
+
+ // 방향에 따른 애니메이션 속성 설정
+ if (direction === "vertical") {
+ animationProps.y = () => (reverse ? `-=${totalSize}` : `+=${totalSize}`);
+ animationProps.modifiers = {
+ y: gsap.utils.unitize((y) => {
+ return reverse
+ ? y < 0
+ ? (Number.parseFloat(y) % totalSize) + totalSize
+ : y
+ : Number.parseFloat(y) % totalSize;
+ }),
+ };
+ } else {
+ animationProps.x = () => (reverse ? `-=${totalSize}` : `+=${totalSize}`);
+ animationProps.modifiers = {
+ x: gsap.utils.unitize((x) => {
+ return reverse
+ ? x < 0
+ ? (Number.parseFloat(x) % totalSize) + totalSize
+ : x
+ : Number.parseFloat(x) % totalSize;
+ }),
+ };
+ }
+
+ // 애니메이션 실행
+ tl.to(item, duration * item.length, animationProps);
+
+ // 호버 이벤트 설정
+ if (pauseOnHover) {
+ const handleMouseOver = () => tl.pause();
+ const handleMouseLeave = () => tl.play();
+
+ trigger.addEventListener("mouseover", handleMouseOver);
+ trigger.addEventListener("mouseleave", handleMouseLeave);
+ }
+ }
+
+ // cleanup 함수 반환
+ return () => {
+ for (const tl of timelines) {
+ tl.kill();
+ }
+ };
+};
diff --git a/src/utils/postPosition.js b/src/utils/postPosition.js
new file mode 100644
index 0000000..02aecff
--- /dev/null
+++ b/src/utils/postPosition.js
@@ -0,0 +1,15 @@
+export default function withPostPosition(word, josa) {
+ if (!word) return "";
+ const lastChar = word[word.length - 1];
+ const code = lastChar.charCodeAt(0);
+ const hasBatchim = (code - 44032) % 28 !== 0;
+
+ const josaMap = {
+ 은는: hasBatchim ? "은" : "는",
+ 이가: hasBatchim ? "이" : "가",
+ 을를: hasBatchim ? "을" : "를",
+ 으로: hasBatchim ? "으로" : "로",
+ };
+
+ return word + (josaMap[josa] || "");
+}
diff --git a/vite.config.js b/vite.config.js
new file mode 100644
index 0000000..3e31e20
--- /dev/null
+++ b/vite.config.js
@@ -0,0 +1,13 @@
+import path from "node:path";
+import react from "@vitejs/plugin-react";
+import { defineConfig } from "vite";
+
+// https://vite.dev/config/
+export default defineConfig({
+ plugins: [react()],
+ resolve: {
+ alias: {
+ "@": path.resolve(__dirname, "./src"), // '@' → src 폴더
+ },
+ },
+});