Deployment Bot commited on
Commit
c219bde
Β·
1 Parent(s): eb576ef

Update: ChatWidget, MyProfile, resume parser, CSS and backend changes

Browse files
backend/main.py CHANGED
@@ -1,5 +1,5 @@
1
  import math
2
- from fastapi import FastAPI, HTTPException, Request
3
  from fastapi.staticfiles import StaticFiles
4
  from fastapi.responses import FileResponse
5
  from fastapi.middleware.cors import CORSMiddleware
@@ -1088,6 +1088,99 @@ async def update_student(student_id: str, req: StudentUpdate):
1088
  })
1089
 
1090
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1091
  # ─── Verification Layer (anti-fraud / self-report bias) ─────────────────────
1092
 
1093
  class AcademicVerifyReq(BaseModel):
 
1
  import math
2
+ from fastapi import FastAPI, HTTPException, Request, UploadFile, File, Form
3
  from fastapi.staticfiles import StaticFiles
4
  from fastapi.responses import FileResponse
5
  from fastapi.middleware.cors import CORSMiddleware
 
1088
  })
1089
 
1090
 
1091
+ # ─── Resume Parser + ATS Scorer ─────────────────────────────────────────────
1092
+
1093
+ JOB_PROFILES = [
1094
+ "Software Engineer – Backend",
1095
+ "Software Engineer – Frontend / Full-Stack",
1096
+ "Data Analyst",
1097
+ "Data Scientist / ML Engineer",
1098
+ "Business Analyst",
1099
+ "Product Manager",
1100
+ "Finance Analyst / Investment Banking",
1101
+ "Marketing Manager",
1102
+ "Human Resources Manager",
1103
+ "Operations Manager",
1104
+ "Supply Chain Analyst",
1105
+ "Healthcare Administrator",
1106
+ "Clinical Research Associate",
1107
+ "Embedded Systems / Hardware Engineer",
1108
+ "DevOps / Cloud Engineer",
1109
+ ]
1110
+
1111
+
1112
+ @app.get("/api/v1/resume/job-profiles")
1113
+ async def get_job_profiles():
1114
+ """Return the list of job profiles available for ATS scoring."""
1115
+ return {"profiles": JOB_PROFILES}
1116
+
1117
+
1118
+ @app.post("/api/v1/student/{student_id}/resume/parse")
1119
+ async def resume_parse(student_id: str, file: UploadFile = File(...)):
1120
+ """Upload a PDF or DOCX resume, extract text, and return structured profile
1121
+ fields that the borrower can review and apply to their profile."""
1122
+ from resume_parser import extract_text, parse_resume as _parse
1123
+
1124
+ allowed = {
1125
+ "application/pdf",
1126
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
1127
+ "application/msword",
1128
+ "text/plain",
1129
+ }
1130
+ ct = (file.content_type or "").lower()
1131
+ if ct not in allowed and not ct.startswith("application/pdf"):
1132
+ raise HTTPException(
1133
+ status_code=415,
1134
+ detail="Unsupported file type. Upload a PDF, DOCX, or plain-text resume.",
1135
+ )
1136
+
1137
+ raw = await file.read()
1138
+ if len(raw) > 5 * 1024 * 1024: # 5 MB hard cap
1139
+ raise HTTPException(status_code=413, detail="File too large (max 5 MB).")
1140
+
1141
+ try:
1142
+ resume_text = extract_text(raw, ct)
1143
+ except ValueError as exc:
1144
+ raise HTTPException(status_code=422, detail=str(exc))
1145
+
1146
+ parsed = _parse(resume_text)
1147
+ if "_error" in parsed and "parsed" not in parsed:
1148
+ raise HTTPException(status_code=502, detail=f"Parse failed: {parsed['_error']}")
1149
+
1150
+ return sanitize({
1151
+ "student_id": student_id,
1152
+ "resume_text": resume_text, # returned so ATS call reuses it (no re-upload)
1153
+ "char_count": len(resume_text),
1154
+ "parsed": parsed,
1155
+ })
1156
+
1157
+
1158
+ class ATSRequest(BaseModel):
1159
+ resume_text: str
1160
+ job_profile: str
1161
+
1162
+
1163
+ @app.post("/api/v1/student/{student_id}/resume/ats-score")
1164
+ async def resume_ats_score(student_id: str, req: ATSRequest):
1165
+ """Score a resume (as plain text) against a named job profile and return
1166
+ keyword match, skills alignment, experience relevance, and recommendations."""
1167
+ from resume_parser import ats_score as _ats
1168
+
1169
+ if req.job_profile not in JOB_PROFILES:
1170
+ raise HTTPException(
1171
+ status_code=400,
1172
+ detail=f"Unknown job profile. Choose from: {', '.join(JOB_PROFILES)}",
1173
+ )
1174
+ if not req.resume_text.strip():
1175
+ raise HTTPException(status_code=400, detail="resume_text is empty.")
1176
+
1177
+ result = _ats(req.resume_text, req.job_profile)
1178
+ if "_error" in result and result.get("overall_score", 0) == 0:
1179
+ raise HTTPException(status_code=502, detail=f"ATS scoring failed: {result['_error']}")
1180
+
1181
+ return sanitize({"student_id": student_id, **result})
1182
+
1183
+
1184
  # ─── Verification Layer (anti-fraud / self-report bias) ─────────────────────
1185
 
1186
  class AcademicVerifyReq(BaseModel):
backend/requirements.txt CHANGED
@@ -16,3 +16,6 @@ httpx>=0.27.0
16
  pytrends>=4.9.0
17
  kagglehub>=0.3.0
18
  pytest>=8.0.0
 
 
 
 
16
  pytrends>=4.9.0
17
  kagglehub>=0.3.0
18
  pytest>=8.0.0
19
+ pdfplumber>=0.11.0
20
+ python-docx>=1.1.0
21
+ python-multipart>=0.0.9
backend/resume_parser.py ADDED
@@ -0,0 +1,257 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Resume text extraction (PDF / DOCX) + LLM-based profile parsing + ATS scoring.
3
+
4
+ Two public functions:
5
+ parse_resume(resume_text) -> dict of profile fields (maps to MyProfile tabs)
6
+ ats_score(resume_text, job_profile) -> dict with overall_score, breakdown, gaps, recs
7
+ """
8
+ from __future__ import annotations
9
+ import io
10
+ import json
11
+ import os
12
+ import sys
13
+
14
+ # Add agents/ to path so `from provider import call_llm` resolves correctly.
15
+ _AGENTS_DIR = os.path.join(os.path.dirname(__file__), "agents")
16
+ if _AGENTS_DIR not in sys.path:
17
+ sys.path.insert(0, _AGENTS_DIR)
18
+
19
+ from provider import call_llm # noqa: E402 (path manipulation above)
20
+
21
+
22
+ # ─── Text extraction ──────────────────────────────────────────────────────────
23
+
24
+ def extract_text(file_bytes: bytes, content_type: str) -> str:
25
+ ct = (content_type or "").lower()
26
+ if "pdf" in ct:
27
+ return _from_pdf(file_bytes)
28
+ if "word" in ct or "docx" in ct or "openxmlformats" in ct:
29
+ return _from_docx(file_bytes)
30
+ # Fall back to plain-text decode (txt / unknown)
31
+ return file_bytes.decode("utf-8", errors="replace")
32
+
33
+
34
+ def _from_pdf(data: bytes) -> str:
35
+ try:
36
+ import pdfplumber
37
+ parts: list[str] = []
38
+ with pdfplumber.open(io.BytesIO(data)) as pdf:
39
+ for page in pdf.pages:
40
+ t = page.extract_text()
41
+ if t:
42
+ parts.append(t)
43
+ text = "\n".join(parts)
44
+ if not text.strip():
45
+ raise ValueError("No text found β€” the PDF may be image-only (scanned).")
46
+ return text
47
+ except Exception as exc:
48
+ raise ValueError(f"PDF extraction failed: {exc}") from exc
49
+
50
+
51
+ def _from_docx(data: bytes) -> str:
52
+ try:
53
+ from docx import Document # python-docx
54
+ doc = Document(io.BytesIO(data))
55
+ lines = [p.text for p in doc.paragraphs if p.text.strip()]
56
+ return "\n".join(lines)
57
+ except Exception as exc:
58
+ raise ValueError(f"DOCX extraction failed: {exc}") from exc
59
+
60
+
61
+ # ─── LLM helpers ─────────────────────────────────────────────────────────────
62
+
63
+ _PARSE_SYSTEM = """
64
+ You are a resume parser for PlacementIQ β€” an education-loan risk platform for Indian students.
65
+ Extract structured information from the resume text and return ONLY valid JSON (no markdown, no preamble).
66
+
67
+ Use this exact JSON shape. Use null for missing scalar fields and [] for missing arrays:
68
+ {
69
+ "full_name": "string or null",
70
+ "email": "string or null",
71
+ "mobile": "string or null",
72
+ "city": "string or null",
73
+ "state": "Indian state name or null",
74
+ "graduation_year": integer or null,
75
+ "cgpa": float (0.0–10.0) or null,
76
+ "achievements": ["string", ...],
77
+ "coding_problems_solved": integer or null,
78
+ "hackathons_attended": integer or null,
79
+ "skills": {
80
+ "languages": ["Python", "Java", ...],
81
+ "technical": ["Machine Learning", "SQL", ...],
82
+ "tools": ["Docker", "React", ...],
83
+ "soft": ["Leadership", "Communication", ...]
84
+ },
85
+ "internships": [
86
+ {
87
+ "company_name": "string",
88
+ "role": "string",
89
+ "duration_months": integer,
90
+ "tier": "MNC | Unicorn | MidSize | Startup | Other",
91
+ "description": "string"
92
+ }
93
+ ],
94
+ "certifications": [
95
+ {
96
+ "name": "string",
97
+ "org": "string",
98
+ "date": "YYYY-MM-DD or empty string",
99
+ "url": "string or empty string"
100
+ }
101
+ ],
102
+ "projects": [
103
+ {
104
+ "name": "string",
105
+ "tech_stack": "comma-separated technologies",
106
+ "description": "string",
107
+ "url": "string or empty string"
108
+ }
109
+ ]
110
+ }
111
+
112
+ Tier classification for internship companies:
113
+ - MNC: TCS, Infosys, Wipro, Google, Microsoft, Amazon, Meta, Apple, IBM, Accenture, Deloitte, etc.
114
+ - Unicorn: Indian unicorn startups (Zomato, Swiggy, CRED, Zepto, PhonePe, Meesho, Razorpay, BYJU'S, etc.)
115
+ - MidSize: companies with 50–5000 employees that are not MNC/Unicorn
116
+ - Startup: early-stage startups, seed/Series-A companies
117
+ - Other: government bodies, NGOs, research labs, unknown
118
+ """
119
+
120
+ _ATS_SYSTEM = """
121
+ You are an ATS (Applicant Tracking System) scorer for PlacementIQ.
122
+ Score the given resume strictly against the specified job profile and return ONLY valid JSON (no markdown).
123
+
124
+ Scoring methodology β€” each section scored 0–100 independently:
125
+ keyword_match (30% weight): Does the resume use relevant technical/domain keywords for this JD?
126
+ skills_alignment (25% weight): Do the candidate's listed skills match what the role needs?
127
+ experience_relevance (20% weight): Are internships, projects, roles relevant to the job?
128
+ education_match (15% weight): Does the degree/specialisation/CGPA fit the profile?
129
+ quantified_impact (10% weight): Are achievements stated with measurable metrics?
130
+
131
+ overall_score = round(0.30Γ—km + 0.25Γ—sa + 0.20Γ—er + 0.15Γ—em + 0.10Γ—qi)
132
+
133
+ Grade bands:
134
+ 90–100 β†’ A+, 80–89 β†’ A, 70–79 β†’ B+, 60–69 β†’ B, 50–59 β†’ C+, 40–49 β†’ C, <40 β†’ D
135
+
136
+ Return ONLY this JSON structure:
137
+ {
138
+ "overall_score": integer,
139
+ "grade": "A+ | A | B+ | B | C+ | C | D",
140
+ "job_profile": "the job profile name as given",
141
+ "breakdown": {
142
+ "keyword_match": { "score": integer, "found_keywords": ["kw1", ...], "missing_keywords": ["kw2", ...] },
143
+ "skills_alignment": { "score": integer, "matched": ["skill1", ...], "gaps": ["gap1", ...] },
144
+ "experience_relevance": { "score": integer, "notes": "one sentence" },
145
+ "education_match": { "score": integer, "notes": "one sentence" },
146
+ "quantified_impact": { "score": integer, "notes": "one sentence" }
147
+ },
148
+ "strengths": ["specific strength observed in the resume", ...],
149
+ "improvement_areas": ["specific actionable gap relevant to this job profile", ...],
150
+ "recommendations": ["concrete action the candidate can take before applying", ...]
151
+ }
152
+ """
153
+
154
+
155
+ def _extract_json(raw: str) -> dict | None:
156
+ """Robust extraction: find the outermost balanced {...} block."""
157
+ if not raw:
158
+ return None
159
+ depth, start, in_str, esc = 0, -1, False, False
160
+ for i, ch in enumerate(raw):
161
+ if esc:
162
+ esc = False
163
+ continue
164
+ if ch == "\\" and in_str:
165
+ esc = True
166
+ continue
167
+ if ch == '"':
168
+ in_str = not in_str
169
+ continue
170
+ if in_str:
171
+ continue
172
+ if ch == "{":
173
+ if depth == 0:
174
+ start = i
175
+ depth += 1
176
+ elif ch == "}":
177
+ depth -= 1
178
+ if depth == 0 and start != -1:
179
+ try:
180
+ return json.loads(raw[start: i + 1])
181
+ except json.JSONDecodeError:
182
+ start = -1
183
+ try:
184
+ s, e = raw.find("{"), raw.rfind("}") + 1
185
+ return json.loads(raw[s:e]) if s != -1 and e > s else None
186
+ except (json.JSONDecodeError, ValueError):
187
+ return None
188
+
189
+
190
+ # ─── Public API ───────────────────────────────────────────────────────────────
191
+
192
+ def parse_resume(resume_text: str) -> dict:
193
+ """Extract structured profile fields from raw resume text via LLM.
194
+
195
+ Returns a dict matching the MyProfile data shape, plus optional '_error'.
196
+ """
197
+ # Clamp to 8K chars β€” enough for any single resume, avoids token overflow
198
+ text_snippet = resume_text[:8000]
199
+ user_msg = f"Parse this resume and return the JSON profile:\n\n{text_snippet}"
200
+ try:
201
+ resp = call_llm(
202
+ system=_PARSE_SYSTEM,
203
+ messages=[{"role": "user", "content": user_msg}],
204
+ max_tokens=2000,
205
+ )
206
+ parsed = _extract_json(resp.text or "")
207
+ if parsed is None:
208
+ return {
209
+ "_error": "LLM returned unparseable output",
210
+ "_raw": (resp.text or "")[:400],
211
+ }
212
+ # Ensure required nested keys exist
213
+ parsed.setdefault("skills", {})
214
+ parsed["skills"].setdefault("languages", [])
215
+ parsed["skills"].setdefault("technical", [])
216
+ parsed["skills"].setdefault("tools", [])
217
+ parsed["skills"].setdefault("soft", [])
218
+ parsed.setdefault("internships", [])
219
+ parsed.setdefault("certifications", [])
220
+ parsed.setdefault("projects", [])
221
+ parsed.setdefault("achievements", [])
222
+ return parsed
223
+ except Exception as exc:
224
+ return {"_error": str(exc)}
225
+
226
+
227
+ def ats_score(resume_text: str, job_profile: str) -> dict:
228
+ """Score resume against a named job profile and return ATS breakdown via LLM."""
229
+ text_snippet = resume_text[:8000]
230
+ user_msg = (
231
+ f"Job profile to score against: {job_profile}\n\n"
232
+ f"Resume text:\n{text_snippet}\n\n"
233
+ "Score this resume and return the JSON."
234
+ )
235
+ try:
236
+ resp = call_llm(
237
+ system=_ATS_SYSTEM,
238
+ messages=[{"role": "user", "content": user_msg}],
239
+ max_tokens=2000,
240
+ )
241
+ result = _extract_json(resp.text or "")
242
+ if result is None:
243
+ return {
244
+ "_error": "LLM returned unparseable output",
245
+ "overall_score": 0,
246
+ "job_profile": job_profile,
247
+ }
248
+ result.setdefault("job_profile", job_profile)
249
+ result.setdefault("overall_score", 0)
250
+ result.setdefault("grade", "D")
251
+ result.setdefault("breakdown", {})
252
+ result.setdefault("strengths", [])
253
+ result.setdefault("improvement_areas", [])
254
+ result.setdefault("recommendations", [])
255
+ return result
256
+ except Exception as exc:
257
+ return {"_error": str(exc), "overall_score": 0, "job_profile": job_profile}
frontend/package-lock.json CHANGED
@@ -16,6 +16,7 @@
16
  "react": "^19.2.5",
17
  "react-dom": "^19.2.5",
18
  "react-leaflet": "^5.0.0",
 
19
  "react-router-dom": "^7.14.2",
20
  "recharts": "^3.8.1"
21
  },
@@ -975,6 +976,15 @@
975
  "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==",
976
  "license": "MIT"
977
  },
 
 
 
 
 
 
 
 
 
978
  "node_modules/@types/esrecurse": {
979
  "version": "4.3.1",
980
  "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz",
@@ -986,9 +996,26 @@
986
  "version": "1.0.8",
987
  "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
988
  "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
989
- "dev": true,
990
  "license": "MIT"
991
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
992
  "node_modules/@types/json-schema": {
993
  "version": "7.0.15",
994
  "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
@@ -996,11 +1023,25 @@
996
  "dev": true,
997
  "license": "MIT"
998
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
999
  "node_modules/@types/react": {
1000
  "version": "19.2.14",
1001
  "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz",
1002
  "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==",
1003
- "devOptional": true,
1004
  "license": "MIT",
1005
  "dependencies": {
1006
  "csstype": "^3.2.2"
@@ -1016,12 +1057,24 @@
1016
  "@types/react": "^19.2.0"
1017
  }
1018
  },
 
 
 
 
 
 
1019
  "node_modules/@types/use-sync-external-store": {
1020
  "version": "0.0.6",
1021
  "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz",
1022
  "integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==",
1023
  "license": "MIT"
1024
  },
 
 
 
 
 
 
1025
  "node_modules/@vitejs/plugin-react": {
1026
  "version": "6.0.1",
1027
  "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.0.1.tgz",
@@ -1105,6 +1158,16 @@
1105
  "proxy-from-env": "^2.1.0"
1106
  }
1107
  },
 
 
 
 
 
 
 
 
 
 
1108
  "node_modules/balanced-match": {
1109
  "version": "4.0.4",
1110
  "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
@@ -1209,6 +1272,56 @@
1209
  ],
1210
  "license": "CC-BY-4.0"
1211
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1212
  "node_modules/clsx": {
1213
  "version": "2.1.1",
1214
  "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
@@ -1230,6 +1343,16 @@
1230
  "node": ">= 0.8"
1231
  }
1232
  },
 
 
 
 
 
 
 
 
 
 
1233
  "node_modules/convert-source-map": {
1234
  "version": "2.0.0",
1235
  "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
@@ -1269,7 +1392,6 @@
1269
  "version": "3.2.3",
1270
  "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
1271
  "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
1272
- "devOptional": true,
1273
  "license": "MIT"
1274
  },
1275
  "node_modules/d3-array": {
@@ -1397,7 +1519,6 @@
1397
  "version": "4.4.3",
1398
  "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
1399
  "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
1400
- "dev": true,
1401
  "license": "MIT",
1402
  "dependencies": {
1403
  "ms": "^2.1.3"
@@ -1417,6 +1538,19 @@
1417
  "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==",
1418
  "license": "MIT"
1419
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
1420
  "node_modules/deep-is": {
1421
  "version": "0.1.4",
1422
  "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
@@ -1433,6 +1567,15 @@
1433
  "node": ">=0.4.0"
1434
  }
1435
  },
 
 
 
 
 
 
 
 
 
1436
  "node_modules/detect-libc": {
1437
  "version": "2.1.2",
1438
  "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
@@ -1443,6 +1586,19 @@
1443
  "node": ">=8"
1444
  }
1445
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
1446
  "node_modules/dunder-proto": {
1447
  "version": "1.0.1",
1448
  "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
@@ -1714,6 +1870,16 @@
1714
  "node": ">=4.0"
1715
  }
1716
  },
 
 
 
 
 
 
 
 
 
 
1717
  "node_modules/esutils": {
1718
  "version": "2.0.3",
1719
  "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
@@ -1730,6 +1896,12 @@
1730
  "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==",
1731
  "license": "MIT"
1732
  },
 
 
 
 
 
 
1733
  "node_modules/fast-deep-equal": {
1734
  "version": "3.1.3",
1735
  "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
@@ -2031,6 +2203,46 @@
2031
  "node": ">= 0.4"
2032
  }
2033
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2034
  "node_modules/hermes-estree": {
2035
  "version": "0.25.1",
2036
  "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz",
@@ -2048,6 +2260,16 @@
2048
  "hermes-estree": "0.25.1"
2049
  }
2050
  },
 
 
 
 
 
 
 
 
 
 
2051
  "node_modules/ignore": {
2052
  "version": "5.3.2",
2053
  "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
@@ -2078,6 +2300,12 @@
2078
  "node": ">=0.8.19"
2079
  }
2080
  },
 
 
 
 
 
 
2081
  "node_modules/internmap": {
2082
  "version": "2.0.3",
2083
  "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz",
@@ -2087,6 +2315,40 @@
2087
  "node": ">=12"
2088
  }
2089
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2090
  "node_modules/is-extglob": {
2091
  "version": "2.1.1",
2092
  "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
@@ -2110,6 +2372,28 @@
2110
  "node": ">=0.10.0"
2111
  }
2112
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2113
  "node_modules/isexe": {
2114
  "version": "2.0.0",
2115
  "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
@@ -2483,6 +2767,16 @@
2483
  "url": "https://github.com/sponsors/sindresorhus"
2484
  }
2485
  },
 
 
 
 
 
 
 
 
 
 
2486
  "node_modules/lru-cache": {
2487
  "version": "5.1.1",
2488
  "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
@@ -2511,111 +2805,705 @@
2511
  "node": ">= 0.4"
2512
  }
2513
  },
2514
- "node_modules/mime-db": {
2515
- "version": "1.52.0",
2516
- "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
2517
- "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
2518
  "license": "MIT",
2519
- "engines": {
2520
- "node": ">= 0.6"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2521
  }
2522
  },
2523
- "node_modules/mime-types": {
2524
- "version": "2.1.35",
2525
- "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
2526
- "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
2527
  "license": "MIT",
2528
  "dependencies": {
2529
- "mime-db": "1.52.0"
 
 
 
 
 
2530
  },
2531
- "engines": {
2532
- "node": ">= 0.6"
 
2533
  }
2534
  },
2535
- "node_modules/minimatch": {
2536
- "version": "10.2.5",
2537
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",
2538
- "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==",
2539
- "dev": true,
2540
- "license": "BlueOak-1.0.0",
2541
  "dependencies": {
2542
- "brace-expansion": "^5.0.5"
2543
- },
2544
- "engines": {
2545
- "node": "18 || 20 || >=22"
 
 
 
 
 
 
 
 
2546
  },
2547
  "funding": {
2548
- "url": "https://github.com/sponsors/isaacs"
 
2549
  }
2550
  },
2551
- "node_modules/motion": {
2552
- "version": "12.40.0",
2553
- "resolved": "https://registry.npmjs.org/motion/-/motion-12.40.0.tgz",
2554
- "integrity": "sha512-yjrHUrBFW6kQvjJwRsoiPSAhC5tRwRqNGJWmiJ4CrGnbKp0V88AdzkhBmDoqIsIPfarOe0Uddd37Xq43/gIocA==",
2555
  "license": "MIT",
2556
  "dependencies": {
2557
- "framer-motion": "^12.40.0",
2558
- "tslib": "^2.4.0"
2559
- },
2560
- "peerDependencies": {
2561
- "@emotion/is-prop-valid": "*",
2562
- "react": "^18.0.0 || ^19.0.0",
2563
- "react-dom": "^18.0.0 || ^19.0.0"
2564
  },
2565
- "peerDependenciesMeta": {
2566
- "@emotion/is-prop-valid": {
2567
- "optional": true
2568
- },
2569
- "react": {
2570
- "optional": true
2571
- },
2572
- "react-dom": {
2573
- "optional": true
2574
- }
2575
  }
2576
  },
2577
- "node_modules/motion-dom": {
2578
- "version": "12.40.0",
2579
- "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.40.0.tgz",
2580
- "integrity": "sha512-HxU3ZaBwNPVQUBQf1xxgq+7JrPNZvjLVxgbpEZL7RrWJnsxOf0/OM+yrHG9ogLQ31Do/r57Oz2gQWPK+6q62mg==",
2581
  "license": "MIT",
2582
  "dependencies": {
2583
- "motion-utils": "^12.39.0"
 
 
 
 
 
2584
  }
2585
  },
2586
- "node_modules/motion-utils": {
2587
- "version": "12.39.0",
2588
- "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.39.0.tgz",
2589
- "integrity": "sha512-8nadJAJjTtqRkmRF36FoJTrywK9nnFmnPwnSMyxaOCU7GDjN9RTMJIxx9De8ErM+vpPhMccr/6fo5WciyQLnMQ==",
2590
- "license": "MIT"
2591
- },
2592
- "node_modules/ms": {
2593
- "version": "2.1.3",
2594
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
2595
- "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
2596
- "dev": true,
2597
- "license": "MIT"
2598
- },
2599
- "node_modules/nanoid": {
2600
- "version": "3.3.12",
2601
- "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz",
2602
- "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==",
2603
- "dev": true,
2604
- "funding": [
2605
- {
2606
- "type": "github",
2607
- "url": "https://github.com/sponsors/ai"
2608
- }
2609
- ],
2610
  "license": "MIT",
2611
- "bin": {
2612
- "nanoid": "bin/nanoid.cjs"
 
 
 
 
 
 
 
 
2613
  },
2614
- "engines": {
2615
- "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
 
2616
  }
2617
  },
2618
- "node_modules/natural-compare": {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2619
  "version": "1.4.0",
2620
  "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
2621
  "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
@@ -2679,6 +3567,31 @@
2679
  "url": "https://github.com/sponsors/sindresorhus"
2680
  }
2681
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2682
  "node_modules/path-exists": {
2683
  "version": "4.0.0",
2684
  "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
@@ -2758,6 +3671,16 @@
2758
  "node": ">= 0.8.0"
2759
  }
2760
  },
 
 
 
 
 
 
 
 
 
 
2761
  "node_modules/proxy-from-env": {
2762
  "version": "2.1.0",
2763
  "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz",
@@ -2819,6 +3742,33 @@
2819
  "react-dom": "^19.0.0"
2820
  }
2821
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2822
  "node_modules/react-redux": {
2823
  "version": "9.2.0",
2824
  "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz",
@@ -2925,6 +3875,39 @@
2925
  "redux": "^5.0.0"
2926
  }
2927
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2928
  "node_modules/reselect": {
2929
  "version": "5.1.1",
2930
  "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz",
@@ -3027,6 +4010,48 @@
3027
  "node": ">=0.10.0"
3028
  }
3029
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3030
  "node_modules/tiny-invariant": {
3031
  "version": "1.3.3",
3032
  "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz",
@@ -3050,6 +4075,26 @@
3050
  "url": "https://github.com/sponsors/SuperchupuDev"
3051
  }
3052
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3053
  "node_modules/tslib": {
3054
  "version": "2.8.1",
3055
  "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
@@ -3069,6 +4114,93 @@
3069
  "node": ">= 0.8.0"
3070
  }
3071
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3072
  "node_modules/update-browserslist-db": {
3073
  "version": "1.2.3",
3074
  "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
@@ -3119,6 +4251,34 @@
3119
  "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
3120
  }
3121
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3122
  "node_modules/victory-vendor": {
3123
  "version": "37.3.6",
3124
  "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-37.3.6.tgz",
@@ -3287,6 +4447,16 @@
3287
  "peerDependencies": {
3288
  "zod": "^3.25.0 || ^4.0.0"
3289
  }
 
 
 
 
 
 
 
 
 
 
3290
  }
3291
  }
3292
  }
 
16
  "react": "^19.2.5",
17
  "react-dom": "^19.2.5",
18
  "react-leaflet": "^5.0.0",
19
+ "react-markdown": "^10.1.0",
20
  "react-router-dom": "^7.14.2",
21
  "recharts": "^3.8.1"
22
  },
 
976
  "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==",
977
  "license": "MIT"
978
  },
979
+ "node_modules/@types/debug": {
980
+ "version": "4.1.13",
981
+ "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz",
982
+ "integrity": "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==",
983
+ "license": "MIT",
984
+ "dependencies": {
985
+ "@types/ms": "*"
986
+ }
987
+ },
988
  "node_modules/@types/esrecurse": {
989
  "version": "4.3.1",
990
  "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz",
 
996
  "version": "1.0.8",
997
  "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
998
  "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
 
999
  "license": "MIT"
1000
  },
1001
+ "node_modules/@types/estree-jsx": {
1002
+ "version": "1.0.5",
1003
+ "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz",
1004
+ "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==",
1005
+ "license": "MIT",
1006
+ "dependencies": {
1007
+ "@types/estree": "*"
1008
+ }
1009
+ },
1010
+ "node_modules/@types/hast": {
1011
+ "version": "3.0.4",
1012
+ "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz",
1013
+ "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==",
1014
+ "license": "MIT",
1015
+ "dependencies": {
1016
+ "@types/unist": "*"
1017
+ }
1018
+ },
1019
  "node_modules/@types/json-schema": {
1020
  "version": "7.0.15",
1021
  "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
 
1023
  "dev": true,
1024
  "license": "MIT"
1025
  },
1026
+ "node_modules/@types/mdast": {
1027
+ "version": "4.0.4",
1028
+ "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz",
1029
+ "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==",
1030
+ "license": "MIT",
1031
+ "dependencies": {
1032
+ "@types/unist": "*"
1033
+ }
1034
+ },
1035
+ "node_modules/@types/ms": {
1036
+ "version": "2.1.0",
1037
+ "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz",
1038
+ "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==",
1039
+ "license": "MIT"
1040
+ },
1041
  "node_modules/@types/react": {
1042
  "version": "19.2.14",
1043
  "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz",
1044
  "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==",
 
1045
  "license": "MIT",
1046
  "dependencies": {
1047
  "csstype": "^3.2.2"
 
1057
  "@types/react": "^19.2.0"
1058
  }
1059
  },
1060
+ "node_modules/@types/unist": {
1061
+ "version": "3.0.3",
1062
+ "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz",
1063
+ "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==",
1064
+ "license": "MIT"
1065
+ },
1066
  "node_modules/@types/use-sync-external-store": {
1067
  "version": "0.0.6",
1068
  "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz",
1069
  "integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==",
1070
  "license": "MIT"
1071
  },
1072
+ "node_modules/@ungap/structured-clone": {
1073
+ "version": "1.3.1",
1074
+ "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.1.tgz",
1075
+ "integrity": "sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ==",
1076
+ "license": "ISC"
1077
+ },
1078
  "node_modules/@vitejs/plugin-react": {
1079
  "version": "6.0.1",
1080
  "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.0.1.tgz",
 
1158
  "proxy-from-env": "^2.1.0"
1159
  }
1160
  },
1161
+ "node_modules/bail": {
1162
+ "version": "2.0.2",
1163
+ "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz",
1164
+ "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==",
1165
+ "license": "MIT",
1166
+ "funding": {
1167
+ "type": "github",
1168
+ "url": "https://github.com/sponsors/wooorm"
1169
+ }
1170
+ },
1171
  "node_modules/balanced-match": {
1172
  "version": "4.0.4",
1173
  "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
 
1272
  ],
1273
  "license": "CC-BY-4.0"
1274
  },
1275
+ "node_modules/ccount": {
1276
+ "version": "2.0.1",
1277
+ "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz",
1278
+ "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==",
1279
+ "license": "MIT",
1280
+ "funding": {
1281
+ "type": "github",
1282
+ "url": "https://github.com/sponsors/wooorm"
1283
+ }
1284
+ },
1285
+ "node_modules/character-entities": {
1286
+ "version": "2.0.2",
1287
+ "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz",
1288
+ "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==",
1289
+ "license": "MIT",
1290
+ "funding": {
1291
+ "type": "github",
1292
+ "url": "https://github.com/sponsors/wooorm"
1293
+ }
1294
+ },
1295
+ "node_modules/character-entities-html4": {
1296
+ "version": "2.1.0",
1297
+ "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz",
1298
+ "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==",
1299
+ "license": "MIT",
1300
+ "funding": {
1301
+ "type": "github",
1302
+ "url": "https://github.com/sponsors/wooorm"
1303
+ }
1304
+ },
1305
+ "node_modules/character-entities-legacy": {
1306
+ "version": "3.0.0",
1307
+ "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz",
1308
+ "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==",
1309
+ "license": "MIT",
1310
+ "funding": {
1311
+ "type": "github",
1312
+ "url": "https://github.com/sponsors/wooorm"
1313
+ }
1314
+ },
1315
+ "node_modules/character-reference-invalid": {
1316
+ "version": "2.0.1",
1317
+ "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz",
1318
+ "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==",
1319
+ "license": "MIT",
1320
+ "funding": {
1321
+ "type": "github",
1322
+ "url": "https://github.com/sponsors/wooorm"
1323
+ }
1324
+ },
1325
  "node_modules/clsx": {
1326
  "version": "2.1.1",
1327
  "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
 
1343
  "node": ">= 0.8"
1344
  }
1345
  },
1346
+ "node_modules/comma-separated-tokens": {
1347
+ "version": "2.0.3",
1348
+ "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz",
1349
+ "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==",
1350
+ "license": "MIT",
1351
+ "funding": {
1352
+ "type": "github",
1353
+ "url": "https://github.com/sponsors/wooorm"
1354
+ }
1355
+ },
1356
  "node_modules/convert-source-map": {
1357
  "version": "2.0.0",
1358
  "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
 
1392
  "version": "3.2.3",
1393
  "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
1394
  "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
 
1395
  "license": "MIT"
1396
  },
1397
  "node_modules/d3-array": {
 
1519
  "version": "4.4.3",
1520
  "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
1521
  "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
 
1522
  "license": "MIT",
1523
  "dependencies": {
1524
  "ms": "^2.1.3"
 
1538
  "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==",
1539
  "license": "MIT"
1540
  },
1541
+ "node_modules/decode-named-character-reference": {
1542
+ "version": "1.3.0",
1543
+ "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.3.0.tgz",
1544
+ "integrity": "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==",
1545
+ "license": "MIT",
1546
+ "dependencies": {
1547
+ "character-entities": "^2.0.0"
1548
+ },
1549
+ "funding": {
1550
+ "type": "github",
1551
+ "url": "https://github.com/sponsors/wooorm"
1552
+ }
1553
+ },
1554
  "node_modules/deep-is": {
1555
  "version": "0.1.4",
1556
  "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
 
1567
  "node": ">=0.4.0"
1568
  }
1569
  },
1570
+ "node_modules/dequal": {
1571
+ "version": "2.0.3",
1572
+ "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
1573
+ "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==",
1574
+ "license": "MIT",
1575
+ "engines": {
1576
+ "node": ">=6"
1577
+ }
1578
+ },
1579
  "node_modules/detect-libc": {
1580
  "version": "2.1.2",
1581
  "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
 
1586
  "node": ">=8"
1587
  }
1588
  },
1589
+ "node_modules/devlop": {
1590
+ "version": "1.1.0",
1591
+ "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz",
1592
+ "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==",
1593
+ "license": "MIT",
1594
+ "dependencies": {
1595
+ "dequal": "^2.0.0"
1596
+ },
1597
+ "funding": {
1598
+ "type": "github",
1599
+ "url": "https://github.com/sponsors/wooorm"
1600
+ }
1601
+ },
1602
  "node_modules/dunder-proto": {
1603
  "version": "1.0.1",
1604
  "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
 
1870
  "node": ">=4.0"
1871
  }
1872
  },
1873
+ "node_modules/estree-util-is-identifier-name": {
1874
+ "version": "3.0.0",
1875
+ "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz",
1876
+ "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==",
1877
+ "license": "MIT",
1878
+ "funding": {
1879
+ "type": "opencollective",
1880
+ "url": "https://opencollective.com/unified"
1881
+ }
1882
+ },
1883
  "node_modules/esutils": {
1884
  "version": "2.0.3",
1885
  "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
 
1896
  "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==",
1897
  "license": "MIT"
1898
  },
1899
+ "node_modules/extend": {
1900
+ "version": "3.0.2",
1901
+ "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
1902
+ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
1903
+ "license": "MIT"
1904
+ },
1905
  "node_modules/fast-deep-equal": {
1906
  "version": "3.1.3",
1907
  "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
 
2203
  "node": ">= 0.4"
2204
  }
2205
  },
2206
+ "node_modules/hast-util-to-jsx-runtime": {
2207
+ "version": "2.3.6",
2208
+ "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz",
2209
+ "integrity": "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==",
2210
+ "license": "MIT",
2211
+ "dependencies": {
2212
+ "@types/estree": "^1.0.0",
2213
+ "@types/hast": "^3.0.0",
2214
+ "@types/unist": "^3.0.0",
2215
+ "comma-separated-tokens": "^2.0.0",
2216
+ "devlop": "^1.0.0",
2217
+ "estree-util-is-identifier-name": "^3.0.0",
2218
+ "hast-util-whitespace": "^3.0.0",
2219
+ "mdast-util-mdx-expression": "^2.0.0",
2220
+ "mdast-util-mdx-jsx": "^3.0.0",
2221
+ "mdast-util-mdxjs-esm": "^2.0.0",
2222
+ "property-information": "^7.0.0",
2223
+ "space-separated-tokens": "^2.0.0",
2224
+ "style-to-js": "^1.0.0",
2225
+ "unist-util-position": "^5.0.0",
2226
+ "vfile-message": "^4.0.0"
2227
+ },
2228
+ "funding": {
2229
+ "type": "opencollective",
2230
+ "url": "https://opencollective.com/unified"
2231
+ }
2232
+ },
2233
+ "node_modules/hast-util-whitespace": {
2234
+ "version": "3.0.0",
2235
+ "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz",
2236
+ "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==",
2237
+ "license": "MIT",
2238
+ "dependencies": {
2239
+ "@types/hast": "^3.0.0"
2240
+ },
2241
+ "funding": {
2242
+ "type": "opencollective",
2243
+ "url": "https://opencollective.com/unified"
2244
+ }
2245
+ },
2246
  "node_modules/hermes-estree": {
2247
  "version": "0.25.1",
2248
  "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz",
 
2260
  "hermes-estree": "0.25.1"
2261
  }
2262
  },
2263
+ "node_modules/html-url-attributes": {
2264
+ "version": "3.0.1",
2265
+ "resolved": "https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.1.tgz",
2266
+ "integrity": "sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==",
2267
+ "license": "MIT",
2268
+ "funding": {
2269
+ "type": "opencollective",
2270
+ "url": "https://opencollective.com/unified"
2271
+ }
2272
+ },
2273
  "node_modules/ignore": {
2274
  "version": "5.3.2",
2275
  "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
 
2300
  "node": ">=0.8.19"
2301
  }
2302
  },
2303
+ "node_modules/inline-style-parser": {
2304
+ "version": "0.2.7",
2305
+ "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.7.tgz",
2306
+ "integrity": "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==",
2307
+ "license": "MIT"
2308
+ },
2309
  "node_modules/internmap": {
2310
  "version": "2.0.3",
2311
  "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz",
 
2315
  "node": ">=12"
2316
  }
2317
  },
2318
+ "node_modules/is-alphabetical": {
2319
+ "version": "2.0.1",
2320
+ "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz",
2321
+ "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==",
2322
+ "license": "MIT",
2323
+ "funding": {
2324
+ "type": "github",
2325
+ "url": "https://github.com/sponsors/wooorm"
2326
+ }
2327
+ },
2328
+ "node_modules/is-alphanumerical": {
2329
+ "version": "2.0.1",
2330
+ "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz",
2331
+ "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==",
2332
+ "license": "MIT",
2333
+ "dependencies": {
2334
+ "is-alphabetical": "^2.0.0",
2335
+ "is-decimal": "^2.0.0"
2336
+ },
2337
+ "funding": {
2338
+ "type": "github",
2339
+ "url": "https://github.com/sponsors/wooorm"
2340
+ }
2341
+ },
2342
+ "node_modules/is-decimal": {
2343
+ "version": "2.0.1",
2344
+ "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz",
2345
+ "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==",
2346
+ "license": "MIT",
2347
+ "funding": {
2348
+ "type": "github",
2349
+ "url": "https://github.com/sponsors/wooorm"
2350
+ }
2351
+ },
2352
  "node_modules/is-extglob": {
2353
  "version": "2.1.1",
2354
  "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
 
2372
  "node": ">=0.10.0"
2373
  }
2374
  },
2375
+ "node_modules/is-hexadecimal": {
2376
+ "version": "2.0.1",
2377
+ "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz",
2378
+ "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==",
2379
+ "license": "MIT",
2380
+ "funding": {
2381
+ "type": "github",
2382
+ "url": "https://github.com/sponsors/wooorm"
2383
+ }
2384
+ },
2385
+ "node_modules/is-plain-obj": {
2386
+ "version": "4.1.0",
2387
+ "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz",
2388
+ "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==",
2389
+ "license": "MIT",
2390
+ "engines": {
2391
+ "node": ">=12"
2392
+ },
2393
+ "funding": {
2394
+ "url": "https://github.com/sponsors/sindresorhus"
2395
+ }
2396
+ },
2397
  "node_modules/isexe": {
2398
  "version": "2.0.0",
2399
  "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
 
2767
  "url": "https://github.com/sponsors/sindresorhus"
2768
  }
2769
  },
2770
+ "node_modules/longest-streak": {
2771
+ "version": "3.1.0",
2772
+ "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz",
2773
+ "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==",
2774
+ "license": "MIT",
2775
+ "funding": {
2776
+ "type": "github",
2777
+ "url": "https://github.com/sponsors/wooorm"
2778
+ }
2779
+ },
2780
  "node_modules/lru-cache": {
2781
  "version": "5.1.1",
2782
  "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
 
2805
  "node": ">= 0.4"
2806
  }
2807
  },
2808
+ "node_modules/mdast-util-from-markdown": {
2809
+ "version": "2.0.3",
2810
+ "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.3.tgz",
2811
+ "integrity": "sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==",
2812
  "license": "MIT",
2813
+ "dependencies": {
2814
+ "@types/mdast": "^4.0.0",
2815
+ "@types/unist": "^3.0.0",
2816
+ "decode-named-character-reference": "^1.0.0",
2817
+ "devlop": "^1.0.0",
2818
+ "mdast-util-to-string": "^4.0.0",
2819
+ "micromark": "^4.0.0",
2820
+ "micromark-util-decode-numeric-character-reference": "^2.0.0",
2821
+ "micromark-util-decode-string": "^2.0.0",
2822
+ "micromark-util-normalize-identifier": "^2.0.0",
2823
+ "micromark-util-symbol": "^2.0.0",
2824
+ "micromark-util-types": "^2.0.0",
2825
+ "unist-util-stringify-position": "^4.0.0"
2826
+ },
2827
+ "funding": {
2828
+ "type": "opencollective",
2829
+ "url": "https://opencollective.com/unified"
2830
  }
2831
  },
2832
+ "node_modules/mdast-util-mdx-expression": {
2833
+ "version": "2.0.1",
2834
+ "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz",
2835
+ "integrity": "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==",
2836
  "license": "MIT",
2837
  "dependencies": {
2838
+ "@types/estree-jsx": "^1.0.0",
2839
+ "@types/hast": "^3.0.0",
2840
+ "@types/mdast": "^4.0.0",
2841
+ "devlop": "^1.0.0",
2842
+ "mdast-util-from-markdown": "^2.0.0",
2843
+ "mdast-util-to-markdown": "^2.0.0"
2844
  },
2845
+ "funding": {
2846
+ "type": "opencollective",
2847
+ "url": "https://opencollective.com/unified"
2848
  }
2849
  },
2850
+ "node_modules/mdast-util-mdx-jsx": {
2851
+ "version": "3.2.0",
2852
+ "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.0.tgz",
2853
+ "integrity": "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==",
2854
+ "license": "MIT",
 
2855
  "dependencies": {
2856
+ "@types/estree-jsx": "^1.0.0",
2857
+ "@types/hast": "^3.0.0",
2858
+ "@types/mdast": "^4.0.0",
2859
+ "@types/unist": "^3.0.0",
2860
+ "ccount": "^2.0.0",
2861
+ "devlop": "^1.1.0",
2862
+ "mdast-util-from-markdown": "^2.0.0",
2863
+ "mdast-util-to-markdown": "^2.0.0",
2864
+ "parse-entities": "^4.0.0",
2865
+ "stringify-entities": "^4.0.0",
2866
+ "unist-util-stringify-position": "^4.0.0",
2867
+ "vfile-message": "^4.0.0"
2868
  },
2869
  "funding": {
2870
+ "type": "opencollective",
2871
+ "url": "https://opencollective.com/unified"
2872
  }
2873
  },
2874
+ "node_modules/mdast-util-mdxjs-esm": {
2875
+ "version": "2.0.1",
2876
+ "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz",
2877
+ "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==",
2878
  "license": "MIT",
2879
  "dependencies": {
2880
+ "@types/estree-jsx": "^1.0.0",
2881
+ "@types/hast": "^3.0.0",
2882
+ "@types/mdast": "^4.0.0",
2883
+ "devlop": "^1.0.0",
2884
+ "mdast-util-from-markdown": "^2.0.0",
2885
+ "mdast-util-to-markdown": "^2.0.0"
 
2886
  },
2887
+ "funding": {
2888
+ "type": "opencollective",
2889
+ "url": "https://opencollective.com/unified"
 
 
 
 
 
 
 
2890
  }
2891
  },
2892
+ "node_modules/mdast-util-phrasing": {
2893
+ "version": "4.1.0",
2894
+ "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz",
2895
+ "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==",
2896
  "license": "MIT",
2897
  "dependencies": {
2898
+ "@types/mdast": "^4.0.0",
2899
+ "unist-util-is": "^6.0.0"
2900
+ },
2901
+ "funding": {
2902
+ "type": "opencollective",
2903
+ "url": "https://opencollective.com/unified"
2904
  }
2905
  },
2906
+ "node_modules/mdast-util-to-hast": {
2907
+ "version": "13.2.1",
2908
+ "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz",
2909
+ "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2910
  "license": "MIT",
2911
+ "dependencies": {
2912
+ "@types/hast": "^3.0.0",
2913
+ "@types/mdast": "^4.0.0",
2914
+ "@ungap/structured-clone": "^1.0.0",
2915
+ "devlop": "^1.0.0",
2916
+ "micromark-util-sanitize-uri": "^2.0.0",
2917
+ "trim-lines": "^3.0.0",
2918
+ "unist-util-position": "^5.0.0",
2919
+ "unist-util-visit": "^5.0.0",
2920
+ "vfile": "^6.0.0"
2921
  },
2922
+ "funding": {
2923
+ "type": "opencollective",
2924
+ "url": "https://opencollective.com/unified"
2925
  }
2926
  },
2927
+ "node_modules/mdast-util-to-markdown": {
2928
+ "version": "2.1.2",
2929
+ "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz",
2930
+ "integrity": "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==",
2931
+ "license": "MIT",
2932
+ "dependencies": {
2933
+ "@types/mdast": "^4.0.0",
2934
+ "@types/unist": "^3.0.0",
2935
+ "longest-streak": "^3.0.0",
2936
+ "mdast-util-phrasing": "^4.0.0",
2937
+ "mdast-util-to-string": "^4.0.0",
2938
+ "micromark-util-classify-character": "^2.0.0",
2939
+ "micromark-util-decode-string": "^2.0.0",
2940
+ "unist-util-visit": "^5.0.0",
2941
+ "zwitch": "^2.0.0"
2942
+ },
2943
+ "funding": {
2944
+ "type": "opencollective",
2945
+ "url": "https://opencollective.com/unified"
2946
+ }
2947
+ },
2948
+ "node_modules/mdast-util-to-string": {
2949
+ "version": "4.0.0",
2950
+ "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz",
2951
+ "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==",
2952
+ "license": "MIT",
2953
+ "dependencies": {
2954
+ "@types/mdast": "^4.0.0"
2955
+ },
2956
+ "funding": {
2957
+ "type": "opencollective",
2958
+ "url": "https://opencollective.com/unified"
2959
+ }
2960
+ },
2961
+ "node_modules/micromark": {
2962
+ "version": "4.0.2",
2963
+ "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz",
2964
+ "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==",
2965
+ "funding": [
2966
+ {
2967
+ "type": "GitHub Sponsors",
2968
+ "url": "https://github.com/sponsors/unifiedjs"
2969
+ },
2970
+ {
2971
+ "type": "OpenCollective",
2972
+ "url": "https://opencollective.com/unified"
2973
+ }
2974
+ ],
2975
+ "license": "MIT",
2976
+ "dependencies": {
2977
+ "@types/debug": "^4.0.0",
2978
+ "debug": "^4.0.0",
2979
+ "decode-named-character-reference": "^1.0.0",
2980
+ "devlop": "^1.0.0",
2981
+ "micromark-core-commonmark": "^2.0.0",
2982
+ "micromark-factory-space": "^2.0.0",
2983
+ "micromark-util-character": "^2.0.0",
2984
+ "micromark-util-chunked": "^2.0.0",
2985
+ "micromark-util-combine-extensions": "^2.0.0",
2986
+ "micromark-util-decode-numeric-character-reference": "^2.0.0",
2987
+ "micromark-util-encode": "^2.0.0",
2988
+ "micromark-util-normalize-identifier": "^2.0.0",
2989
+ "micromark-util-resolve-all": "^2.0.0",
2990
+ "micromark-util-sanitize-uri": "^2.0.0",
2991
+ "micromark-util-subtokenize": "^2.0.0",
2992
+ "micromark-util-symbol": "^2.0.0",
2993
+ "micromark-util-types": "^2.0.0"
2994
+ }
2995
+ },
2996
+ "node_modules/micromark-core-commonmark": {
2997
+ "version": "2.0.3",
2998
+ "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz",
2999
+ "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==",
3000
+ "funding": [
3001
+ {
3002
+ "type": "GitHub Sponsors",
3003
+ "url": "https://github.com/sponsors/unifiedjs"
3004
+ },
3005
+ {
3006
+ "type": "OpenCollective",
3007
+ "url": "https://opencollective.com/unified"
3008
+ }
3009
+ ],
3010
+ "license": "MIT",
3011
+ "dependencies": {
3012
+ "decode-named-character-reference": "^1.0.0",
3013
+ "devlop": "^1.0.0",
3014
+ "micromark-factory-destination": "^2.0.0",
3015
+ "micromark-factory-label": "^2.0.0",
3016
+ "micromark-factory-space": "^2.0.0",
3017
+ "micromark-factory-title": "^2.0.0",
3018
+ "micromark-factory-whitespace": "^2.0.0",
3019
+ "micromark-util-character": "^2.0.0",
3020
+ "micromark-util-chunked": "^2.0.0",
3021
+ "micromark-util-classify-character": "^2.0.0",
3022
+ "micromark-util-html-tag-name": "^2.0.0",
3023
+ "micromark-util-normalize-identifier": "^2.0.0",
3024
+ "micromark-util-resolve-all": "^2.0.0",
3025
+ "micromark-util-subtokenize": "^2.0.0",
3026
+ "micromark-util-symbol": "^2.0.0",
3027
+ "micromark-util-types": "^2.0.0"
3028
+ }
3029
+ },
3030
+ "node_modules/micromark-factory-destination": {
3031
+ "version": "2.0.1",
3032
+ "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz",
3033
+ "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==",
3034
+ "funding": [
3035
+ {
3036
+ "type": "GitHub Sponsors",
3037
+ "url": "https://github.com/sponsors/unifiedjs"
3038
+ },
3039
+ {
3040
+ "type": "OpenCollective",
3041
+ "url": "https://opencollective.com/unified"
3042
+ }
3043
+ ],
3044
+ "license": "MIT",
3045
+ "dependencies": {
3046
+ "micromark-util-character": "^2.0.0",
3047
+ "micromark-util-symbol": "^2.0.0",
3048
+ "micromark-util-types": "^2.0.0"
3049
+ }
3050
+ },
3051
+ "node_modules/micromark-factory-label": {
3052
+ "version": "2.0.1",
3053
+ "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz",
3054
+ "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==",
3055
+ "funding": [
3056
+ {
3057
+ "type": "GitHub Sponsors",
3058
+ "url": "https://github.com/sponsors/unifiedjs"
3059
+ },
3060
+ {
3061
+ "type": "OpenCollective",
3062
+ "url": "https://opencollective.com/unified"
3063
+ }
3064
+ ],
3065
+ "license": "MIT",
3066
+ "dependencies": {
3067
+ "devlop": "^1.0.0",
3068
+ "micromark-util-character": "^2.0.0",
3069
+ "micromark-util-symbol": "^2.0.0",
3070
+ "micromark-util-types": "^2.0.0"
3071
+ }
3072
+ },
3073
+ "node_modules/micromark-factory-space": {
3074
+ "version": "2.0.1",
3075
+ "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz",
3076
+ "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==",
3077
+ "funding": [
3078
+ {
3079
+ "type": "GitHub Sponsors",
3080
+ "url": "https://github.com/sponsors/unifiedjs"
3081
+ },
3082
+ {
3083
+ "type": "OpenCollective",
3084
+ "url": "https://opencollective.com/unified"
3085
+ }
3086
+ ],
3087
+ "license": "MIT",
3088
+ "dependencies": {
3089
+ "micromark-util-character": "^2.0.0",
3090
+ "micromark-util-types": "^2.0.0"
3091
+ }
3092
+ },
3093
+ "node_modules/micromark-factory-title": {
3094
+ "version": "2.0.1",
3095
+ "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz",
3096
+ "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==",
3097
+ "funding": [
3098
+ {
3099
+ "type": "GitHub Sponsors",
3100
+ "url": "https://github.com/sponsors/unifiedjs"
3101
+ },
3102
+ {
3103
+ "type": "OpenCollective",
3104
+ "url": "https://opencollective.com/unified"
3105
+ }
3106
+ ],
3107
+ "license": "MIT",
3108
+ "dependencies": {
3109
+ "micromark-factory-space": "^2.0.0",
3110
+ "micromark-util-character": "^2.0.0",
3111
+ "micromark-util-symbol": "^2.0.0",
3112
+ "micromark-util-types": "^2.0.0"
3113
+ }
3114
+ },
3115
+ "node_modules/micromark-factory-whitespace": {
3116
+ "version": "2.0.1",
3117
+ "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz",
3118
+ "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==",
3119
+ "funding": [
3120
+ {
3121
+ "type": "GitHub Sponsors",
3122
+ "url": "https://github.com/sponsors/unifiedjs"
3123
+ },
3124
+ {
3125
+ "type": "OpenCollective",
3126
+ "url": "https://opencollective.com/unified"
3127
+ }
3128
+ ],
3129
+ "license": "MIT",
3130
+ "dependencies": {
3131
+ "micromark-factory-space": "^2.0.0",
3132
+ "micromark-util-character": "^2.0.0",
3133
+ "micromark-util-symbol": "^2.0.0",
3134
+ "micromark-util-types": "^2.0.0"
3135
+ }
3136
+ },
3137
+ "node_modules/micromark-util-character": {
3138
+ "version": "2.1.1",
3139
+ "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz",
3140
+ "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==",
3141
+ "funding": [
3142
+ {
3143
+ "type": "GitHub Sponsors",
3144
+ "url": "https://github.com/sponsors/unifiedjs"
3145
+ },
3146
+ {
3147
+ "type": "OpenCollective",
3148
+ "url": "https://opencollective.com/unified"
3149
+ }
3150
+ ],
3151
+ "license": "MIT",
3152
+ "dependencies": {
3153
+ "micromark-util-symbol": "^2.0.0",
3154
+ "micromark-util-types": "^2.0.0"
3155
+ }
3156
+ },
3157
+ "node_modules/micromark-util-chunked": {
3158
+ "version": "2.0.1",
3159
+ "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz",
3160
+ "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==",
3161
+ "funding": [
3162
+ {
3163
+ "type": "GitHub Sponsors",
3164
+ "url": "https://github.com/sponsors/unifiedjs"
3165
+ },
3166
+ {
3167
+ "type": "OpenCollective",
3168
+ "url": "https://opencollective.com/unified"
3169
+ }
3170
+ ],
3171
+ "license": "MIT",
3172
+ "dependencies": {
3173
+ "micromark-util-symbol": "^2.0.0"
3174
+ }
3175
+ },
3176
+ "node_modules/micromark-util-classify-character": {
3177
+ "version": "2.0.1",
3178
+ "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz",
3179
+ "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==",
3180
+ "funding": [
3181
+ {
3182
+ "type": "GitHub Sponsors",
3183
+ "url": "https://github.com/sponsors/unifiedjs"
3184
+ },
3185
+ {
3186
+ "type": "OpenCollective",
3187
+ "url": "https://opencollective.com/unified"
3188
+ }
3189
+ ],
3190
+ "license": "MIT",
3191
+ "dependencies": {
3192
+ "micromark-util-character": "^2.0.0",
3193
+ "micromark-util-symbol": "^2.0.0",
3194
+ "micromark-util-types": "^2.0.0"
3195
+ }
3196
+ },
3197
+ "node_modules/micromark-util-combine-extensions": {
3198
+ "version": "2.0.1",
3199
+ "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz",
3200
+ "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==",
3201
+ "funding": [
3202
+ {
3203
+ "type": "GitHub Sponsors",
3204
+ "url": "https://github.com/sponsors/unifiedjs"
3205
+ },
3206
+ {
3207
+ "type": "OpenCollective",
3208
+ "url": "https://opencollective.com/unified"
3209
+ }
3210
+ ],
3211
+ "license": "MIT",
3212
+ "dependencies": {
3213
+ "micromark-util-chunked": "^2.0.0",
3214
+ "micromark-util-types": "^2.0.0"
3215
+ }
3216
+ },
3217
+ "node_modules/micromark-util-decode-numeric-character-reference": {
3218
+ "version": "2.0.2",
3219
+ "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz",
3220
+ "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==",
3221
+ "funding": [
3222
+ {
3223
+ "type": "GitHub Sponsors",
3224
+ "url": "https://github.com/sponsors/unifiedjs"
3225
+ },
3226
+ {
3227
+ "type": "OpenCollective",
3228
+ "url": "https://opencollective.com/unified"
3229
+ }
3230
+ ],
3231
+ "license": "MIT",
3232
+ "dependencies": {
3233
+ "micromark-util-symbol": "^2.0.0"
3234
+ }
3235
+ },
3236
+ "node_modules/micromark-util-decode-string": {
3237
+ "version": "2.0.1",
3238
+ "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz",
3239
+ "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==",
3240
+ "funding": [
3241
+ {
3242
+ "type": "GitHub Sponsors",
3243
+ "url": "https://github.com/sponsors/unifiedjs"
3244
+ },
3245
+ {
3246
+ "type": "OpenCollective",
3247
+ "url": "https://opencollective.com/unified"
3248
+ }
3249
+ ],
3250
+ "license": "MIT",
3251
+ "dependencies": {
3252
+ "decode-named-character-reference": "^1.0.0",
3253
+ "micromark-util-character": "^2.0.0",
3254
+ "micromark-util-decode-numeric-character-reference": "^2.0.0",
3255
+ "micromark-util-symbol": "^2.0.0"
3256
+ }
3257
+ },
3258
+ "node_modules/micromark-util-encode": {
3259
+ "version": "2.0.1",
3260
+ "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz",
3261
+ "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==",
3262
+ "funding": [
3263
+ {
3264
+ "type": "GitHub Sponsors",
3265
+ "url": "https://github.com/sponsors/unifiedjs"
3266
+ },
3267
+ {
3268
+ "type": "OpenCollective",
3269
+ "url": "https://opencollective.com/unified"
3270
+ }
3271
+ ],
3272
+ "license": "MIT"
3273
+ },
3274
+ "node_modules/micromark-util-html-tag-name": {
3275
+ "version": "2.0.1",
3276
+ "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz",
3277
+ "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==",
3278
+ "funding": [
3279
+ {
3280
+ "type": "GitHub Sponsors",
3281
+ "url": "https://github.com/sponsors/unifiedjs"
3282
+ },
3283
+ {
3284
+ "type": "OpenCollective",
3285
+ "url": "https://opencollective.com/unified"
3286
+ }
3287
+ ],
3288
+ "license": "MIT"
3289
+ },
3290
+ "node_modules/micromark-util-normalize-identifier": {
3291
+ "version": "2.0.1",
3292
+ "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz",
3293
+ "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==",
3294
+ "funding": [
3295
+ {
3296
+ "type": "GitHub Sponsors",
3297
+ "url": "https://github.com/sponsors/unifiedjs"
3298
+ },
3299
+ {
3300
+ "type": "OpenCollective",
3301
+ "url": "https://opencollective.com/unified"
3302
+ }
3303
+ ],
3304
+ "license": "MIT",
3305
+ "dependencies": {
3306
+ "micromark-util-symbol": "^2.0.0"
3307
+ }
3308
+ },
3309
+ "node_modules/micromark-util-resolve-all": {
3310
+ "version": "2.0.1",
3311
+ "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz",
3312
+ "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==",
3313
+ "funding": [
3314
+ {
3315
+ "type": "GitHub Sponsors",
3316
+ "url": "https://github.com/sponsors/unifiedjs"
3317
+ },
3318
+ {
3319
+ "type": "OpenCollective",
3320
+ "url": "https://opencollective.com/unified"
3321
+ }
3322
+ ],
3323
+ "license": "MIT",
3324
+ "dependencies": {
3325
+ "micromark-util-types": "^2.0.0"
3326
+ }
3327
+ },
3328
+ "node_modules/micromark-util-sanitize-uri": {
3329
+ "version": "2.0.1",
3330
+ "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz",
3331
+ "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==",
3332
+ "funding": [
3333
+ {
3334
+ "type": "GitHub Sponsors",
3335
+ "url": "https://github.com/sponsors/unifiedjs"
3336
+ },
3337
+ {
3338
+ "type": "OpenCollective",
3339
+ "url": "https://opencollective.com/unified"
3340
+ }
3341
+ ],
3342
+ "license": "MIT",
3343
+ "dependencies": {
3344
+ "micromark-util-character": "^2.0.0",
3345
+ "micromark-util-encode": "^2.0.0",
3346
+ "micromark-util-symbol": "^2.0.0"
3347
+ }
3348
+ },
3349
+ "node_modules/micromark-util-subtokenize": {
3350
+ "version": "2.1.0",
3351
+ "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz",
3352
+ "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==",
3353
+ "funding": [
3354
+ {
3355
+ "type": "GitHub Sponsors",
3356
+ "url": "https://github.com/sponsors/unifiedjs"
3357
+ },
3358
+ {
3359
+ "type": "OpenCollective",
3360
+ "url": "https://opencollective.com/unified"
3361
+ }
3362
+ ],
3363
+ "license": "MIT",
3364
+ "dependencies": {
3365
+ "devlop": "^1.0.0",
3366
+ "micromark-util-chunked": "^2.0.0",
3367
+ "micromark-util-symbol": "^2.0.0",
3368
+ "micromark-util-types": "^2.0.0"
3369
+ }
3370
+ },
3371
+ "node_modules/micromark-util-symbol": {
3372
+ "version": "2.0.1",
3373
+ "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz",
3374
+ "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==",
3375
+ "funding": [
3376
+ {
3377
+ "type": "GitHub Sponsors",
3378
+ "url": "https://github.com/sponsors/unifiedjs"
3379
+ },
3380
+ {
3381
+ "type": "OpenCollective",
3382
+ "url": "https://opencollective.com/unified"
3383
+ }
3384
+ ],
3385
+ "license": "MIT"
3386
+ },
3387
+ "node_modules/micromark-util-types": {
3388
+ "version": "2.0.2",
3389
+ "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz",
3390
+ "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==",
3391
+ "funding": [
3392
+ {
3393
+ "type": "GitHub Sponsors",
3394
+ "url": "https://github.com/sponsors/unifiedjs"
3395
+ },
3396
+ {
3397
+ "type": "OpenCollective",
3398
+ "url": "https://opencollective.com/unified"
3399
+ }
3400
+ ],
3401
+ "license": "MIT"
3402
+ },
3403
+ "node_modules/mime-db": {
3404
+ "version": "1.52.0",
3405
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
3406
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
3407
+ "license": "MIT",
3408
+ "engines": {
3409
+ "node": ">= 0.6"
3410
+ }
3411
+ },
3412
+ "node_modules/mime-types": {
3413
+ "version": "2.1.35",
3414
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
3415
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
3416
+ "license": "MIT",
3417
+ "dependencies": {
3418
+ "mime-db": "1.52.0"
3419
+ },
3420
+ "engines": {
3421
+ "node": ">= 0.6"
3422
+ }
3423
+ },
3424
+ "node_modules/minimatch": {
3425
+ "version": "10.2.5",
3426
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",
3427
+ "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==",
3428
+ "dev": true,
3429
+ "license": "BlueOak-1.0.0",
3430
+ "dependencies": {
3431
+ "brace-expansion": "^5.0.5"
3432
+ },
3433
+ "engines": {
3434
+ "node": "18 || 20 || >=22"
3435
+ },
3436
+ "funding": {
3437
+ "url": "https://github.com/sponsors/isaacs"
3438
+ }
3439
+ },
3440
+ "node_modules/motion": {
3441
+ "version": "12.40.0",
3442
+ "resolved": "https://registry.npmjs.org/motion/-/motion-12.40.0.tgz",
3443
+ "integrity": "sha512-yjrHUrBFW6kQvjJwRsoiPSAhC5tRwRqNGJWmiJ4CrGnbKp0V88AdzkhBmDoqIsIPfarOe0Uddd37Xq43/gIocA==",
3444
+ "license": "MIT",
3445
+ "dependencies": {
3446
+ "framer-motion": "^12.40.0",
3447
+ "tslib": "^2.4.0"
3448
+ },
3449
+ "peerDependencies": {
3450
+ "@emotion/is-prop-valid": "*",
3451
+ "react": "^18.0.0 || ^19.0.0",
3452
+ "react-dom": "^18.0.0 || ^19.0.0"
3453
+ },
3454
+ "peerDependenciesMeta": {
3455
+ "@emotion/is-prop-valid": {
3456
+ "optional": true
3457
+ },
3458
+ "react": {
3459
+ "optional": true
3460
+ },
3461
+ "react-dom": {
3462
+ "optional": true
3463
+ }
3464
+ }
3465
+ },
3466
+ "node_modules/motion-dom": {
3467
+ "version": "12.40.0",
3468
+ "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.40.0.tgz",
3469
+ "integrity": "sha512-HxU3ZaBwNPVQUBQf1xxgq+7JrPNZvjLVxgbpEZL7RrWJnsxOf0/OM+yrHG9ogLQ31Do/r57Oz2gQWPK+6q62mg==",
3470
+ "license": "MIT",
3471
+ "dependencies": {
3472
+ "motion-utils": "^12.39.0"
3473
+ }
3474
+ },
3475
+ "node_modules/motion-utils": {
3476
+ "version": "12.39.0",
3477
+ "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.39.0.tgz",
3478
+ "integrity": "sha512-8nadJAJjTtqRkmRF36FoJTrywK9nnFmnPwnSMyxaOCU7GDjN9RTMJIxx9De8ErM+vpPhMccr/6fo5WciyQLnMQ==",
3479
+ "license": "MIT"
3480
+ },
3481
+ "node_modules/ms": {
3482
+ "version": "2.1.3",
3483
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
3484
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
3485
+ "license": "MIT"
3486
+ },
3487
+ "node_modules/nanoid": {
3488
+ "version": "3.3.12",
3489
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz",
3490
+ "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==",
3491
+ "dev": true,
3492
+ "funding": [
3493
+ {
3494
+ "type": "github",
3495
+ "url": "https://github.com/sponsors/ai"
3496
+ }
3497
+ ],
3498
+ "license": "MIT",
3499
+ "bin": {
3500
+ "nanoid": "bin/nanoid.cjs"
3501
+ },
3502
+ "engines": {
3503
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
3504
+ }
3505
+ },
3506
+ "node_modules/natural-compare": {
3507
  "version": "1.4.0",
3508
  "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
3509
  "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
 
3567
  "url": "https://github.com/sponsors/sindresorhus"
3568
  }
3569
  },
3570
+ "node_modules/parse-entities": {
3571
+ "version": "4.0.2",
3572
+ "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz",
3573
+ "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==",
3574
+ "license": "MIT",
3575
+ "dependencies": {
3576
+ "@types/unist": "^2.0.0",
3577
+ "character-entities-legacy": "^3.0.0",
3578
+ "character-reference-invalid": "^2.0.0",
3579
+ "decode-named-character-reference": "^1.0.0",
3580
+ "is-alphanumerical": "^2.0.0",
3581
+ "is-decimal": "^2.0.0",
3582
+ "is-hexadecimal": "^2.0.0"
3583
+ },
3584
+ "funding": {
3585
+ "type": "github",
3586
+ "url": "https://github.com/sponsors/wooorm"
3587
+ }
3588
+ },
3589
+ "node_modules/parse-entities/node_modules/@types/unist": {
3590
+ "version": "2.0.11",
3591
+ "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz",
3592
+ "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==",
3593
+ "license": "MIT"
3594
+ },
3595
  "node_modules/path-exists": {
3596
  "version": "4.0.0",
3597
  "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
 
3671
  "node": ">= 0.8.0"
3672
  }
3673
  },
3674
+ "node_modules/property-information": {
3675
+ "version": "7.2.0",
3676
+ "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.2.0.tgz",
3677
+ "integrity": "sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg==",
3678
+ "license": "MIT",
3679
+ "funding": {
3680
+ "type": "github",
3681
+ "url": "https://github.com/sponsors/wooorm"
3682
+ }
3683
+ },
3684
  "node_modules/proxy-from-env": {
3685
  "version": "2.1.0",
3686
  "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz",
 
3742
  "react-dom": "^19.0.0"
3743
  }
3744
  },
3745
+ "node_modules/react-markdown": {
3746
+ "version": "10.1.0",
3747
+ "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-10.1.0.tgz",
3748
+ "integrity": "sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==",
3749
+ "license": "MIT",
3750
+ "dependencies": {
3751
+ "@types/hast": "^3.0.0",
3752
+ "@types/mdast": "^4.0.0",
3753
+ "devlop": "^1.0.0",
3754
+ "hast-util-to-jsx-runtime": "^2.0.0",
3755
+ "html-url-attributes": "^3.0.0",
3756
+ "mdast-util-to-hast": "^13.0.0",
3757
+ "remark-parse": "^11.0.0",
3758
+ "remark-rehype": "^11.0.0",
3759
+ "unified": "^11.0.0",
3760
+ "unist-util-visit": "^5.0.0",
3761
+ "vfile": "^6.0.0"
3762
+ },
3763
+ "funding": {
3764
+ "type": "opencollective",
3765
+ "url": "https://opencollective.com/unified"
3766
+ },
3767
+ "peerDependencies": {
3768
+ "@types/react": ">=18",
3769
+ "react": ">=18"
3770
+ }
3771
+ },
3772
  "node_modules/react-redux": {
3773
  "version": "9.2.0",
3774
  "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz",
 
3875
  "redux": "^5.0.0"
3876
  }
3877
  },
3878
+ "node_modules/remark-parse": {
3879
+ "version": "11.0.0",
3880
+ "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz",
3881
+ "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==",
3882
+ "license": "MIT",
3883
+ "dependencies": {
3884
+ "@types/mdast": "^4.0.0",
3885
+ "mdast-util-from-markdown": "^2.0.0",
3886
+ "micromark-util-types": "^2.0.0",
3887
+ "unified": "^11.0.0"
3888
+ },
3889
+ "funding": {
3890
+ "type": "opencollective",
3891
+ "url": "https://opencollective.com/unified"
3892
+ }
3893
+ },
3894
+ "node_modules/remark-rehype": {
3895
+ "version": "11.1.2",
3896
+ "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.2.tgz",
3897
+ "integrity": "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==",
3898
+ "license": "MIT",
3899
+ "dependencies": {
3900
+ "@types/hast": "^3.0.0",
3901
+ "@types/mdast": "^4.0.0",
3902
+ "mdast-util-to-hast": "^13.0.0",
3903
+ "unified": "^11.0.0",
3904
+ "vfile": "^6.0.0"
3905
+ },
3906
+ "funding": {
3907
+ "type": "opencollective",
3908
+ "url": "https://opencollective.com/unified"
3909
+ }
3910
+ },
3911
  "node_modules/reselect": {
3912
  "version": "5.1.1",
3913
  "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz",
 
4010
  "node": ">=0.10.0"
4011
  }
4012
  },
4013
+ "node_modules/space-separated-tokens": {
4014
+ "version": "2.0.2",
4015
+ "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz",
4016
+ "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==",
4017
+ "license": "MIT",
4018
+ "funding": {
4019
+ "type": "github",
4020
+ "url": "https://github.com/sponsors/wooorm"
4021
+ }
4022
+ },
4023
+ "node_modules/stringify-entities": {
4024
+ "version": "4.0.4",
4025
+ "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz",
4026
+ "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==",
4027
+ "license": "MIT",
4028
+ "dependencies": {
4029
+ "character-entities-html4": "^2.0.0",
4030
+ "character-entities-legacy": "^3.0.0"
4031
+ },
4032
+ "funding": {
4033
+ "type": "github",
4034
+ "url": "https://github.com/sponsors/wooorm"
4035
+ }
4036
+ },
4037
+ "node_modules/style-to-js": {
4038
+ "version": "1.1.21",
4039
+ "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.21.tgz",
4040
+ "integrity": "sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==",
4041
+ "license": "MIT",
4042
+ "dependencies": {
4043
+ "style-to-object": "1.0.14"
4044
+ }
4045
+ },
4046
+ "node_modules/style-to-object": {
4047
+ "version": "1.0.14",
4048
+ "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.14.tgz",
4049
+ "integrity": "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==",
4050
+ "license": "MIT",
4051
+ "dependencies": {
4052
+ "inline-style-parser": "0.2.7"
4053
+ }
4054
+ },
4055
  "node_modules/tiny-invariant": {
4056
  "version": "1.3.3",
4057
  "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz",
 
4075
  "url": "https://github.com/sponsors/SuperchupuDev"
4076
  }
4077
  },
4078
+ "node_modules/trim-lines": {
4079
+ "version": "3.0.1",
4080
+ "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz",
4081
+ "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==",
4082
+ "license": "MIT",
4083
+ "funding": {
4084
+ "type": "github",
4085
+ "url": "https://github.com/sponsors/wooorm"
4086
+ }
4087
+ },
4088
+ "node_modules/trough": {
4089
+ "version": "2.2.0",
4090
+ "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz",
4091
+ "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==",
4092
+ "license": "MIT",
4093
+ "funding": {
4094
+ "type": "github",
4095
+ "url": "https://github.com/sponsors/wooorm"
4096
+ }
4097
+ },
4098
  "node_modules/tslib": {
4099
  "version": "2.8.1",
4100
  "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
 
4114
  "node": ">= 0.8.0"
4115
  }
4116
  },
4117
+ "node_modules/unified": {
4118
+ "version": "11.0.5",
4119
+ "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz",
4120
+ "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==",
4121
+ "license": "MIT",
4122
+ "dependencies": {
4123
+ "@types/unist": "^3.0.0",
4124
+ "bail": "^2.0.0",
4125
+ "devlop": "^1.0.0",
4126
+ "extend": "^3.0.0",
4127
+ "is-plain-obj": "^4.0.0",
4128
+ "trough": "^2.0.0",
4129
+ "vfile": "^6.0.0"
4130
+ },
4131
+ "funding": {
4132
+ "type": "opencollective",
4133
+ "url": "https://opencollective.com/unified"
4134
+ }
4135
+ },
4136
+ "node_modules/unist-util-is": {
4137
+ "version": "6.0.1",
4138
+ "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz",
4139
+ "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==",
4140
+ "license": "MIT",
4141
+ "dependencies": {
4142
+ "@types/unist": "^3.0.0"
4143
+ },
4144
+ "funding": {
4145
+ "type": "opencollective",
4146
+ "url": "https://opencollective.com/unified"
4147
+ }
4148
+ },
4149
+ "node_modules/unist-util-position": {
4150
+ "version": "5.0.0",
4151
+ "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz",
4152
+ "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==",
4153
+ "license": "MIT",
4154
+ "dependencies": {
4155
+ "@types/unist": "^3.0.0"
4156
+ },
4157
+ "funding": {
4158
+ "type": "opencollective",
4159
+ "url": "https://opencollective.com/unified"
4160
+ }
4161
+ },
4162
+ "node_modules/unist-util-stringify-position": {
4163
+ "version": "4.0.0",
4164
+ "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz",
4165
+ "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==",
4166
+ "license": "MIT",
4167
+ "dependencies": {
4168
+ "@types/unist": "^3.0.0"
4169
+ },
4170
+ "funding": {
4171
+ "type": "opencollective",
4172
+ "url": "https://opencollective.com/unified"
4173
+ }
4174
+ },
4175
+ "node_modules/unist-util-visit": {
4176
+ "version": "5.1.0",
4177
+ "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.1.0.tgz",
4178
+ "integrity": "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==",
4179
+ "license": "MIT",
4180
+ "dependencies": {
4181
+ "@types/unist": "^3.0.0",
4182
+ "unist-util-is": "^6.0.0",
4183
+ "unist-util-visit-parents": "^6.0.0"
4184
+ },
4185
+ "funding": {
4186
+ "type": "opencollective",
4187
+ "url": "https://opencollective.com/unified"
4188
+ }
4189
+ },
4190
+ "node_modules/unist-util-visit-parents": {
4191
+ "version": "6.0.2",
4192
+ "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz",
4193
+ "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==",
4194
+ "license": "MIT",
4195
+ "dependencies": {
4196
+ "@types/unist": "^3.0.0",
4197
+ "unist-util-is": "^6.0.0"
4198
+ },
4199
+ "funding": {
4200
+ "type": "opencollective",
4201
+ "url": "https://opencollective.com/unified"
4202
+ }
4203
+ },
4204
  "node_modules/update-browserslist-db": {
4205
  "version": "1.2.3",
4206
  "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
 
4251
  "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
4252
  }
4253
  },
4254
+ "node_modules/vfile": {
4255
+ "version": "6.0.3",
4256
+ "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz",
4257
+ "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==",
4258
+ "license": "MIT",
4259
+ "dependencies": {
4260
+ "@types/unist": "^3.0.0",
4261
+ "vfile-message": "^4.0.0"
4262
+ },
4263
+ "funding": {
4264
+ "type": "opencollective",
4265
+ "url": "https://opencollective.com/unified"
4266
+ }
4267
+ },
4268
+ "node_modules/vfile-message": {
4269
+ "version": "4.0.3",
4270
+ "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz",
4271
+ "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==",
4272
+ "license": "MIT",
4273
+ "dependencies": {
4274
+ "@types/unist": "^3.0.0",
4275
+ "unist-util-stringify-position": "^4.0.0"
4276
+ },
4277
+ "funding": {
4278
+ "type": "opencollective",
4279
+ "url": "https://opencollective.com/unified"
4280
+ }
4281
+ },
4282
  "node_modules/victory-vendor": {
4283
  "version": "37.3.6",
4284
  "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-37.3.6.tgz",
 
4447
  "peerDependencies": {
4448
  "zod": "^3.25.0 || ^4.0.0"
4449
  }
4450
+ },
4451
+ "node_modules/zwitch": {
4452
+ "version": "2.0.4",
4453
+ "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz",
4454
+ "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==",
4455
+ "license": "MIT",
4456
+ "funding": {
4457
+ "type": "github",
4458
+ "url": "https://github.com/sponsors/wooorm"
4459
+ }
4460
  }
4461
  }
4462
  }
frontend/package.json CHANGED
@@ -18,6 +18,7 @@
18
  "react": "^19.2.5",
19
  "react-dom": "^19.2.5",
20
  "react-leaflet": "^5.0.0",
 
21
  "react-router-dom": "^7.14.2",
22
  "recharts": "^3.8.1"
23
  },
 
18
  "react": "^19.2.5",
19
  "react-dom": "^19.2.5",
20
  "react-leaflet": "^5.0.0",
21
+ "react-markdown": "^10.1.0",
22
  "react-router-dom": "^7.14.2",
23
  "recharts": "^3.8.1"
24
  },
frontend/src/components/ChatWidget.jsx CHANGED
@@ -1,5 +1,6 @@
1
  import React, { useState, useRef, useEffect } from 'react';
2
  import axios from 'axios';
 
3
  import { MessageCircle, X, Send, Sparkles, Bot, ShieldCheck } from 'lucide-react';
4
  import { API_BASE } from '../App';
5
 
@@ -203,18 +204,40 @@ export default function ChatWidget({ scope = 'public', studentId = null }) {
203
  );
204
  }
205
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
206
  function Bubble({ who, children }) {
207
  const isUser = who === 'user';
 
208
  return (
209
  <div style={{ display: 'flex', justifyContent: isUser ? 'flex-end' : 'flex-start' }}>
210
  <div style={{
211
- maxWidth: '85%', fontSize: '0.85rem', lineHeight: 1.45, whiteSpace: 'pre-wrap',
212
  padding: '9px 12px', borderRadius: isUser ? '12px 12px 3px 12px' : '12px 12px 12px 3px',
213
  background: isUser ? 'var(--navy, #1B2C5E)' : 'var(--card-raised, #fff)',
214
  color: isUser ? '#fff' : 'var(--ink, #1A1A1A)',
215
  border: isUser ? 'none' : '1px solid var(--card-edge, rgba(0,0,0,0.12))',
216
  }}>
217
- {children}
 
 
 
218
  </div>
219
  </div>
220
  );
 
1
  import React, { useState, useRef, useEffect } from 'react';
2
  import axios from 'axios';
3
+ import ReactMarkdown from 'react-markdown';
4
  import { MessageCircle, X, Send, Sparkles, Bot, ShieldCheck } from 'lucide-react';
5
  import { API_BASE } from '../App';
6
 
 
204
  );
205
  }
206
 
207
+ const mdComponents = {
208
+ p: ({ children }) => <p style={{ margin: '0 0 0.5em', lineHeight: 1.55 }}>{children}</p>,
209
+ strong: ({ children }) => <strong style={{ fontWeight: 700 }}>{children}</strong>,
210
+ em: ({ children }) => <em style={{ fontStyle: 'italic' }}>{children}</em>,
211
+ ul: ({ children }) => <ul style={{ margin: '0.3em 0 0.55em', paddingLeft: '1.3em', listStyle: 'disc' }}>{children}</ul>,
212
+ ol: ({ children }) => <ol style={{ margin: '0.3em 0 0.55em', paddingLeft: '1.3em', listStyle: 'decimal' }}>{children}</ol>,
213
+ li: ({ children }) => <li style={{ marginBottom: '0.2em', lineHeight: 1.5 }}>{children}</li>,
214
+ code: ({ inline, children }) => inline
215
+ ? <code style={{ fontFamily: 'monospace', fontSize: '0.82em', background: 'rgba(0,0,0,0.08)', borderRadius: '3px', padding: '1px 5px' }}>{children}</code>
216
+ : <pre style={{ fontFamily: 'monospace', fontSize: '0.8em', background: 'rgba(0,0,0,0.06)', borderRadius: '6px', padding: '0.6em 0.8em', overflowX: 'auto', margin: '0.4em 0' }}><code>{children}</code></pre>,
217
+ h1: ({ children }) => <div style={{ fontWeight: 800, fontSize: '1em', marginBottom: '0.35em' }}>{children}</div>,
218
+ h2: ({ children }) => <div style={{ fontWeight: 700, fontSize: '0.95em', marginBottom: '0.3em' }}>{children}</div>,
219
+ h3: ({ children }) => <div style={{ fontWeight: 700, fontSize: '0.9em', marginBottom: '0.25em' }}>{children}</div>,
220
+ hr: () => <hr style={{ border: 'none', borderTop: '1px solid rgba(0,0,0,0.1)', margin: '0.5em 0' }} />,
221
+ a: ({ href, children }) => <a href={href} target="_blank" rel="noreferrer" style={{ color: 'var(--signal, #C2410C)', textDecoration: 'underline' }}>{children}</a>,
222
+ blockquote: ({ children }) => <blockquote style={{ borderLeft: '3px solid rgba(0,0,0,0.15)', paddingLeft: '0.75em', margin: '0.3em 0', color: 'rgba(0,0,0,0.6)', fontStyle: 'italic' }}>{children}</blockquote>,
223
+ };
224
+
225
  function Bubble({ who, children }) {
226
  const isUser = who === 'user';
227
+ const text = typeof children === 'string' ? children : '';
228
  return (
229
  <div style={{ display: 'flex', justifyContent: isUser ? 'flex-end' : 'flex-start' }}>
230
  <div style={{
231
+ maxWidth: '85%', fontSize: '0.85rem',
232
  padding: '9px 12px', borderRadius: isUser ? '12px 12px 3px 12px' : '12px 12px 12px 3px',
233
  background: isUser ? 'var(--navy, #1B2C5E)' : 'var(--card-raised, #fff)',
234
  color: isUser ? '#fff' : 'var(--ink, #1A1A1A)',
235
  border: isUser ? 'none' : '1px solid var(--card-edge, rgba(0,0,0,0.12))',
236
  }}>
237
+ {isUser
238
+ ? <span style={{ whiteSpace: 'pre-wrap', lineHeight: 1.45 }}>{text}</span>
239
+ : <div className="chat-md"><ReactMarkdown components={mdComponents}>{text}</ReactMarkdown></div>
240
+ }
241
  </div>
242
  </div>
243
  );
frontend/src/index.css CHANGED
@@ -3320,3 +3320,6 @@ textarea.select-input { padding: 0.7rem 0.9rem; min-height: 84px; }
3320
  .notif-panel { width: calc(100vw - 1.5rem); }
3321
  .btn { padding: 0.55rem 0.95rem; font-size: 0.72rem; }
3322
  }
 
 
 
 
3320
  .notif-panel { width: calc(100vw - 1.5rem); }
3321
  .btn { padding: 0.55rem 0.95rem; font-size: 0.72rem; }
3322
  }
3323
+
3324
+ /* Chat bubble markdown β€” kill trailing paragraph gap */
3325
+ .chat-md > p:last-child { margin-bottom: 0 !important; }
frontend/src/pages/student/MyProfile.jsx CHANGED
@@ -4,7 +4,7 @@ import {
4
  User, GraduationCap, Briefcase, Target, Save, Plus, X, RefreshCw,
5
  CheckCircle2, AlertTriangle, ShieldAlert, TrendingUp, TrendingDown,
6
  Info, Award, Code2, MessageSquare, Wrench, Minus, ShieldCheck, Lock,
7
- Clock, XCircle,
8
  } from 'lucide-react';
9
  import { useAuth } from '../../context/AuthContext';
10
  import { API_BASE } from '../../App';
@@ -16,6 +16,7 @@ const TABS = [
16
  { id: 'employability', label: 'Employability', icon: Briefcase },
17
  { id: 'placement', label: 'Placement', icon: Target },
18
  { id: 'verification', label: 'Verification', icon: ShieldCheck },
 
19
  ];
20
  const INTERNSHIP_TIERS = ['MNC', 'Unicorn', 'MidSize', 'Startup', 'Other'];
21
  const INTERVIEW_STATUSES = ['', 'Applied', 'Shortlisted', 'Interview Scheduled', 'Selected', 'Offer Received', 'Rejected'];
@@ -1141,6 +1142,437 @@ function VerificationTab({ sid, richProfile, initialVerif, onVerifUpdate }) {
1141
  );
1142
  }
1143
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1144
  // ─── Main page ────────────────────────────────────────────────────────────────
1145
  export default function MyProfile() {
1146
  const { user } = useAuth();
@@ -1308,6 +1740,34 @@ export default function MyProfile() {
1308
  onVerifUpdate={updateVerification}
1309
  />
1310
  )}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1311
  </div>
1312
  </div>
1313
  );
 
4
  User, GraduationCap, Briefcase, Target, Save, Plus, X, RefreshCw,
5
  CheckCircle2, AlertTriangle, ShieldAlert, TrendingUp, TrendingDown,
6
  Info, Award, Code2, MessageSquare, Wrench, Minus, ShieldCheck, Lock,
7
+ Clock, XCircle, FileText, Upload, Sparkles, BarChart2,
8
  } from 'lucide-react';
9
  import { useAuth } from '../../context/AuthContext';
10
  import { API_BASE } from '../../App';
 
16
  { id: 'employability', label: 'Employability', icon: Briefcase },
17
  { id: 'placement', label: 'Placement', icon: Target },
18
  { id: 'verification', label: 'Verification', icon: ShieldCheck },
19
+ { id: 'resume', label: 'Resume & ATS', icon: FileText },
20
  ];
21
  const INTERNSHIP_TIERS = ['MNC', 'Unicorn', 'MidSize', 'Startup', 'Other'];
22
  const INTERVIEW_STATUSES = ['', 'Applied', 'Shortlisted', 'Interview Scheduled', 'Selected', 'Offer Received', 'Rejected'];
 
1142
  );
1143
  }
1144
 
1145
+ // ─── Tab: Resume & ATS ───────────────────────────────────────────────────────
1146
+ const JOB_PROFILES = [
1147
+ 'Software Engineer – Backend',
1148
+ 'Software Engineer – Frontend / Full-Stack',
1149
+ 'Data Analyst',
1150
+ 'Data Scientist / ML Engineer',
1151
+ 'Business Analyst',
1152
+ 'Product Manager',
1153
+ 'Finance Analyst / Investment Banking',
1154
+ 'Marketing Manager',
1155
+ 'Human Resources Manager',
1156
+ 'Operations Manager',
1157
+ 'Supply Chain Analyst',
1158
+ 'Healthcare Administrator',
1159
+ 'Clinical Research Associate',
1160
+ 'Embedded Systems / Hardware Engineer',
1161
+ 'DevOps / Cloud Engineer',
1162
+ ];
1163
+
1164
+ const gradeColor = g => {
1165
+ if (!g) return 'var(--ink-faint)';
1166
+ if (g.startsWith('A')) return 'var(--risk-low)';
1167
+ if (g.startsWith('B')) return 'var(--signal)';
1168
+ if (g.startsWith('C')) return 'var(--risk-medium)';
1169
+ return 'var(--risk-high)';
1170
+ };
1171
+
1172
+ function ScoreBar({ label, score, max = 100 }) {
1173
+ const pct = Math.min(100, Math.round((score / max) * 100));
1174
+ const color = pct >= 70 ? 'var(--risk-low)' : pct >= 50 ? 'var(--signal)' : 'var(--risk-high)';
1175
+ return (
1176
+ <div style={{ marginBottom: '0.65rem' }}>
1177
+ <div style={{ display: 'flex', justifyContent: 'space-between', fontSize: '0.78rem', marginBottom: '0.25rem' }}>
1178
+ <span style={{ color: 'var(--ink-muted)' }}>{label}</span>
1179
+ <span className="mono" style={{ fontWeight: 700, color }}>{score}<span style={{ color: 'var(--ink-faint)', fontWeight: 400 }}>/100</span></span>
1180
+ </div>
1181
+ <div style={{ height: '5px', background: 'var(--paper-deep)', borderRadius: '3px', overflow: 'hidden', border: '1px solid var(--card-edge)' }}>
1182
+ <div style={{ height: '100%', width: `${pct}%`, background: color, borderRadius: '3px', transition: 'width 0.5s ease' }} />
1183
+ </div>
1184
+ </div>
1185
+ );
1186
+ }
1187
+
1188
+ function ResumeTab({ data, onChange, studentId }) {
1189
+ const [file, setFile] = useState(null);
1190
+ const [dragging, setDragging] = useState(false);
1191
+ const [parsing, setParsing] = useState(false);
1192
+ const [parsed, setParsed] = useState(null);
1193
+ const [resumeText, setResumeText] = useState('');
1194
+ const [parseErr, setParseErr] = useState(null);
1195
+ const [applied, setApplied] = useState(false);
1196
+
1197
+ const [jobProfile, setJobProfile] = useState('');
1198
+ const [scoring, setScoring] = useState(false);
1199
+ const [atsResult, setAtsResult] = useState(null);
1200
+ const [atsErr, setAtsErr] = useState(null);
1201
+
1202
+ const fileInputRef = useState(null);
1203
+
1204
+ const pickFile = f => {
1205
+ if (!f) return;
1206
+ const ok = ['application/pdf',
1207
+ 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
1208
+ 'text/plain'].includes(f.type) || f.name.endsWith('.pdf') || f.name.endsWith('.docx');
1209
+ if (!ok) { setParseErr('Please upload a PDF, DOCX, or plain-text file.'); return; }
1210
+ setFile(f);
1211
+ setParsed(null);
1212
+ setResumeText('');
1213
+ setParseErr(null);
1214
+ setAtsResult(null);
1215
+ setAtsErr(null);
1216
+ setApplied(false);
1217
+ };
1218
+
1219
+ const handleDrop = e => {
1220
+ e.preventDefault();
1221
+ setDragging(false);
1222
+ pickFile(e.dataTransfer.files?.[0]);
1223
+ };
1224
+
1225
+ const handleParse = async () => {
1226
+ if (!file) return;
1227
+ setParsing(true);
1228
+ setParseErr(null);
1229
+ setParsed(null);
1230
+ setAtsResult(null);
1231
+ try {
1232
+ const fd = new FormData();
1233
+ fd.append('file', file);
1234
+ const r = await axios.post(`${API_BASE}/api/v1/student/${studentId}/resume/parse`, fd, {
1235
+ headers: { 'Content-Type': 'multipart/form-data' },
1236
+ });
1237
+ setParsed(r.data.parsed);
1238
+ setResumeText(r.data.resume_text || '');
1239
+ } catch (e) {
1240
+ setParseErr(e?.response?.data?.detail || 'Parse failed β€” check that the backend is running and an LLM key is set.');
1241
+ } finally {
1242
+ setParsing(false);
1243
+ }
1244
+ };
1245
+
1246
+ const applyParsed = () => {
1247
+ if (!parsed || !onChange) return;
1248
+ const p = parsed;
1249
+ const updated = { ...data };
1250
+ // Personal
1251
+ if (p.full_name) updated.full_name = p.full_name;
1252
+ if (p.email) updated.email = p.email;
1253
+ if (p.mobile) updated.mobile = p.mobile;
1254
+ if (p.city) updated.city = p.city;
1255
+ if (p.state) updated.state = p.state;
1256
+ if (p.graduation_year) updated.graduation_year = p.graduation_year;
1257
+ // Academic
1258
+ if (p.cgpa) updated.cgpa = p.cgpa;
1259
+ if (p.achievements?.length) updated.achievements = p.achievements;
1260
+ if (p.coding_problems_solved != null) updated.coding_problems_solved = p.coding_problems_solved;
1261
+ if (p.hackathons_attended != null) updated.hackathons_attended = p.hackathons_attended;
1262
+ // Skills
1263
+ if (p.skills) updated.skills = {
1264
+ languages: p.skills.languages?.length ? p.skills.languages : (data.skills?.languages || []),
1265
+ technical: p.skills.technical?.length ? p.skills.technical : (data.skills?.technical || []),
1266
+ tools: p.skills.tools?.length ? p.skills.tools : (data.skills?.tools || []),
1267
+ soft: p.skills.soft?.length ? p.skills.soft : (data.skills?.soft || []),
1268
+ };
1269
+ // Employability lists β€” merge, don't overwrite if resume had none
1270
+ if (p.internships?.length) updated.internships = p.internships;
1271
+ if (p.certifications?.length) updated.certifications = p.certifications;
1272
+ if (p.projects?.length) updated.projects = p.projects;
1273
+ onChange(updated);
1274
+ setApplied(true);
1275
+ };
1276
+
1277
+ const handleAtsScore = async () => {
1278
+ if (!resumeText || !jobProfile) return;
1279
+ setScoring(true);
1280
+ setAtsErr(null);
1281
+ setAtsResult(null);
1282
+ try {
1283
+ const r = await axios.post(`${API_BASE}/api/v1/student/${studentId}/resume/ats-score`, {
1284
+ resume_text: resumeText,
1285
+ job_profile: jobProfile,
1286
+ });
1287
+ setAtsResult(r.data);
1288
+ } catch (e) {
1289
+ setAtsErr(e?.response?.data?.detail || 'ATS scoring failed β€” check that an LLM API key is configured.');
1290
+ } finally {
1291
+ setScoring(false);
1292
+ }
1293
+ };
1294
+
1295
+ const bd = atsResult?.breakdown || {};
1296
+
1297
+ return (
1298
+ <div>
1299
+ {/* ── Upload zone ─────────────────────────────────────────────────── */}
1300
+ <div className="card" style={{ marginBottom: '1rem' }}>
1301
+ <div className="card-title" style={{ marginBottom: '0.85rem' }}><Upload size={13}/> Upload Resume</div>
1302
+
1303
+ {/* Drop zone */}
1304
+ <div
1305
+ onDragOver={e => { e.preventDefault(); setDragging(true); }}
1306
+ onDragLeave={() => setDragging(false)}
1307
+ onDrop={handleDrop}
1308
+ onClick={() => document.getElementById('resume-file-input').click()}
1309
+ style={{
1310
+ border: `2px dashed ${dragging ? 'var(--accent-primary)' : 'var(--card-edge-strong)'}`,
1311
+ borderRadius: '6px',
1312
+ padding: '2.5rem 1.5rem',
1313
+ textAlign: 'center',
1314
+ cursor: 'pointer',
1315
+ background: dragging ? 'rgba(var(--accent-primary-rgb, 30,100,220),0.04)' : 'var(--paper-deep)',
1316
+ transition: 'border-color 0.2s, background 0.2s',
1317
+ marginBottom: '1rem',
1318
+ }}
1319
+ >
1320
+ <FileText size={32} color="var(--ink-faint)" style={{ marginBottom: '0.65rem' }} />
1321
+ <div style={{ fontWeight: 600, color: 'var(--ink)', fontSize: '0.9rem', marginBottom: '0.3rem' }}>
1322
+ {file ? file.name : 'Drop your resume here'}
1323
+ </div>
1324
+ <div style={{ fontSize: '0.74rem', color: 'var(--ink-faint)' }}>
1325
+ {file ? `${(file.size / 1024).toFixed(0)} KB Β· click to change` : 'PDF or DOCX Β· click to browse Β· max 5 MB'}
1326
+ </div>
1327
+ <input
1328
+ id="resume-file-input"
1329
+ type="file"
1330
+ accept=".pdf,.docx,.txt"
1331
+ style={{ display: 'none' }}
1332
+ onChange={e => pickFile(e.target.files?.[0])}
1333
+ />
1334
+ </div>
1335
+
1336
+ {parseErr && (
1337
+ <div style={{ padding: '0.5rem 0.75rem', background: 'rgba(168,40,40,0.07)', border: '1px solid rgba(168,40,40,0.2)', borderRadius: '3px', fontSize: '0.8rem', color: 'var(--risk-high)', marginBottom: '0.75rem', display: 'flex', gap: '6px', alignItems: 'flex-start' }}>
1338
+ <AlertTriangle size={13} style={{ marginTop: '1px', flexShrink: 0 }}/>{parseErr}
1339
+ </div>
1340
+ )}
1341
+
1342
+ <button
1343
+ className="btn btn-primary"
1344
+ onClick={handleParse}
1345
+ disabled={!file || parsing}
1346
+ style={{ opacity: (!file || parsing) ? 0.5 : 1 }}
1347
+ >
1348
+ {parsing
1349
+ ? <><RefreshCw size={14} style={{ animation: 'spin 1s linear infinite' }}/> Parsing…</>
1350
+ : <><Sparkles size={14}/> Parse &amp; Autofill Profile</>}
1351
+ </button>
1352
+ <p style={{ marginTop: '0.55rem', fontSize: '0.72rem', color: 'var(--ink-faint)', lineHeight: 1.5 }}>
1353
+ The AI reads your resume and pre-fills Personal, Academic, and Employability fields.
1354
+ Review the preview below before applying.
1355
+ </p>
1356
+ </div>
1357
+
1358
+ {/* ── Parsed preview ──────────────────────────────────────────────── */}
1359
+ {parsed && !parsed._error && (
1360
+ <div className="card animate-fade-up" style={{ marginBottom: '1rem', borderLeft: '4px solid var(--risk-low)' }}>
1361
+ <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '1rem' }}>
1362
+ <div className="card-title"><CheckCircle2 size={13} color="var(--risk-low)"/> Resume parsed β€” review before applying</div>
1363
+ {applied && (
1364
+ <span style={{ fontSize: '0.72rem', color: 'var(--risk-low)', fontWeight: 700, display: 'flex', alignItems: 'center', gap: '4px' }}>
1365
+ <CheckCircle2 size={11}/> Applied to profile
1366
+ </span>
1367
+ )}
1368
+ </div>
1369
+
1370
+ <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(220px, 1fr))', gap: '0.5rem 1.5rem', marginBottom: '1rem' }}>
1371
+ {[
1372
+ ['Name', parsed.full_name],
1373
+ ['Email', parsed.email],
1374
+ ['Mobile', parsed.mobile],
1375
+ ['City / State', [parsed.city, parsed.state].filter(Boolean).join(', ')],
1376
+ ['CGPA', parsed.cgpa],
1377
+ ['Graduation', parsed.graduation_year],
1378
+ ].map(([label, val]) => val ? (
1379
+ <div key={label} style={{ fontSize: '0.82rem' }}>
1380
+ <span style={{ fontSize: '0.68rem', fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.1em', color: 'var(--ink-faint)', display: 'block', marginBottom: '2px' }}>{label}</span>
1381
+ <span style={{ color: 'var(--ink)', fontWeight: 500 }}>{String(val)}</span>
1382
+ </div>
1383
+ ) : null)}
1384
+ </div>
1385
+
1386
+ {/* Skills chips */}
1387
+ {Object.entries(parsed.skills || {}).some(([, v]) => v?.length > 0) && (
1388
+ <div style={{ marginBottom: '0.85rem' }}>
1389
+ <div style={{ fontSize: '0.68rem', fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.1em', color: 'var(--ink-faint)', marginBottom: '0.4rem' }}>Skills extracted</div>
1390
+ <div style={{ display: 'flex', flexWrap: 'wrap', gap: '4px' }}>
1391
+ {[...(parsed.skills.languages || []), ...(parsed.skills.technical || []), ...(parsed.skills.tools || [])].slice(0, 20).map(s => (
1392
+ <span key={s} style={{ fontSize: '0.72rem', padding: '2px 8px', background: 'var(--paper-deep)', border: '1px solid var(--card-edge)', borderRadius: '2px', color: 'var(--ink)' }}>{s}</span>
1393
+ ))}
1394
+ </div>
1395
+ </div>
1396
+ )}
1397
+
1398
+ {/* Counts */}
1399
+ <div style={{ display: 'flex', gap: '1.5rem', flexWrap: 'wrap', marginBottom: '1rem', fontSize: '0.82rem', color: 'var(--ink-muted)' }}>
1400
+ {parsed.internships?.length > 0 && <span><strong style={{ color: 'var(--ink)' }}>{parsed.internships.length}</strong> internship{parsed.internships.length > 1 ? 's' : ''}</span>}
1401
+ {parsed.certifications?.length > 0 && <span><strong style={{ color: 'var(--ink)' }}>{parsed.certifications.length}</strong> certification{parsed.certifications.length > 1 ? 's' : ''}</span>}
1402
+ {parsed.projects?.length > 0 && <span><strong style={{ color: 'var(--ink)' }}>{parsed.projects.length}</strong> project{parsed.projects.length > 1 ? 's' : ''}</span>}
1403
+ {parsed.achievements?.length > 0 && <span><strong style={{ color: 'var(--ink)' }}>{parsed.achievements.length}</strong> achievement{parsed.achievements.length > 1 ? 's' : ''}</span>}
1404
+ </div>
1405
+
1406
+ <div style={{ display: 'flex', gap: '0.6rem', flexWrap: 'wrap' }}>
1407
+ <button
1408
+ className="btn btn-primary"
1409
+ onClick={applyParsed}
1410
+ disabled={applied}
1411
+ style={{ opacity: applied ? 0.5 : 1 }}
1412
+ >
1413
+ <CheckCircle2 size={14}/> {applied ? 'Applied' : 'Apply to Profile'}
1414
+ </button>
1415
+ <button className="btn btn-ghost" onClick={() => { setParsed(null); setApplied(false); }}>
1416
+ <X size={14}/> Discard
1417
+ </button>
1418
+ </div>
1419
+ {applied && (
1420
+ <p style={{ marginTop: '0.5rem', fontSize: '0.72rem', color: 'var(--ink-faint)' }}>
1421
+ Fields populated β€” switch to Personal / Academic / Employability tabs to review, then save each tab.
1422
+ </p>
1423
+ )}
1424
+ </div>
1425
+ )}
1426
+
1427
+ {parsed?._error && (
1428
+ <div style={{ padding: '0.6rem 0.9rem', background: 'rgba(168,40,40,0.07)', border: '1px solid rgba(168,40,40,0.2)', borderRadius: '3px', fontSize: '0.8rem', color: 'var(--risk-high)', marginBottom: '1rem' }}>
1429
+ <AlertTriangle size={12} style={{ verticalAlign: '-2px', marginRight: '5px' }}/>Parse error: {parsed._error}
1430
+ </div>
1431
+ )}
1432
+
1433
+ {/* ── ATS Scoring ──────────────────────────────────────────────────── */}
1434
+ <div className="card">
1435
+ <div className="card-title" style={{ marginBottom: '0.85rem' }}><BarChart2 size={13}/> ATS Score</div>
1436
+ <p style={{ fontSize: '0.8rem', color: 'var(--ink-muted)', marginBottom: '1rem', lineHeight: 1.55 }}>
1437
+ Select a target job profile and score your resume against what recruiters' ATS systems look for.
1438
+ Upload and parse your resume first β€” the text carries over automatically.
1439
+ </p>
1440
+
1441
+ <div style={{ display: 'flex', gap: '0.6rem', flexWrap: 'wrap', alignItems: 'flex-end', marginBottom: '1rem' }}>
1442
+ <div style={{ flex: 1, minWidth: '220px' }}>
1443
+ <div style={{ fontSize: '0.68rem', fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.1em', color: 'var(--ink-faint)', marginBottom: '0.4rem' }}>Target job profile</div>
1444
+ <select
1445
+ className="select-input"
1446
+ value={jobProfile}
1447
+ onChange={e => setJobProfile(e.target.value)}
1448
+ style={{ width: '100%' }}
1449
+ >
1450
+ <option value="">β€” Select a role β€”</option>
1451
+ {JOB_PROFILES.map(p => <option key={p} value={p}>{p}</option>)}
1452
+ </select>
1453
+ </div>
1454
+ <button
1455
+ className="btn btn-primary"
1456
+ onClick={handleAtsScore}
1457
+ disabled={!resumeText || !jobProfile || scoring}
1458
+ style={{ opacity: (!resumeText || !jobProfile || scoring) ? 0.5 : 1, flexShrink: 0 }}
1459
+ >
1460
+ {scoring
1461
+ ? <><RefreshCw size={14} style={{ animation: 'spin 1s linear infinite' }}/> Scoring…</>
1462
+ : <><BarChart2 size={14}/> Score Resume</>}
1463
+ </button>
1464
+ </div>
1465
+ {!resumeText && (
1466
+ <p style={{ fontSize: '0.72rem', color: 'var(--risk-medium)', display: 'flex', alignItems: 'center', gap: '5px' }}>
1467
+ <Info size={12}/> Parse your resume above first to enable ATS scoring.
1468
+ </p>
1469
+ )}
1470
+
1471
+ {atsErr && (
1472
+ <div style={{ padding: '0.5rem 0.75rem', background: 'rgba(168,40,40,0.07)', border: '1px solid rgba(168,40,40,0.2)', borderRadius: '3px', fontSize: '0.8rem', color: 'var(--risk-high)', marginTop: '0.5rem' }}>
1473
+ <AlertTriangle size={12} style={{ verticalAlign: '-2px', marginRight: '5px' }}/>{atsErr}
1474
+ </div>
1475
+ )}
1476
+
1477
+ {/* ATS result */}
1478
+ {atsResult && !atsResult._error && (
1479
+ <div className="animate-fade-up" style={{ marginTop: '1.25rem' }}>
1480
+
1481
+ {/* Hero score */}
1482
+ <div style={{ display: 'flex', alignItems: 'center', gap: '2rem', flexWrap: 'wrap', padding: '1rem 1.25rem', background: 'var(--paper-deep)', border: `1px solid ${gradeColor(atsResult.grade)}44`, borderRadius: '6px', marginBottom: '1.25rem' }}>
1483
+ <div style={{ textAlign: 'center' }}>
1484
+ <div style={{ fontFamily: 'var(--font-display)', fontSize: '3.5rem', lineHeight: 1, color: gradeColor(atsResult.grade), fontWeight: 400 }}>
1485
+ {atsResult.overall_score}
1486
+ </div>
1487
+ <div style={{ fontSize: '0.68rem', color: 'var(--ink-faint)', textTransform: 'uppercase', letterSpacing: '0.1em' }}>/ 100</div>
1488
+ </div>
1489
+ <div>
1490
+ <div style={{ fontFamily: 'var(--font-display)', fontSize: '2rem', color: gradeColor(atsResult.grade), fontWeight: 400, lineHeight: 1, marginBottom: '0.25rem' }}>
1491
+ {atsResult.grade}
1492
+ </div>
1493
+ <div style={{ fontSize: '0.78rem', color: 'var(--ink-muted)' }}>vs. <strong style={{ color: 'var(--ink)' }}>{atsResult.job_profile}</strong></div>
1494
+ </div>
1495
+ </div>
1496
+
1497
+ {/* Score bars */}
1498
+ <div style={{ marginBottom: '1.25rem' }}>
1499
+ <div style={{ fontSize: '0.68rem', fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.1em', color: 'var(--ink-faint)', marginBottom: '0.75rem' }}>Score breakdown</div>
1500
+ <ScoreBar label="Keyword Match (30%)" score={bd.keyword_match?.score ?? 0} />
1501
+ <ScoreBar label="Skills Alignment (25%)" score={bd.skills_alignment?.score ?? 0} />
1502
+ <ScoreBar label="Experience Relevance (20%)" score={bd.experience_relevance?.score ?? 0} />
1503
+ <ScoreBar label="Education Match (15%)" score={bd.education_match?.score ?? 0} />
1504
+ <ScoreBar label="Quantified Impact (10%)" score={bd.quantified_impact?.score ?? 0} />
1505
+ </div>
1506
+
1507
+ {/* Missing keywords */}
1508
+ {bd.keyword_match?.missing_keywords?.length > 0 && (
1509
+ <div style={{ marginBottom: '1rem' }}>
1510
+ <div style={{ fontSize: '0.68rem', fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.1em', color: 'var(--risk-high)', marginBottom: '0.4rem' }}>Missing keywords</div>
1511
+ <div style={{ display: 'flex', flexWrap: 'wrap', gap: '4px' }}>
1512
+ {bd.keyword_match.missing_keywords.map(k => (
1513
+ <span key={k} style={{ fontSize: '0.72rem', padding: '2px 8px', background: 'rgba(168,40,40,0.07)', border: '1px solid rgba(168,40,40,0.2)', borderRadius: '2px', color: 'var(--risk-high)' }}>{k}</span>
1514
+ ))}
1515
+ </div>
1516
+ </div>
1517
+ )}
1518
+
1519
+ {/* Found keywords */}
1520
+ {bd.keyword_match?.found_keywords?.length > 0 && (
1521
+ <div style={{ marginBottom: '1rem' }}>
1522
+ <div style={{ fontSize: '0.68rem', fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.1em', color: 'var(--risk-low)', marginBottom: '0.4rem' }}>Matched keywords</div>
1523
+ <div style={{ display: 'flex', flexWrap: 'wrap', gap: '4px' }}>
1524
+ {bd.keyword_match.found_keywords.map(k => (
1525
+ <span key={k} style={{ fontSize: '0.72rem', padding: '2px 8px', background: 'rgba(47,110,69,0.07)', border: '1px solid rgba(47,110,69,0.2)', borderRadius: '2px', color: 'var(--risk-low)' }}>{k}</span>
1526
+ ))}
1527
+ </div>
1528
+ </div>
1529
+ )}
1530
+
1531
+ {/* Strengths & Gaps */}
1532
+ <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '1rem', marginBottom: '1rem' }}>
1533
+ {atsResult.strengths?.length > 0 && (
1534
+ <div>
1535
+ <div style={{ fontSize: '0.68rem', fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.1em', color: 'var(--risk-low)', marginBottom: '0.4rem' }}>Strengths</div>
1536
+ {atsResult.strengths.map((s, i) => (
1537
+ <div key={i} style={{ display: 'flex', gap: '6px', fontSize: '0.79rem', color: 'var(--ink-soft)', marginBottom: '0.3rem', alignItems: 'flex-start' }}>
1538
+ <span style={{ width: '5px', height: '5px', borderRadius: '50%', background: 'var(--risk-low)', flexShrink: 0, marginTop: '6px' }}/>
1539
+ {s}
1540
+ </div>
1541
+ ))}
1542
+ </div>
1543
+ )}
1544
+ {atsResult.improvement_areas?.length > 0 && (
1545
+ <div>
1546
+ <div style={{ fontSize: '0.68rem', fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.1em', color: 'var(--risk-high)', marginBottom: '0.4rem' }}>Improvement areas</div>
1547
+ {atsResult.improvement_areas.map((s, i) => (
1548
+ <div key={i} style={{ display: 'flex', gap: '6px', fontSize: '0.79rem', color: 'var(--ink-soft)', marginBottom: '0.3rem', alignItems: 'flex-start' }}>
1549
+ <span style={{ width: '5px', height: '5px', borderRadius: '50%', background: 'var(--risk-high)', flexShrink: 0, marginTop: '6px' }}/>
1550
+ {s}
1551
+ </div>
1552
+ ))}
1553
+ </div>
1554
+ )}
1555
+ </div>
1556
+
1557
+ {/* Recommendations */}
1558
+ {atsResult.recommendations?.length > 0 && (
1559
+ <div style={{ padding: '0.85rem 1rem', background: 'rgba(var(--signal-rgb,30,80,180),0.05)', border: '1px solid var(--card-edge-strong)', borderRadius: '4px' }}>
1560
+ <div style={{ fontSize: '0.68rem', fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.1em', color: 'var(--signal)', marginBottom: '0.55rem' }}>Recommendations</div>
1561
+ {atsResult.recommendations.map((r, i) => (
1562
+ <div key={i} style={{ display: 'flex', gap: '8px', fontSize: '0.8rem', color: 'var(--ink-soft)', marginBottom: '0.4rem', alignItems: 'flex-start' }}>
1563
+ <span className="mono" style={{ color: 'var(--signal)', fontWeight: 700, flexShrink: 0, fontSize: '0.72rem', marginTop: '2px' }}>{String(i + 1).padStart(2, '0')}</span>
1564
+ {r}
1565
+ </div>
1566
+ ))}
1567
+ </div>
1568
+ )}
1569
+ </div>
1570
+ )}
1571
+ </div>
1572
+ </div>
1573
+ );
1574
+ }
1575
+
1576
  // ─── Main page ────────────────────────────────────────────────────────────────
1577
  export default function MyProfile() {
1578
  const { user } = useAuth();
 
1740
  onVerifUpdate={updateVerification}
1741
  />
1742
  )}
1743
+ {tab === 'resume' && (
1744
+ <ResumeTab
1745
+ data={{ ...personal, ...academic, ...employability }}
1746
+ onChange={merged => {
1747
+ setPersonal(p => ({ ...p,
1748
+ full_name: merged.full_name ?? p?.full_name,
1749
+ email: merged.email ?? p?.email,
1750
+ mobile: merged.mobile ?? p?.mobile,
1751
+ city: merged.city ?? p?.city,
1752
+ state: merged.state ?? p?.state,
1753
+ graduation_year: merged.graduation_year ?? p?.graduation_year,
1754
+ }));
1755
+ setAcademic(a => ({ ...a,
1756
+ cgpa: merged.cgpa ?? a?.cgpa,
1757
+ achievements: merged.achievements ?? a?.achievements,
1758
+ coding_problems_solved: merged.coding_problems_solved ?? a?.coding_problems_solved,
1759
+ hackathons_attended: merged.hackathons_attended ?? a?.hackathons_attended,
1760
+ }));
1761
+ setEmployability(e => ({ ...e,
1762
+ skills: merged.skills ?? e?.skills,
1763
+ internships: merged.internships ?? e?.internships,
1764
+ certifications: merged.certifications ?? e?.certifications,
1765
+ projects: merged.projects ?? e?.projects,
1766
+ }));
1767
+ }}
1768
+ studentId={sid}
1769
+ />
1770
+ )}
1771
  </div>
1772
  </div>
1773
  );